930 lines
30 KiB
PowerShell
930 lines
30 KiB
PowerShell
#requires -Modules powershell-yaml
|
|
|
|
param(
|
|
[string]$StartDir = (Split-Path -Parent $MyInvocation.MyCommand.Path)
|
|
)
|
|
|
|
function Find-WorkspaceRoot($startDir) {
|
|
$current = $startDir
|
|
while ($true) {
|
|
if (Test-Path -LiteralPath (Join-Path $current 'workspace.yml')) {
|
|
return $current
|
|
}
|
|
$parent = Split-Path -Parent $current
|
|
if ($parent -eq $current) {
|
|
throw "workspace.yml not found from the provided start directory"
|
|
}
|
|
$current = $parent
|
|
}
|
|
}
|
|
|
|
function Parse-Yaml($text) {
|
|
try {
|
|
$parsedYaml = ConvertFrom-Yaml $text
|
|
return $parsedYaml ?? @{}
|
|
} catch {
|
|
throw "Failed to parse YAML content : $_"
|
|
}
|
|
}
|
|
|
|
function Parse-YamlFile($path) {
|
|
return Parse-Yaml (Get-Content -LiteralPath $path -Raw)
|
|
}
|
|
|
|
function Strip-Quotes($value) {
|
|
$trim = $value.Trim()
|
|
if (($trim.StartsWith('"') -and $trim.EndsWith('"')) -or
|
|
($trim.StartsWith("'") -and $trim.EndsWith("'"))) {
|
|
return $trim.Substring(1, $trim.Length - 2)
|
|
}
|
|
return $trim
|
|
}
|
|
|
|
function Parse-Workspace($workspacePath) {
|
|
$lines = Get-Content -LiteralPath $workspacePath
|
|
$collections = @()
|
|
$inCollections = $false
|
|
$current = $null
|
|
|
|
foreach ($line in $lines) {
|
|
$trim = $line.Trim()
|
|
|
|
if (-not $inCollections -and $trim -eq 'collections:') {
|
|
$inCollections = $true
|
|
continue
|
|
}
|
|
|
|
if (-not $inCollections) { continue }
|
|
|
|
if (-not ($line.StartsWith(' ') -or $line.StartsWith("`t"))) { # && trimmed?
|
|
break
|
|
}
|
|
|
|
if ($line -match '^\s*-\s+name:\s*(.+)$') {
|
|
$name = Strip-Quotes $Matches[1]
|
|
$current = [ordered]@{ name = $name }
|
|
$collections += $current
|
|
continue
|
|
}
|
|
|
|
if ($line -match '^\s*path:\s*(.+)$' -and $current) {
|
|
$current.path = Strip-Quotes $Matches[1]
|
|
}
|
|
}
|
|
|
|
return @{ collections = $collections }
|
|
}
|
|
|
|
function Sanitize-VarName($name) {
|
|
$sanitized = ([string]$name).Trim() -replace '[{}]', '' -replace '[^A-Za-z0-9_]', '_' -replace '^([0-9])', '_$1'
|
|
|
|
if ([string]::IsNullOrWhiteSpace($sanitized)) {
|
|
return 'value'
|
|
}
|
|
return $sanitized
|
|
}
|
|
|
|
function Parse-PlaceholderContent($content) {
|
|
$trimmed = ([string]$content).Trim();
|
|
$dotenvMatch = [regex]::Match($trimmed, '^\$dotenv\s+(.+)$', 'IgnoreCase')
|
|
if ($dotenvMatch.Success) {
|
|
return @{
|
|
name = $dotenvMatch.Groups[1].value.Trim()
|
|
isDotenv = $true
|
|
}
|
|
}
|
|
return @{
|
|
name = $trimmed
|
|
isDotenv = $false
|
|
}
|
|
}
|
|
|
|
function Collect-Placeholders($value) {
|
|
# Se non è stringa → restituisci array vuoto
|
|
if ($value -isnot [string]) {
|
|
return @()
|
|
}
|
|
$placeholders = @()
|
|
$regex = [regex]'\{\{([^{}]+)\}\}'
|
|
$foundMatches = $regex.Matches($value)
|
|
foreach ($m in $foundMatches) {
|
|
$inner = $m.Groups[1].value
|
|
$parsed = Parse-PlaceholderContent $inner
|
|
$placeholders += $parsed.name
|
|
}
|
|
return $placeholders
|
|
}
|
|
|
|
function Find-Block ([string[]]$lines, $keyName) {
|
|
for ($i = 0; $i -lt $lines.Count; $i++) {
|
|
$trimmed = $lines[$i].Trim()
|
|
if ($trimmed -ne $keyName -and -not $trimmed.StartsWith("$($keyName):")) {
|
|
continue
|
|
}
|
|
|
|
$lineIndent = ([regex]::Match($lines[$i], '^\s*')).value.Length
|
|
$block = @()
|
|
for ($j = $i + 1; $j -lt $lines.Count; $j++) {
|
|
$currentLine = $lines[$j]
|
|
$currentTrimmed = $currentLine.Trim()
|
|
if ([string]::IsNullOrWhiteSpace($currentTrimmed)) {
|
|
$block += $currentLine
|
|
continue
|
|
}
|
|
$currentIndent = ([regex]::Match($currentLine, '^\s*')).value.Length
|
|
# Caso 1: indentazione <= indentazione della chiave e NON inizia con spazio → fine blocco
|
|
if ($currentIndent -le $lineIndent -and -not $currentLine.StartsWith(' ')) {
|
|
break
|
|
}
|
|
# Caso 2: indentazione <= indentazione della chiave e riga commento → includi
|
|
if ($currentIndent -le $lineIndent -and $currentTrimmed.StartsWith('#')) {
|
|
$block += $currentLine
|
|
continue
|
|
}
|
|
# Caso 3: indentazione <= indentazione della chiave → fine blocco
|
|
if ($currentIndent -le $lineIndent) {
|
|
break
|
|
}
|
|
# Altrimenti la riga fa parte del blocco
|
|
$block += $currentLine
|
|
}
|
|
return $block
|
|
}
|
|
return @()
|
|
}
|
|
|
|
function Parse-RequestInfo ($text) {
|
|
$lines = $text -split '\r?\n'
|
|
$infoLines = Find-Block -Lines $lines -KeyName 'info'
|
|
$joined = ($infoLines -join "`n")
|
|
$nameMatch = [regex]::Match($joined, '^\s*name:\s*(.+)$', 'Multiline')
|
|
if ($nameMatch.Success) {
|
|
return Strip-Quotes $nameMatch.Groups[1].value
|
|
}
|
|
return ''
|
|
}
|
|
|
|
function Parse-HttpBlock ($text) {
|
|
# ConvertFrom-Yaml restituisce $null se il testo è vuoto o non valido
|
|
$parsed = Parse-Yaml $text
|
|
if (-not $parsed) { $parsed = @{} }
|
|
|
|
$http = $parsed.http
|
|
if (-not $http) { $http = @{} }
|
|
|
|
$headers = @()
|
|
$headerList = @()
|
|
if ($http.headers -is [System.Collections.IEnumerable]) {
|
|
$headerList = $http.headers
|
|
}
|
|
foreach ($header in $headerList) {
|
|
if (-not $header -or $header -isnot [psobject] -and $header -isnot [hashtable]) {
|
|
continue
|
|
}
|
|
$name = ([string]($header.name ?? '')).Trim()
|
|
$value = ([string]($header.value ?? '')).Trim()
|
|
$headers += @{
|
|
name = $name
|
|
value = $value
|
|
}
|
|
}
|
|
|
|
$params = @()
|
|
if ($http.params -is [System.Collections.IEnumerable]) {
|
|
$params = $http.params
|
|
}
|
|
|
|
$body = $null
|
|
if ($http.body -and ($http.body -is [psobject] -or $http.body -is [hashtable])) {
|
|
$body = $http.body
|
|
}
|
|
|
|
return @{
|
|
method = $http.method ?? 'GET'
|
|
url = $http.url ?? ''
|
|
params = $params
|
|
headers = $headers
|
|
body = $body
|
|
}
|
|
}
|
|
|
|
function Format-VariableValue ($value, [hashtable]$RenderContext = @{}) {
|
|
if ($null -eq $value) {
|
|
return "''"
|
|
}
|
|
|
|
if ($value -is [string]) {
|
|
if ($value.Trim() -eq '') {
|
|
return "''"
|
|
}
|
|
|
|
$renderedValue = $value
|
|
if ($RenderContext.ContainsKey('renderValue') -and $RenderContext.renderValue) {
|
|
$renderedValue = $RenderContext.renderValue.Invoke($value)
|
|
}
|
|
if ($renderedValue -match '\s') {
|
|
$escaped = $renderedValue -replace '"', '\"'
|
|
return '"' + $escaped + '"'
|
|
}
|
|
return $renderedValue
|
|
}
|
|
|
|
return ($value | ConvertTo-Json -Depth 20 -Compress)
|
|
}
|
|
|
|
function Merge-RequestConfig ($base, $updates) {
|
|
if (-not $updates -or ($updates -isnot [psobject] -and $updates -isnot [hashtable])) {
|
|
return $base
|
|
}
|
|
|
|
# Clona base (shallow clone)
|
|
$merged = @{}
|
|
if ($base -is [psobject] -or $base -is [hashtable]) {
|
|
foreach ($key in $base.Keys) {
|
|
$merged[$key] = $base[$key]
|
|
}
|
|
}
|
|
|
|
if ($updates.ContainsKey('auth')) {
|
|
$auth = $updates.auth
|
|
if ($auth -eq 'inherit') {
|
|
if ($merged.ContainsKey('auth') -and ($merged.auth -is [psobject] -or $merged.auth -is [hashtable])) {
|
|
$merged.auth = $merged.auth
|
|
}
|
|
else {
|
|
$merged.Remove('auth')
|
|
}
|
|
}
|
|
elseif ($auth -is [psobject] -or $auth -is [hashtable]) {
|
|
$merged.auth = $auth
|
|
}
|
|
else {
|
|
$merged.Remove('auth')
|
|
}
|
|
}
|
|
else {
|
|
$merged.Remove('auth')
|
|
}
|
|
|
|
if ($updates.variables -is [System.Collections.IEnumerable]) {
|
|
$variables = @()
|
|
if ($base.variables -is [System.Collections.IEnumerable]) {
|
|
$variables = @($base.variables)
|
|
}
|
|
# Mappa per nome
|
|
$byName = @{}
|
|
foreach ($variable in $variables) {
|
|
if ($variable -and $variable.name) {
|
|
$byName[[string]$variable.name] = $variable
|
|
}
|
|
}
|
|
foreach ($variable in $updates.variables) {
|
|
if ($variable -and $variable.name) {
|
|
$byName[[string]$variable.name] = $variable
|
|
}
|
|
}
|
|
$merged.variables = $byName.Values
|
|
}
|
|
|
|
return $merged
|
|
}
|
|
|
|
function Get-RequestConfigForFile ($yamlFile, $sourceDir) {
|
|
$resolved = @()
|
|
$seenFiles = New-Object System.Collections.Generic.HashSet[string]
|
|
|
|
function Add-File ($FilePath) {
|
|
if (-not $FilePath -or $seenFiles.Contains($FilePath)) {
|
|
return
|
|
}
|
|
$seenFiles.Add($FilePath)
|
|
if (-not (Test-Path $FilePath)) {
|
|
return
|
|
}
|
|
|
|
try {
|
|
$parsed = (Get-Content -Raw $FilePath | ConvertFrom-Yaml)
|
|
if (-not $parsed) { $parsed = @{} }
|
|
$requestConfig = $null
|
|
|
|
# Caso 1: parsed.request esiste ed è un oggetto
|
|
if ($parsed.request -and ($parsed.request -is [psobject] -or $parsed.request -is [hashtable])) {
|
|
$requestConfig = $parsed.request
|
|
}
|
|
# Caso 2: parsed è un oggetto e contiene auth/variables
|
|
elseif ($parsed -is [psobject] -or $parsed -is [hashtable]) {
|
|
$config = @{}
|
|
# auth
|
|
if ($parsed.ContainsKey('auth')) {
|
|
$config.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
|
|
}
|
|
# variables
|
|
if ($parsed.variables -is [System.Collections.IEnumerable]) {
|
|
$config.variables = $parsed.variables
|
|
}
|
|
if ($config.Count -gt 0) {
|
|
$requestConfig = $config
|
|
}
|
|
}
|
|
|
|
if ($null -ne $requestConfig) {
|
|
$resolved += $requestConfig
|
|
}
|
|
elseif ((Resolve-Path $FilePath).Path -eq (Resolve-Path $yamlFile).Path) {
|
|
$resolved += @{}
|
|
}
|
|
}
|
|
catch {
|
|
# Ignora file YAML non validi
|
|
}
|
|
}
|
|
|
|
# Costruisci la catena delle directory
|
|
$dirChain = @()
|
|
$currentDir = Split-Path -Parent $yamlFile
|
|
while ($true) {
|
|
$dirChain = ,$currentDir + $dirChain
|
|
if ($currentDir -eq $sourceDir) {
|
|
break
|
|
}
|
|
$parentDir = Split-Path -Parent $currentDir
|
|
if ($parentDir -eq $currentDir) {
|
|
break
|
|
}
|
|
$currentDir = $parentDir
|
|
}
|
|
|
|
foreach ($dir in $dirChain) {
|
|
Add-File (Join-Path $dir 'opencollection.yml')
|
|
Add-File (Join-Path $dir 'folder.yml')
|
|
}
|
|
|
|
# Aggiungi il file principale
|
|
Add-File $yamlFile
|
|
|
|
$result = @{}
|
|
foreach ($config in $resolved) {
|
|
$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])
|
|
) {
|
|
|
|
$lines = @()
|
|
$variableDefinitions = @()
|
|
$commentedVariableDefinitions = @()
|
|
$parameterVariableDefinitions = @()
|
|
$seenVariables = 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 }
|
|
}
|
|
|
|
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 }
|
|
}
|
|
|
|
function Add-CommentedVariable ($Name, $Value) {
|
|
if (-not $Name) { return }
|
|
$normalized = ([string]$Name).Trim()
|
|
if (-not $normalized) { return }
|
|
$commentedVariableDefinitions += @{ name=$normalized; value=$Value }
|
|
}
|
|
|
|
function Add-ReferencedVariables ($Value, $FallbackValue = 'YOUR_VALUE_HERE') {
|
|
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]
|
|
}
|
|
return $result
|
|
}
|
|
return $Value
|
|
}
|
|
|
|
function Add-ParameterVariables ($Name, $Value) { Add-ParameterVariable $Name $Value }
|
|
|
|
function Add-CommentedVariables ($Name, $Value) { Add-CommentedVariable $Name $Value }
|
|
|
|
function Render-Value ($Value) {
|
|
if ($Value -isnot [string]) { return $Value }
|
|
|
|
return ($Value -replace '\{\{([^{}]+)\}\}', {
|
|
param($match,$inner)
|
|
$placeholder = Parse-PlaceholderContent $inner
|
|
if ($placeholder.isDotenv) { return $match }
|
|
if ($placeholder.name -and $dotenvVariables.Contains($placeholder.name)) {
|
|
return "{{$dotenv $($placeholder.name)}}"
|
|
}
|
|
return $match
|
|
})
|
|
}
|
|
|
|
# -------------------------
|
|
# Variabili da requestConfig
|
|
# -------------------------
|
|
$configVariables = @()
|
|
if ($requestConfig.variables -is [System.Collections.IEnumerable]) {
|
|
$configVariables = $requestConfig.variables
|
|
}
|
|
foreach ($variable in $configVariables) {
|
|
if ($variable -and $variable.name) {
|
|
Add-Variable $variable.name $variable.value
|
|
}
|
|
}
|
|
|
|
# -------------------------
|
|
# URL
|
|
# -------------------------
|
|
$url = $request.url ?? ''
|
|
if ($url) {
|
|
Add-ReferencedVariables $url
|
|
$url = ($url -replace ':([A-Za-z0-9_]+)', '{{$1}}')
|
|
$url = Render-Value $url
|
|
$url = $url.Split('?')[0]
|
|
}
|
|
|
|
# -------------------------
|
|
# Headers
|
|
# -------------------------
|
|
$queryParams = @()
|
|
$headers = @()
|
|
|
|
function Add-Header ($Name, $Value) {
|
|
if (-not $Name) { return }
|
|
Add-ReferencedVariables ($Value ?? '')
|
|
$headers += @{
|
|
name = ([string]$Name).Trim()
|
|
value = Render-Value ($Value ?? '')
|
|
}
|
|
}
|
|
|
|
foreach ($header in ($request.headers ?? @())) {
|
|
if ($header -and $header.name) {
|
|
Add-Header $header.name ($header.value ?? '')
|
|
}
|
|
}
|
|
|
|
foreach ($header in ($requestConfig.headers ?? @())) {
|
|
if ($header -and $header.name) {
|
|
Add-Header $header.name ($header.value ?? '')
|
|
}
|
|
}
|
|
|
|
# -------------------------
|
|
# Params
|
|
# -------------------------
|
|
foreach ($param in ($request.params ?? @())) {
|
|
$name = $param.name ?? ''
|
|
$value = $param.value ?? ''
|
|
$type = ([string]($param.type ?? 'query')).ToLower()
|
|
$disabled = ([string]$param.disabled).ToLower() -eq 'true'
|
|
|
|
if ($disabled) {
|
|
Add-CommentedVariables $name $value
|
|
continue
|
|
}
|
|
|
|
Add-ReferencedVariables $value
|
|
|
|
if ($type -eq 'header') {
|
|
$headers += @{ name=$name; value=(Render-Value $value) }
|
|
}
|
|
else {
|
|
$queryParams += @{ name=$name; value="{{$name}}" }
|
|
Add-ParameterVariables $name $value
|
|
}
|
|
}
|
|
|
|
# -------------------------
|
|
# Auth
|
|
# -------------------------
|
|
if ($requestConfig.auth) {
|
|
switch ($requestConfig.auth.type) {
|
|
'bearer' {
|
|
Add-ReferencedVariables ($requestConfig.auth.token ?? '')
|
|
Add-Header 'Authorization' ("Bearer " + (Render-Value ($requestConfig.auth.token ?? '')))
|
|
}
|
|
'basic' {
|
|
$username = $requestConfig.auth.username ?? ''
|
|
$password = $requestConfig.auth.password ?? ''
|
|
Add-ReferencedVariables $username
|
|
Add-ReferencedVariables $password
|
|
Add-Header 'Authorization' ("Basic " + (Render-Value $username) + ":" + (Render-Value $password))
|
|
}
|
|
default {
|
|
$headers += @{
|
|
name = "UNKNOWN_$($requestConfig.auth.type)"
|
|
value = "Basic $($requestConfig.auth.token)"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# -------------------------
|
|
# Commented variables
|
|
# -------------------------
|
|
if ($commentedVariableDefinitions.Count -gt 0) {
|
|
$lines += "# Other variables for $requestName"
|
|
foreach ($variable in $commentedVariableDefinitions) {
|
|
$lines += "# @$(Sanitize-VarName $variable.name)$VARIABLE_NAME_VALUE_SEPARATOR$(Format-VariableValue $variable.value @{ renderValue = { param($v) Render-Value $v } })"
|
|
}
|
|
}
|
|
|
|
# -------------------------
|
|
# Parameter variables
|
|
# -------------------------
|
|
if ($parameterVariableDefinitions.Count -gt 0) {
|
|
$lines += "# Parameter variables for $requestName"
|
|
foreach ($variable in $parameterVariableDefinitions) {
|
|
$lines += "@$(Sanitize-VarName $variable.name)$VARIABLE_NAME_VALUE_SEPARATOR$(Format-VariableValue $variable.value @{ renderValue = { param($v) Render-Value $v } })"
|
|
}
|
|
}
|
|
|
|
# -------------------------
|
|
# Body
|
|
# -------------------------
|
|
$requestBody = ''
|
|
|
|
if ($request.body -and ($request.body -is [psobject] -or $request.body -is [hashtable])) {
|
|
$bodyType = ([string]($request.body.type ?? '')).ToLower()
|
|
|
|
if ($bodyType -eq 'json') {
|
|
$jsonData = ConvertFrom-Json -Depth 20 -InputObject ($request.body.data)
|
|
|
|
if ($jsonData -is [string]) {
|
|
$requestBody = Render-Value $jsonData
|
|
}
|
|
elseif ($jsonData -is [System.Collections.IEnumerable]) {
|
|
$requestBody = (ConvertTo-Json (RenderJsonValue $jsonData) -Depth 20)
|
|
}
|
|
elseif ($jsonData -is [psobject] -or $jsonData -is [hashtable]) {
|
|
$requestBody = (ConvertTo-Json (RenderJsonValue $jsonData) -Depth 20)
|
|
}
|
|
|
|
Add-Header 'Content-Type' 'application/json'
|
|
}
|
|
elseif ($bodyType -eq 'form-urlencoded') {
|
|
$parts = @()
|
|
foreach ($entry in ($request.body.data ?? @())) {
|
|
if (-not $entry -or -not $entry.name) { continue }
|
|
Add-ReferencedVariables ($entry.value ?? '')
|
|
$parts += "$($entry.name)=$(Render-Value ($entry.value ?? ''))"
|
|
}
|
|
$requestBody = ($parts -join '&')
|
|
Add-Header 'Content-Type' 'application/x-www-form-urlencoded'
|
|
}
|
|
}
|
|
|
|
# -------------------------
|
|
# Variables
|
|
# -------------------------
|
|
if ($variableDefinitions.Count -gt 0) {
|
|
$lines += "# Variables for $requestName"
|
|
foreach ($variable in $variableDefinitions) {
|
|
$lines += "@$(Sanitize-VarName $variable.name)$VARIABLE_NAME_VALUE_SEPARATOR$(Format-VariableValue $variable.value @{ renderValue = { param($v) Render-Value $v } })"
|
|
}
|
|
}
|
|
|
|
# -------------------------
|
|
# Final request line
|
|
# -------------------------
|
|
$method = ([string]($request.method ?? 'GET')).ToUpper()
|
|
$requestUrl = $url
|
|
|
|
foreach ($param in $queryParams) {
|
|
if (-not $param.name) { continue }
|
|
$separator = ($requestUrl.Contains('?') ? '&' : '?')
|
|
$requestUrl = "$requestUrl$separator$($param.name)=$($param.value)"
|
|
}
|
|
|
|
$lines += ''
|
|
$lines += "$method $requestUrl"
|
|
|
|
foreach ($header in $headers) {
|
|
$lines += "$($header.name): $($header.value)"
|
|
}
|
|
|
|
if ($requestBody) {
|
|
$lines += ''
|
|
$lines += $requestBody
|
|
}
|
|
|
|
return ($lines -join "`n")
|
|
}
|
|
|
|
function Ensure-Dir($path) {
|
|
if (-not (Test-Path -LiteralPath $path)) {
|
|
New-Item -ItemType Directory -Path $path | Out-Null
|
|
}
|
|
}
|
|
|
|
function Walk-YamlFiles($rootDir) {
|
|
Get-ChildItem -LiteralPath $rootDir -Recurse -File -Include *.yml, *.yaml |
|
|
Where-Object { $_.Name -notmatch '^\.|node_modules' } |
|
|
Select-Object -ExpandProperty FullName
|
|
}
|
|
|
|
function Clean-Folder($dir) {
|
|
if (-not (Test-Path -LiteralPath $dir)) { return }
|
|
|
|
foreach ($entry in Get-ChildItem -LiteralPath $dir) {
|
|
if ($entry.PSIsContainer) {
|
|
Clean-Folder $entry.FullName
|
|
$children = Get-ChildItem -LiteralPath $entry.FullName
|
|
if ($children.Count -eq 0) {
|
|
Remove-Item -LiteralPath $entry.FullName -Force
|
|
}
|
|
} else {
|
|
if (!$entry.PSIsContainer -and $entry.Name -match '\.js$|\.http$|\.env\.template$') {
|
|
Remove-Item -LiteralPath $entry.FullName -Force
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function Get-DotenvVariablesForTargetDir ($TargetDir, $OutputRoot, $DotenvVariablesByTarget) {
|
|
$variables = New-Object System.Collections.Generic.HashSet[string]
|
|
$currentDir = $TargetDir
|
|
|
|
while ($true) {
|
|
if ($DotenvVariablesByTarget.ContainsKey($currentDir)) {
|
|
foreach ($variable in $DotenvVariablesByTarget[$currentDir]) {
|
|
$variables.Add($variable) | Out-Null
|
|
}
|
|
}
|
|
|
|
$parentDir = Split-Path -Parent $currentDir
|
|
if ($currentDir -eq $OutputRoot -or $parentDir -eq $currentDir) {
|
|
break
|
|
}
|
|
|
|
$currentDir = $parentDir
|
|
}
|
|
|
|
return $variables
|
|
}
|
|
|
|
function Write-EnvironmentTemplates ($sourceDir, $outputRoot) {
|
|
$targets = @()
|
|
$dotenvVariablesByTarget = @{} # Hashtable: targetDir → HashSet
|
|
|
|
function Visit ([string]$CurrentDir) {
|
|
$entries = Get-ChildItem -LiteralPath $CurrentDir -Force
|
|
$hasEnvironmentsDir = $entries | Where-Object {
|
|
$_.PSIsContainer -and $_.Name -eq 'environments'
|
|
}
|
|
|
|
if ($hasEnvironmentsDir) {
|
|
$targets += $CurrentDir
|
|
}
|
|
|
|
foreach ($entry in $entries) {
|
|
if (-not $entry.PSIsContainer) { continue }
|
|
if ($entry.Name.StartsWith('.')) { continue }
|
|
if ($entry.Name -eq 'node_modules') { continue }
|
|
|
|
Visit (Join-Path $CurrentDir $entry.Name)
|
|
}
|
|
}
|
|
|
|
Visit $sourceDir
|
|
|
|
foreach ($dir in $targets) {
|
|
$relativeDir = [System.IO.Path]::GetRelativePath($sourceDir, $dir)
|
|
if ($relativeDir -and $relativeDir -ne '.') {
|
|
$targetDir = Join-Path $outputRoot $relativeDir
|
|
}
|
|
else {
|
|
$targetDir = $outputRoot
|
|
}
|
|
|
|
Ensure-Dir $targetDir
|
|
|
|
$environmentsDir = Join-Path $dir 'environments'
|
|
if (-not (Test-Path $environmentsDir)) {
|
|
continue
|
|
}
|
|
|
|
$envFiles =
|
|
Get-ChildItem -LiteralPath $environmentsDir -Force |
|
|
Where-Object { -not $_.PSIsContainer -and $_.Name -match '\.ya?ml$' } |
|
|
ForEach-Object { $_.FullName }
|
|
|
|
$variableNames = @()
|
|
$seenNames = New-Object System.Collections.Generic.HashSet[string]
|
|
|
|
foreach ($envFile in $envFiles) {
|
|
$parsed = Parse-Yaml $envFile
|
|
if (-not $parsed) { continue }
|
|
|
|
$variables = @()
|
|
if ($parsed.variables -is [System.Collections.IEnumerable]) {
|
|
$variables = $parsed.variables
|
|
}
|
|
|
|
foreach ($variable in $variables) {
|
|
if (-not $variable -or -not $variable.name) { continue }
|
|
|
|
$name = ([string]$variable.name).Trim()
|
|
if (-not $name) { continue }
|
|
if ($seenNames.Contains($name)) { continue }
|
|
|
|
$seenNames.Add($name)
|
|
$variableNames += $name
|
|
}
|
|
}
|
|
|
|
if ($variableNames.Count -gt 0) {
|
|
$templateContent = ($variableNames | ForEach-Object { "$_=$DEFAULT_ENV_VAR_VALUE" }) -join "`n"
|
|
$templateContent += "`n"
|
|
}
|
|
else {
|
|
$templateContent = ""
|
|
}
|
|
|
|
$templatePath = Join-Path $targetDir '.env.template'
|
|
Set-Content -Path $templatePath -Value $templateContent -Encoding UTF8
|
|
|
|
$dotenvVariablesByTarget[$targetDir] = $seenNames
|
|
}
|
|
|
|
return $dotenvVariablesByTarget
|
|
}
|
|
|
|
function Write-JsFiles ($sourceDir, $outputRoot) {
|
|
$targets = @()
|
|
|
|
function Visit ($CurrentDir) {
|
|
$entries = Get-ChildItem -LiteralPath $CurrentDir -Force
|
|
|
|
$hasJsFiles = $entries | Where-Object {
|
|
-not $_.PSIsContainer -and $_.Name -match '\.js$'
|
|
}
|
|
|
|
if ($hasJsFiles) {
|
|
$targets += $CurrentDir
|
|
}
|
|
|
|
# Visita ricorsivamente le sottodirectory
|
|
foreach ($entry in $entries) {
|
|
if (-not $entry.PSIsContainer) { continue }
|
|
if ($entry.Name.StartsWith('.')) { continue }
|
|
if ($entry.Name -eq 'node_modules') { continue }
|
|
|
|
Visit (Join-Path $CurrentDir $entry.Name)
|
|
}
|
|
}
|
|
|
|
Visit $sourceDir
|
|
|
|
foreach ($dir in $targets) {
|
|
$relativeDir = [System.IO.Path]::GetRelativePath($sourceDir, $dir)
|
|
if ($relativeDir -and $relativeDir -ne '.') {
|
|
$targetDir = Join-Path $outputRoot $relativeDir
|
|
}
|
|
else {
|
|
$targetDir = $outputRoot
|
|
}
|
|
|
|
Ensure-Dir $targetDir
|
|
|
|
if (-not (Test-Path $dir)) {
|
|
continue
|
|
}
|
|
|
|
$jsFiles =
|
|
Get-ChildItem -LiteralPath $dir -Force |
|
|
Where-Object { -not $_.PSIsContainer -and $_.Name -match '\.js$' } |
|
|
ForEach-Object {
|
|
@{
|
|
Source = $_.FullName
|
|
Target = Join-Path $targetDir $_.Name
|
|
}
|
|
}
|
|
|
|
foreach ($jsFile in $jsFiles) {
|
|
Copy-Item -LiteralPath $jsFile.Source -Destination $jsFile.Target -Force
|
|
}
|
|
}
|
|
|
|
return $targets
|
|
}
|
|
|
|
function Invoke-Main {
|
|
# Workspace root
|
|
$workspaceRoot = Find-WorkspaceRoot $PSScriptRoot
|
|
$workspaceFile = Join-Path $workspaceRoot 'workspace.yml'
|
|
$workspace = Parse-Workspace $workspaceFile
|
|
$collections = @()
|
|
if ($workspace.collections -is [System.Collections.IEnumerable]) {
|
|
$collections = $workspace.collections
|
|
}
|
|
|
|
if ($collections.Count -eq 0) {
|
|
throw "No collections found in workspace.yml"
|
|
}
|
|
|
|
$outputBaseRoot = Join-Path $workspaceRoot 'autogen/httpyac'
|
|
Clean-Folder $outputBaseRoot
|
|
|
|
foreach ($collection in $collections) {
|
|
if (-not $collection -or -not $collection.name -or -not $collection.path) {
|
|
continue
|
|
}
|
|
|
|
$sourceDir = Join-Path $workspaceRoot $collection.path
|
|
if (-not (Test-Path $sourceDir)) {
|
|
Write-Warning "Skipping missing collection path: $($collection.path)"
|
|
continue
|
|
}
|
|
|
|
$outputRoot = Join-Path $outputBaseRoot $collection.name
|
|
Ensure-Dir $outputRoot
|
|
|
|
# JS files
|
|
$writtenJsFiles = Write-JsFiles $sourceDir $outputRoot
|
|
|
|
# dotenv templates
|
|
$dotenvVariablesByTarget = Write-EnvironmentTemplates $sourceDir $outputRoot
|
|
|
|
# YAML files
|
|
$yamlFiles = Walk-YamlFiles $sourceDir
|
|
$processed = 0
|
|
|
|
foreach ($yamlFile in $yamlFiles) {
|
|
|
|
$content = Get-Content -LiteralPath $yamlFile -Raw
|
|
$requestName = Parse-RequestInfo $content
|
|
if (-not $requestName) {
|
|
$requestName = [System.IO.Path]::GetFileNameWithoutExtension($yamlFile)
|
|
}
|
|
|
|
$httpBlock = Parse-HttpBlock $content
|
|
if (-not $httpBlock -or -not $httpBlock.url) {
|
|
continue
|
|
}
|
|
|
|
$relativePath = [System.IO.Path]::GetRelativePath($sourceDir, $yamlFile)
|
|
$parsedPath = [System.IO.Path]::GetFileNameWithoutExtension($relativePath)
|
|
$parsedDir = Split-Path $relativePath -Parent
|
|
|
|
$targetDir = Join-Path $outputRoot $parsedDir
|
|
Ensure-Dir $targetDir
|
|
|
|
$requestConfig = Get-RequestConfigForFile $yamlFile $sourceDir
|
|
$outputFile = Join-Path $targetDir ("$parsedPath.http")
|
|
|
|
$dotenvVariables = Get-DotenvVariablesForTargetDir $targetDir $outputRoot $dotenvVariablesByTarget
|
|
|
|
$requestContent = Build-RequestContent $httpBlock $requestName $requestConfig $dotenvVariables
|
|
|
|
Set-Content -Path $outputFile -Value ($requestContent + "`n") -Encoding UTF8
|
|
|
|
$processed++
|
|
}
|
|
|
|
$namePadded = $collection.name.PadRight(33)
|
|
$httpCount = $processed.ToString().PadLeft(3)
|
|
$jsCount = $writtenJsFiles.Count.ToString().PadLeft(3)
|
|
|
|
Write-Host "$namePadded => generated $httpCount .http file(s) and $jsCount .js file(s)"
|
|
}
|
|
}
|
|
|
|
Invoke-Main
|