Streamline management of API collections #1

Merged
administrator merged 43 commits from feature/script-autogenerate-http into main 2026-08-19 14:54:42 +02:00
Showing only changes of commit 525d4f4cf0 - Show all commits
+327
View File
@@ -0,0 +1,327 @@
#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"
}
$current = $parent
}
}
function Parse-YamlFile($path) {
try {
return ConvertFrom-Yaml (Get-Content -LiteralPath $path -Raw)
} catch {
throw "Failed to parse YAML file $path : $_"
}
}
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"))) {
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 Walk-YamlFiles($rootDir) {
Get-ChildItem -LiteralPath $rootDir -Recurse -File -Include *.yml, *.yaml |
Where-Object { $_.Name -notmatch '^\.|node_modules' } |
Select-Object -ExpandProperty FullName
}
function Ensure-Dir($path) {
if (-not (Test-Path -LiteralPath $path)) {
New-Item -ItemType Directory -Path $path | Out-Null
}
}
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 Parse-HttpBlock($text) {
$parsed = ConvertFrom-Yaml $text
$http = $parsed.http
$headers = @()
foreach ($h in ($http.headers | Where-Object { $_ })) {
$headers += [ordered]@{
name = $h.name
value = $h.value
}
}
return @{
method = $http.method
url = $http.url
params = $http.params
headers = $headers
body = $http.body
}
}
function Build-RequestContent($http, $name, $config, $dotenvVars) {
$lines = @()
$vars = @()
$paramsVars = @()
$commentVars = @()
$seen = New-Object System.Collections.Generic.HashSet[string]
function Add-Var($n, $v) {
if (-not $n) { return }
if ($seen.Contains($n)) { return }
if ($dotenvVars.Contains($n)) { return }
$seen.Add($n) | Out-Null
$vars += @{ name = $n; value = $v }
}
function Add-ParamVar($n, $v) {
if (-not $n) { return }
if ($seen.Contains($n)) { return }
if ($dotenvVars.Contains($n)) { return }
$seen.Add($n) | Out-Null
$paramsVars += @{ name = $n; value = $v }
}
function Add-CommentVar($n, $v) {
if (-not $n) { return }
$commentVars += @{ name = $n; value = $v }
}
# Variables from config
foreach ($v in ($config.variables | Where-Object { $_ })) {
Add-Var $v.name $v.value
}
# URL
$url = $http.url
if ($url) {
$url = $url -replace ':(\w+)', '{{$1}}'
$url = $url.Split('?')[0]
}
# Params
$queryParams = @()
$headers = @()
foreach ($h in $http.headers) {
$headers += $h
}
foreach ($p in $http.params) {
$name = $p.name
$value = $p.value
$type = ($p.type).ToLower()
$disabled = ($p.disabled -eq $true)
if ($disabled) {
Add-CommentVar $name $value
continue
}
if ($type -eq 'header') {
$headers += @{ name = $name; value = $value }
} else {
$queryParams += @{ name = $name; value = "{{$name}}" }
Add-ParamVar $name $value
}
}
# Auth
if ($config.auth) {
switch ($config.auth.type) {
'bearer' {
$headers += @{ name = 'Authorization'; value = "Bearer $($config.auth.token)" }
}
'basic' {
$headers += @{ name = 'Authorization'; value = "Basic $($config.auth.username):$($config.auth.password)" }
}
}
}
# Commented vars
if ($commentVars.Count -gt 0) {
$lines += "# Other variables for $name"
foreach ($v in $commentVars) {
$lines += "# @$($v.name)=$($v.value)"
}
}
# Parameter vars
if ($paramsVars.Count -gt 0) {
$lines += "# Parameter variables for $name"
foreach ($v in $paramsVars) {
$lines += "@$($v.name)=$($v.value)"
}
}
# Vars
if ($vars.Count -gt 0) {
$lines += "# Variables for $name"
foreach ($v in $vars) {
$lines += "@$($v.name)=$($v.value)"
}
}
# Build URL
foreach ($qp in $queryParams) {
$sep = ($url.Contains('?')) ? '&' : '?'
$url = "$url$sep$($qp.name)=$($qp.value)"
}
$lines += ""
$lines += "$($http.method.ToUpper()) $url"
foreach ($h in $headers) {
$lines += "$($h.name): $($h.value)"
}
if ($http.body) {
$lines += ""
$lines += ($http.body.data | ConvertTo-Json -Depth 10)
}
return ($lines -join "`n")
}
function Main {
$workspaceRoot = Find-WorkspaceRoot $StartDir
$workspace = Parse-Workspace (Join-Path $workspaceRoot 'workspace.yml')
$collections = $workspace.collections
$outputBase = Join-Path $workspaceRoot 'autogen/httpyac_ps1'
Clean-Folder $outputBase
foreach ($col in $collections) {
$sourceDir = Join-Path $workspaceRoot $col.path
if (-not (Test-Path -LiteralPath $sourceDir)) {
Write-Warning "Skipping missing collection path: $($col.path)"
continue
}
$outputRoot = Join-Path $outputBase $col.name
Ensure-Dir $outputRoot
# Copy JS
Get-ChildItem $sourceDir -Recurse -File -Include *.js |
ForEach-Object {
$rel = $_.FullName.Substring($sourceDir.Length).TrimStart('\')
$dest = Join-Path $outputRoot $rel
Ensure-Dir (Split-Path -Parent $dest)
Copy-Item $_.FullName $dest -Force
}
# Env templates
$envDir = Join-Path $sourceDir 'environments'
$dotenvVars = New-Object System.Collections.Generic.HashSet[string]
if (Test-Path -LiteralPath $envDir) {
$vars = @()
foreach ($f in Get-ChildItem $envDir -File -Include *.yml, *.yaml) {
$parsed = Parse-YamlFile $f.FullName
foreach ($v in ($parsed.variables | Where-Object { $_ })) {
if (-not $dotenvVars.Contains($v.name)) {
$dotenvVars.Add($v.name) | Out-Null
$vars += $v.name
}
}
}
$template = ($vars | ForEach-Object { "$_=`"EDIT_VALUE_HERE`"" }) -join "`n"
Set-Content -LiteralPath (Join-Path $outputRoot '.env.template') -Value $template
}
# YAML → .http
$yamlFiles = Walk-YamlFiles $sourceDir
$count = 0
foreach ($file in $yamlFiles) {
$content = Get-Content -LiteralPath $file -Raw
$http = Parse-HttpBlock $content
if (-not $http.url) { continue }
$rel = $file.Substring($sourceDir.Length).TrimStart('\')
$parsed = Split-Path $rel -LeafBase
$dir = Split-Path $rel -Parent
$targetDir = Join-Path $outputRoot $dir
Ensure-Dir $targetDir
$requestName = $parsed
$config = @{ variables = @(); auth = $null }
$dotenv = $dotenvVars
$outFile = Join-Path $targetDir "$parsed.http"
$reqContent = Build-RequestContent $http $requestName $config $dotenv
Set-Content -LiteralPath $outFile -Value $reqContent
$count++
}
Write-Host ("{0,-33} => generated {1,3} .http file(s)" -f $col.name, $count)
}
}
Main