Streamline management of API collections #1
@@ -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
|
||||
Reference in New Issue
Block a user