Skip to content

Commit

Permalink
Merge pull request #1423 from mdaneri/issue--1422
Browse files Browse the repository at this point in the history
Fix OpenAPI Route Path Conversion for Placeholder Unescaping and Relocate Function (#1422)
  • Loading branch information
Badgerati authored Oct 20, 2024
2 parents a181172 + 417d9cb commit 56e34f5
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 14 deletions.
97 changes: 97 additions & 0 deletions examples/OpenApi-SimplePotato.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<#
.SYNOPSIS
Sets up a Pode server with OpenAPI documentation, request logging, and routes for handling 'potato' requests.
.DESCRIPTION
This script configures a Pode server to listen on a specified port, enables both request and error logging,
and sets up OpenAPI documentation. It defines routes for fetching 'potato' data with responses in both
JSON and plain text. OpenAPI documentation is exposed via Swagger and other viewers.
.EXAMPLE
./PodeServer-OpenApi.ps1
Invoke-RestMethod -Uri http://localhost:8080/api/v4.2/potato -Method Get
.LINK
https://github.com/Badgerati/Pode
.NOTES
This is an example Pode server setup that demonstrates OpenAPI integration.
Author: Pode Team
License: MIT License
#>

# Try to import the Pode module from the source if available, otherwise use the installed version
try {
# Determine the script path and Pode module path
$ScriptPath = (Split-Path -Parent -Path $MyInvocation.MyCommand.Path)
$podePath = Split-Path -Parent -Path $ScriptPath

# Import Pode from local source if available, otherwise from installed modules
if (Test-Path -Path "$($podePath)/src/Pode.psm1" -PathType Leaf) {
Import-Module "$($podePath)/src/Pode.psm1" -Force -ErrorAction Stop
}
else {
Import-Module -Name 'Pode' -MaximumVersion 2.99 -ErrorAction Stop
}
}
catch {
throw
}

# Start the Pode server
Start-PodeServer {

# Enable terminal logging for requests and errors
New-PodeLoggingMethod -Terminal | Enable-PodeRequestLogging
New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging

# Define the endpoint for the server
Add-PodeEndpoint -Address 127.0.0.1 -Port 8080 -Protocol Http

# Initialize OpenAPI with basic configuration
Enable-PodeOpenApi -Path '/docs/openapi' -DefinitionTag 'potato' -DisableMinimalDefinitions

# Set OpenAPI info
Add-PodeOAInfo -Title 'Potato sample - OpenAPI 3.0' `
-Version 1.0.17 `
-Description 'This is a simple "potato" API with the OpenAPI 3.0 specification.' -DefinitionTag 'potato'

# Define the OpenAPI server endpoint
Add-PodeOAServerEndpoint -url '/api' -Description 'default endpoint' -DefinitionTag 'potato'

# External documentation link for OpenAPI
$extDoc = New-PodeOAExternalDoc -Description 'Find out more about Swagger' -Url 'http://swagger.io'
$extDoc | Add-PodeOAExternalDoc

# Enable OpenAPI viewers
Enable-PodeOAViewer -Type Swagger -Path '/docs/swagger' -Title 'Swagger' -DefinitionTag 'potato'
Enable-PodeOAViewer -Bookmarks -Path '/docs' -Title 'Bookmark' -DefinitionTag 'potato'
Enable-PodeOAViewer -Editor -Path '/docs/editor' -Title 'Editor' -DefinitionTag 'potato'

# Select OpenAPI definition tag
Select-PodeOADefinition -tag 'potato' -ScriptBlock {

# Define routes within the '/api' group
Add-PodeRouteGroup -Path '/api' -Routes {

# JSON output route
Add-PodeRoute -Method Get -Path '/v4.2/:potato' -ScriptBlock {
Write-PodeJsonResponse -Value @{Potato = $WebEvent.Parameters['potato'] } -StatusCode 400
} -Passthru | Set-PodeOARouteInfo -Summary 'Json output' -Description 'Returns JSON response' -OperationId 'json' -Passthru | `
Set-PodeOARequest -PassThru -Parameters (
New-PodeOAStringProperty -Name 'potato' -Description 'Potato Name' -Required |
ConvertTo-PodeOAParameter -In Path -Required
)

# Plain text output route
Add-PodeRoute -Method Get -Path '/:potato' -ScriptBlock {
Write-PodeTextResponse -Value $WebEvent.Parameters['potato'] -StatusCode 200
} -Passthru | Set-PodeOARouteInfo -Summary 'Text output' -Description 'Returns plain text response' -OperationId 'text' -Passthru | `
Set-PodeOARequest -PassThru -Parameters (
New-PodeOAStringProperty -Name 'potato' -Description 'Potato Name' -Required |
ConvertTo-PodeOAParameter -In Path -Required
)
}
}
}
30 changes: 30 additions & 0 deletions src/Private/OpenApi.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2257,4 +2257,34 @@ function Test-PodeOAComponentInternal {
return $true
}
}
}




<#
.SYNOPSIS
Converts a Pode route path into an OpenAPI-compliant route path format.
.DESCRIPTION
This internal function takes a Pode route path and replaces placeholders with OpenAPI-style placeholders.
Specifically, it converts Pode route placeholders (e.g., `:id`) to OpenAPI placeholders (e.g., `{id}`).
.PARAMETER Path
The Pode route path that contains placeholders to be converted to the OpenAPI format.
.RETURNS
The converted OpenAPI-compliant route path as a string.
.NOTES
This is an internal function and may change in future releases of Pode.
#>
function ConvertTo-PodeOARoutePath {
param(
[Parameter(Mandatory = $true)]
[string]
$Path
)

return ([regex]::Unescape((Resolve-PodePlaceholder -Path $Path -Pattern '\:(?<tag>[\w]+)' -Prepend '{' -Append '}')))
}
12 changes: 1 addition & 11 deletions src/Private/Routes.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -368,17 +368,7 @@ function Get-PodeRouteByUrl {
return $null
}


function ConvertTo-PodeOpenApiRoutePath {
param(
[Parameter(Mandatory = $true)]
[string]
$Path
)

return (Resolve-PodePlaceholder -Path $Path -Pattern '\:(?<tag>[\w]+)' -Prepend '{' -Append '}')
}


<#
.SYNOPSIS
Updates a Pode route path to ensure proper formatting.
Expand Down
2 changes: 1 addition & 1 deletion src/Public/OpenApi.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3313,7 +3313,7 @@ function Add-PodeOAExternalRoute {

# ensure the route has appropriate slashes
$Path = Update-PodeRouteSlash -Path $Path
$OpenApiPath = ConvertTo-PodeOpenApiRoutePath -Path $Path
$OpenApiPath = ConvertTo-PodeOARoutePath -Path $Path
$Path = Resolve-PodePlaceholder -Path $Path
$extRoute = @{
Method = $Method.ToLower()
Expand Down
4 changes: 2 additions & 2 deletions src/Public/Routes.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ function Add-PodeRoute {

# ensure the route has appropriate slashes
$Path = Update-PodeRouteSlash -Path $Path
$OpenApiPath = ConvertTo-PodeOpenApiRoutePath -Path $Path
$OpenApiPath = ConvertTo-PodeOARoutePath -Path $Path
$Path = Resolve-PodePlaceholder -Path $Path

# get endpoints from name
Expand Down Expand Up @@ -742,7 +742,7 @@ function Add-PodeStaticRoute {

# ensure the route has appropriate slashes
$Path = Update-PodeRouteSlash -Path $Path -Static
$OpenApiPath = ConvertTo-PodeOpenApiRoutePath -Path $Path
$OpenApiPath = ConvertTo-PodeOARoutePath -Path $Path
$Path = Resolve-PodePlaceholder -Path $Path

# get endpoints from name
Expand Down
20 changes: 20 additions & 0 deletions tests/unit/PrivateOpenApi.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -419,4 +419,24 @@ Describe 'PrivateOpenApi' {

}

Describe 'ConvertTo-PodeOARoutePath' {

It 'should convert the path "/v4.2/:potato" to OpenAPI format' {
$result = ConvertTo-PodeOARoutePath -Path '/v4.2/:potato'
$result | Should -BeExactly '/v4.2/{potato}'
}

It 'should convert the path "/:potato" to OpenAPI format' {
$result = ConvertTo-PodeOARoutePath -Path '/:potato'
$result | Should -BeExactly '/{potato}'
}

It 'should convert the path "/stores/order/:orderId/invoice" to OpenAPI format' {
$result = ConvertTo-PodeOARoutePath -Path '/stores/order/:orderId/invoice'
$result | Should -BeExactly '/stores/order/{orderId}/invoice'
}


}

}

0 comments on commit 56e34f5

Please sign in to comment.