Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for OpenAPI Component Properties, Server Endpoint, and Path Filtering Issues #1420

Merged
merged 8 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions examples/OpenApi-TuttiFrutti.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ Some useful links:


Add-PodeOAServerEndpoint -url '/api/v3' -Description 'default endpoint' -DefinitionTag 'v3', 'v3.1'

Add-PodeOAInfo -Title 'Swagger Petstore - OpenAPI 3.0' -Version 1.0.17 -Description $InfoDescription -TermsOfService 'http://swagger.io/terms/' -LicenseName 'Apache 2.0' `
-LicenseUrl 'http://www.apache.org/licenses/LICENSE-2.0.html' -ContactName 'API Support' -ContactEmail '[email protected]' -DefinitionTag 'v3'

Expand Down Expand Up @@ -303,7 +302,7 @@ Some useful links:
New-PodeOAStringProperty -Name 'message' | New-PodeOAIntProperty -Name 'code'-Format Int32 | New-PodeOAObjectProperty | Add-PodeOAComponentSchema -Name 'ErrorModel'


Add-PodeRoute -PassThru -Method Get -Path '/peta/:id' -ScriptBlock {
Add-PodeRoute -PassThru -Method Get -Path '/peta/:id' -OADefinitionTag 'v3.1' -ScriptBlock {
Write-PodeJsonResponse -Value (Get-Pet -Id $WebEvent.Parameters['id']) -StatusCode 200
} |
Set-PodeOARouteInfo -Summary 'Find pets by ID' -Description 'Returns pets based on ID' -OperationId 'getPetsById' -PassThru |
Expand Down Expand Up @@ -421,17 +420,17 @@ Some useful links:
New-PodeOAExample -ContentType 'text/plain' -Name 'user' -Summary 'User Example in Plain text' -ExternalValue 'http://foo.bar/examples/user-example.txt' |
New-PodeOAExample -ContentType '*/*' -Name 'user' -Summary 'User example in other forma' -ExternalValue 'http://foo.bar/examples/user-example.whatever'
Select-PodeOADefinition -Tag 'v3' -Scriptblock {
Add-PodeRouteGroup -Path '/api/v4' -Routes {
Add-PodeRouteGroup -Path '/api/v3/private' -Routes {

Add-PodeRoute -PassThru -Method Put -Path '/pat/:petId' -ScriptBlock {
Add-PodeRoute -PassThru -Method Put,Post -Path '/pat/:petId' -ScriptBlock {
$JsonPet = ConvertTo-Json $WebEvent.data
if ( Update-Pet -Id $WebEvent.Parameters['petId'] -Data $JsonPet) {
Write-PodeJsonResponse -Value @{} -StatusCode 200
}
else {
Write-PodeJsonResponse -Value @{} -StatusCode 405
}
} | Set-PodeOARouteInfo -Summary 'Updates a pet in the store with form data' -Tags 'pet' -OperationId 'updatePasdadaetWithForm' -PassThru |
} | Set-PodeOARouteInfo -Summary 'Updates a pet in the store with form data' -Tags 'pet' -PassThru |
Set-PodeOARequest -Parameters @(
(New-PodeOAStringProperty -Name 'petId' -Description 'ID of pet that needs to be updated' | ConvertTo-PodeOAParameter -In Path -Required)
) -RequestBody (
Expand All @@ -441,6 +440,9 @@ Some useful links:
Add-PodeOAResponse -StatusCode 200 -Description 'Pet updated.' -Content (@{ 'application/json' = '' ; 'application/xml' = '' }) -PassThru |
Add-PodeOAResponse -StatusCode 405 -Description 'Method Not Allowed' -Content (@{ 'application/json' = '' ; 'application/xml' = '' })




Add-PodeRoute -PassThru -Method Put -Path '/paet/:petId' -ScriptBlock {
$JsonPet = ConvertTo-Json $WebEvent.data
if ( Update-Pet -Id $WebEvent.Parameters['id'] -Data $JsonPet) {
Expand Down
1 change: 0 additions & 1 deletion examples/PetStore/Petstore-OpenApiMultiTag.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ Some useful links:

Add-PodeOAServerEndpoint -url '/api/v3' -Description 'V3 Endpoint' -DefinitionTag 'v3.0.3'
Add-PodeOAServerEndpoint -url '/api/v3' -Description 'V3.1 Endpoint' -DefinitionTag 'v3.1'
Add-PodeOAServerEndpoint -url '/api' -Description 'Default Endpoint' -DefinitionTag 'v3.0.3','v3.1'

#OpenAPI 3.0
Enable-PodeOAViewer -Type Swagger -Path '/docs/swagger' -DefinitionTag 'v3.0.3'
Expand Down
1 change: 1 addition & 0 deletions src/Locales/ar/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,5 @@
getRequestBodyNotAllowedExceptionMessage = 'لا يمكن أن تحتوي عمليات {0} على محتوى الطلب.'
fnDoesNotAcceptArrayAsPipelineInputExceptionMessage = "الدالة '{0}' لا تقبل مصفوفة كمدخل لأنبوب البيانات."
unsupportedStreamCompressionEncodingExceptionMessage = 'تشفير الضغط غير مدعوم للتشفير {0}'
LocalEndpointConflictExceptionMessage = "تم تعريف كل من '{0}' و '{1}' كنقاط نهاية محلية لـ OpenAPI، لكن يُسمح فقط بنقطة نهاية محلية واحدة لكل تعريف API."
}
1 change: 1 addition & 0 deletions src/Locales/de/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,5 @@
getRequestBodyNotAllowedExceptionMessage = '{0}-Operationen können keinen Anforderungstext haben.'
fnDoesNotAcceptArrayAsPipelineInputExceptionMessage = "Die Funktion '{0}' akzeptiert kein Array als Pipeline-Eingabe."
unsupportedStreamCompressionEncodingExceptionMessage = 'Die Stream-Komprimierungskodierung wird nicht unterstützt: {0}'
LocalEndpointConflictExceptionMessage = "Sowohl '{0}' als auch '{1}' sind als lokale OpenAPI-Endpunkte definiert, aber es ist nur ein lokaler Endpunkt pro API-Definition erlaubt."
}
2 changes: 1 addition & 1 deletion src/Locales/en-us/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,4 @@
getRequestBodyNotAllowedExceptionMessage = '{0} operations cannot have a Request Body.'
fnDoesNotAcceptArrayAsPipelineInputExceptionMessage = "The function '{0}' does not accept an array as pipeline input."
unsupportedStreamCompressionEncodingExceptionMessage = 'Unsupported stream compression encoding: {0}'
}
LocalEndpointConflictExceptionMessage = "Both '{0}' and '{1}' are defined as local OpenAPI endpoints, but only one local endpoint is allowed per API definition."}
1 change: 1 addition & 0 deletions src/Locales/en/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,5 @@
getRequestBodyNotAllowedExceptionMessage = '{0} operations cannot have a Request Body.'
fnDoesNotAcceptArrayAsPipelineInputExceptionMessage = "The function '{0}' does not accept an array as pipeline input."
unsupportedStreamCompressionEncodingExceptionMessage = 'Unsupported stream compression encoding: {0}'
LocalEndpointConflictExceptionMessage = "Both '{0}' and '{1}' are defined as local OpenAPI endpoints, but only one local endpoint is allowed per API definition."
}
1 change: 1 addition & 0 deletions src/Locales/es/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,5 @@
getRequestBodyNotAllowedExceptionMessage = 'Las operaciones {0} no pueden tener un cuerpo de solicitud.'
fnDoesNotAcceptArrayAsPipelineInputExceptionMessage = "La función '{0}' no acepta una matriz como entrada de canalización."
unsupportedStreamCompressionEncodingExceptionMessage = 'La codificación de compresión de transmisión no es compatible: {0}'
LocalEndpointConflictExceptionMessage = "Tanto '{0}' como '{1}' están definidos como puntos finales locales de OpenAPI, pero solo se permite un punto final local por definición de API."
}
4 changes: 2 additions & 2 deletions src/Locales/fr/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -291,5 +291,5 @@
getRequestBodyNotAllowedExceptionMessage = 'Les opérations {0} ne peuvent pas avoir de corps de requête.'
fnDoesNotAcceptArrayAsPipelineInputExceptionMessage = "La fonction '{0}' n'accepte pas un tableau en tant qu'entrée de pipeline."
unsupportedStreamCompressionEncodingExceptionMessage = "La compression de flux {0} n'est pas prise en charge."
}

LocalEndpointConflictExceptionMessage = "Les deux '{0}' et '{1}' sont définis comme des points de terminaison locaux pour OpenAPI, mais un seul point de terminaison local est autorisé par définition d'API."
}
1 change: 1 addition & 0 deletions src/Locales/it/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,5 @@
getRequestBodyNotAllowedExceptionMessage = 'Le operazioni {0} non possono avere un corpo della richiesta.'
fnDoesNotAcceptArrayAsPipelineInputExceptionMessage = "La funzione '{0}' non accetta una matrice come input della pipeline."
unsupportedStreamCompressionEncodingExceptionMessage = 'La compressione dello stream non è supportata per la codifica {0}'
LocalEndpointConflictExceptionMessage = "Sia '{0}' che '{1}' sono definiti come endpoint locali OpenAPI, ma è consentito solo un endpoint locale per definizione API."
}
1 change: 1 addition & 0 deletions src/Locales/ja/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,5 @@
getRequestBodyNotAllowedExceptionMessage = '{0}操作にはリクエストボディを含めることはできません。'
fnDoesNotAcceptArrayAsPipelineInputExceptionMessage = "関数 '{0}' は配列をパイプライン入力として受け付けません。"
unsupportedStreamCompressionEncodingExceptionMessage = 'サポートされていないストリーム圧縮エンコーディングが提供されました: {0}'
LocalEndpointConflictExceptionMessage = "'{0}' と '{1}' は OpenAPI のローカルエンドポイントとして定義されていますが、API 定義ごとに 1 つのローカルエンドポイントのみ許可されます。"
}
1 change: 1 addition & 0 deletions src/Locales/ko/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,5 @@
getRequestBodyNotAllowedExceptionMessage = '{0} 작업에는 요청 본문이 있을 수 없습니다.'
fnDoesNotAcceptArrayAsPipelineInputExceptionMessage = "함수 '{0}'은(는) 배열을 파이프라인 입력으로 받지 않습니다."
unsupportedStreamCompressionEncodingExceptionMessage = '지원되지 않는 스트림 압축 인코딩: {0}'
LocalEndpointConflictExceptionMessage = "'{0}' 와 '{1}' 는 OpenAPI 로컬 엔드포인트로 정의되었지만, API 정의당 하나의 로컬 엔드포인트만 허용됩니다."
}
1 change: 1 addition & 0 deletions src/Locales/nl/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,5 @@
getRequestBodyNotAllowedExceptionMessage = '{0}-operaties kunnen geen Request Body hebben.'
fnDoesNotAcceptArrayAsPipelineInputExceptionMessage = "De functie '{0}' accepteert geen array als pipeline-invoer."
unsupportedStreamCompressionEncodingExceptionMessage = 'Niet-ondersteunde streamcompressie-encodering: {0}'
LocalEndpointConflictExceptionMessage = "Zowel '{0}' als '{1}' zijn gedefinieerd als lokale OpenAPI-eindpunten, maar er is slechts één lokaal eindpunt per API-definitie toegestaan."
}
1 change: 1 addition & 0 deletions src/Locales/pl/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,5 @@
getRequestBodyNotAllowedExceptionMessage = 'Operacje {0} nie mogą mieć treści żądania.'
fnDoesNotAcceptArrayAsPipelineInputExceptionMessage = "Funkcja '{0}' nie akceptuje tablicy jako wejścia potoku."
unsupportedStreamCompressionEncodingExceptionMessage = 'Kodowanie kompresji strumienia nie jest obsługiwane: {0}'
LocalEndpointConflictExceptionMessage = "Zarówno '{0}', jak i '{1}' są zdefiniowane jako lokalne punkty końcowe OpenAPI, ale na jedną definicję API dozwolony jest tylko jeden lokalny punkt końcowy."
}
1 change: 1 addition & 0 deletions src/Locales/pt/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,5 @@
getRequestBodyNotAllowedExceptionMessage = 'As operações {0} não podem ter um corpo de solicitação.'
fnDoesNotAcceptArrayAsPipelineInputExceptionMessage = "A função '{0}' não aceita uma matriz como entrada de pipeline."
unsupportedStreamCompressionEncodingExceptionMessage = 'A codificação de compressão de fluxo não é suportada.'
LocalEndpointConflictExceptionMessage = "Tanto '{0}' quanto '{1}' estão definidos como endpoints locais do OpenAPI, mas apenas um endpoint local é permitido por definição de API."
}
1 change: 1 addition & 0 deletions src/Locales/zh/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,5 @@
getRequestBodyNotAllowedExceptionMessage = '{0} 操作不能包含请求体。'
fnDoesNotAcceptArrayAsPipelineInputExceptionMessage = "函数 '{0}' 不接受数组作为管道输入。"
unsupportedStreamCompressionEncodingExceptionMessage = '不支持的流压缩编码: {0}'
LocalEndpointConflictExceptionMessage = "'{0}' 和 '{1}' 都被定义为 OpenAPI 的本地端点,但每个 API 定义仅允许一个本地端点。"
}
7 changes: 6 additions & 1 deletion src/Private/OpenApi.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -1149,7 +1149,12 @@ function Get-PodeOpenApiDefinitionInternal {

#remove the ServerUrl part
if ( $localEndpoint) {
$_route.OpenApi.Path = $_route.OpenApi.Path.replace($localEndpoint, '')
if ($_route.Path.StartsWith($localEndpoint)) {
$_route.OpenApi.Path = $_route.OpenApi.Path.replace($localEndpoint, '')
}
else {
continue
}
}
# do nothing if it has no responses set
if ($_route.OpenApi.Responses.Count -eq 0) {
Expand Down
32 changes: 6 additions & 26 deletions src/Public/OAProperties.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -1942,12 +1942,6 @@ If supplied, the schema will be included in a response but not in a request
.PARAMETER WriteOnly
If supplied, the schema will be included in a request but not in a response

.PARAMETER MinProperties
If supplied, will restrict the minimun number of properties allowed in an schema.

.PARAMETER MaxProperties
If supplied, will restrict the maximum number of properties allowed in an schema.

.PARAMETER Array
If supplied, the schema will be treated as an array of objects.

Expand Down Expand Up @@ -2015,45 +2009,31 @@ function New-PodeOAComponentSchemaProperty {
[switch]
$XmlAttribute,

[Parameter( ParameterSetName = 'Array')]
[string]
$XmlItemName,

[Parameter( ParameterSetName = 'Array')]
[switch]
$XmlWrapped,

[Parameter(ParameterSetName = 'Array')]
[object]
$Example,

[Parameter(ParameterSetName = 'Array')]
[switch]
$Deprecated,

[Parameter(ParameterSetName = 'Array')]
[switch]
$Required,

[Parameter(ParameterSetName = 'Array')]
[switch]
$Nullable,

[Parameter(ParameterSetName = 'Array')]
[switch]
$ReadOnly,

[Parameter(ParameterSetName = 'Array')]
[switch]
$WriteOnly,

[Parameter(ParameterSetName = 'Array')]
[int]
$MinProperties,
[Parameter( ParameterSetName = 'Array')]
[string]
$XmlItemName,

[Parameter(ParameterSetName = 'Array')]
[int]
$MaxProperties,
Badgerati marked this conversation as resolved.
Show resolved Hide resolved
[Parameter( ParameterSetName = 'Array')]
[switch]
$XmlWrapped,

[Parameter(Mandatory = $true, ParameterSetName = 'Array')]
[switch]
Expand Down
48 changes: 40 additions & 8 deletions src/Public/OpenApi.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -395,21 +395,45 @@ function Add-PodeOAServerEndpoint {
$DefinitionTag
)


# If the DefinitionTag is empty, use the selected tag from Pode's OpenAPI context
if (Test-PodeIsEmpty -Value $DefinitionTag) {
$DefinitionTag = @($PodeContext.Server.OpenAPI.SelectedDefinitionTag)
}

# Loop through each tag to add the server object to the corresponding OpenAPI definition
foreach ($tag in $DefinitionTag) {
# If the 'servers' array for the tag doesn't exist, initialize it as an empty array
if (! $PodeContext.Server.OpenAPI.Definitions[$tag].servers) {
$PodeContext.Server.OpenAPI.Definitions[$tag].servers = @()
}

# Create an ordered hashtable representing the server object with the URL
$lUrl = [ordered]@{url = $Url }

# If a description is provided, add it to the server object
if ($Description) {
$lUrl.description = $Description
}

# If variables are provided, add them to the server object
if ($Variables) {
$lUrl.variables = $Variables
}

# Check if the URL is a local endpoint (not starting with 'http(s)://')
if ($lUrl.url -notmatch '^(?i)https?://') {
# Loop through existing server URLs in the definition
foreach ($srv in $PodeContext.Server.OpenAPI.Definitions[$tag].servers) {
# If there's already a local endpoint, throw an exception, as only one local endpoint is allowed per definition
# Both are defined as local OpenAPI endpoints, but only one local endpoint is allowed per API definition.
if ($srv.url -notmatch '^(?i)https?://') {
throw ($PodeLocale.LocalEndpointConflictExceptionMessage -f $Url, $srv.url)
}
}
}

# Add the new server object to the OpenAPI definition for the current tag
$PodeContext.Server.OpenAPI.Definitions[$tag].servers += $lUrl
}
}
Expand Down Expand Up @@ -1632,16 +1656,24 @@ function Set-PodeOARouteInfo {
$Route = $pipelineValue
}

$DefinitionTag = Test-PodeOADefinitionTag -Tag $DefinitionTag
$defaultTag = Test-PodeOADefinitionTag -Tag $DefinitionTag

foreach ($r in @($Route)) {
if ((Compare-Object -ReferenceObject $r.OpenApi.DefinitionTag -DifferenceObject $DefinitionTag).Count -ne 0) {
if ($r.OpenApi.IsDefTagConfigured ) {
# Definition Tag for a Route cannot be changed.
throw ($PodeLocale.definitionTagChangeNotAllowedExceptionMessage)
if ($DefinitionTag) {
if ((Compare-Object -ReferenceObject $r.OpenApi.DefinitionTag -DifferenceObject $DefinitionTag).Count -ne 0) {
if ($r.OpenApi.IsDefTagConfigured ) {
# Definition Tag for a Route cannot be changed.
throw ($PodeLocale.definitionTagChangeNotAllowedExceptionMessage)
}
else {
$r.OpenApi.DefinitionTag = $defaultTag
$r.OpenApi.IsDefTagConfigured = $true
}
}
else {
$r.OpenApi.DefinitionTag = $DefinitionTag
}
else {
if (! $r.OpenApi.IsDefTagConfigured ) {
$r.OpenApi.DefinitionTag = $defaultTag
$r.OpenApi.IsDefTagConfigured = $true
}
}
Expand All @@ -1651,7 +1683,7 @@ function Set-PodeOARouteInfo {
# OperationID:$OperationId has to be unique and cannot be applied to an array
throw ($PodeLocale.operationIdMustBeUniqueForArrayExceptionMessage -f $OperationId)
}
foreach ($tag in $DefinitionTag) {
foreach ($tag in $defaultTag) {
if ($PodeContext.Server.OpenAPI.Definitions[$tag].hiddenComponents.operationId -ccontains $OperationId) {
# OperationID:$OperationId has to be unique
throw ($PodeLocale.operationIdMustBeUniqueExceptionMessage -f $OperationId)
Expand Down
Loading