diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..4a7869c7e --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,19 @@ +{ + "name": "Codespace with PowerShell, Pester, Invoke-Build, and .NET 8", + "image": "mcr.microsoft.com/vscode/devcontainers/base:ubuntu", + "features": { + "ghcr.io/devcontainers/features/powershell:1": {}, + "ghcr.io/devcontainers/features/dotnet:1": { + "version": "8.0" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.powershell", + "pspester.pester-test" + ] + } + }, + "postCreateCommand": "pwsh -Command 'Install-Module -Name InvokeBuild,Pester -Force -SkipPublisherCheck; sleep 5; Invoke-Build Build '" +} diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 9122555d5..b326b5548 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -30,6 +30,7 @@ The following is a set of guidelines for contributing to Pode on GitHub. These a - [Where-Object](#where-object) - [Select-Object](#select-object) - [Measure-Object](#measure-object) + - [New-Object](#new-object) ## Code of Conduct @@ -245,3 +246,15 @@ Instead of using the `Measure-Object` commandlet, please use either the `.Length (@(1, 2, 3)).Length (@{ Name = 'Rick' }).Count ``` + +#### New-Object + +Instead of using the `New-Object` commandlet, please use `::new()` as this is far faster than the former. + +```powershell +# instead of +$stream = New-Object System.IO.MemoryStream + +# do this +$stream = [System.IO.MemoryStream]::new() +``` diff --git a/.github/workflows/PSScriptAnalyzer.yml b/.github/workflows/PSScriptAnalyzer.yml index 8673d21f4..d1af32421 100644 --- a/.github/workflows/PSScriptAnalyzer.yml +++ b/.github/workflows/PSScriptAnalyzer.yml @@ -3,7 +3,7 @@ # separate terms of service, privacy policy, and support # documentation. # -# https://github.com/microsoft/action-psscriptanalyzer +# https://github.com/microsoft/psscriptanalyzer-action # For more information on PSScriptAnalyzer in general, see # https://github.com/PowerShell/PSScriptAnalyzer @@ -48,15 +48,11 @@ jobs: - name: Run PSScriptAnalyzer uses: microsoft/psscriptanalyzer-action@6b2948b1944407914a58661c49941824d149734f with: - # Check https://github.com/microsoft/action-psscriptanalyzer for more info about the options. - # The below set up runs PSScriptAnalyzer to your entire repository and runs some basic security rules. - path: .\ + path: .\src recurse: true - # Include your own basic security rules. Removing this option will run all the rules - includeRule: '"PSAvoidUsingCmdletAliases" ,"PSAvoidUsingPlainTextForPassword","PSAvoidUsingWriteHost","PSAvoidUsingInvokeExpression","PSUseShouldProcessForStateChangingFunctions","PSAvoidUsingUsernameAndPasswordParams","PSUseProcessBlockForPipelineCommand","PSAvoidUsingConvertToSecureStringWithPlainText","PSUseSingularNouns","PSReviewUnusedParameter"' + settings: .\PSScriptAnalyzerSettings.psd1 output: results.sarif - # Upload the SARIF file generated in the previous step - name: Upload SARIF results file uses: github/codeql-action/upload-sarif@v3 with: diff --git a/.github/workflows/ci-no-build-needed.yml b/.github/workflows/ci-no-build-needed.yml index e27e5bafe..3345471f0 100644 --- a/.github/workflows/ci-no-build-needed.yml +++ b/.github/workflows/ci-no-build-needed.yml @@ -12,7 +12,8 @@ on: - 'src/**' - 'tests/**' - '.github/workflows/ci-docs.yml' - - '.github/workflows/ci-pwsh*.yml' + - '.github/workflows/ci-pwsh_lts.yml' + - '.github/workflows/ci-pwsh7_2.yml' - '.github/workflows/ci-powershell.yml' - '.github/workflows/ci-coverage.yml' - '.github/workflows/PSScriptAnalyzer.yml' @@ -30,7 +31,8 @@ on: - 'src/**' - 'tests/**' - '.github/workflows/ci-docs.yml' - - '.github/workflows/ci-pwsh*.yml' + - '.github/workflows/ci-pwsh_lts.yml' + - '.github/workflows/ci-pwsh7_2.yml' - '.github/workflows/ci-powershell.yml' - '.github/workflows/ci-coverage.yml' - '.github/workflows/PSScriptAnalyzer.yml' diff --git a/.github/workflows/ci-pwsh_preview.yml b/.github/workflows/ci-pwsh_preview.yml index 56c9dde71..81bb80c3d 100644 --- a/.github/workflows/ci-pwsh_preview.yml +++ b/.github/workflows/ci-pwsh_preview.yml @@ -28,7 +28,7 @@ env: POWERSHELL_VERSION: 'Preview' jobs: - build: + build-preview: runs-on: ${{ matrix.os }} strategy: diff --git a/.github/workflows/label-issue-project.yml b/.github/workflows/label-issue-project.yml index 3abc4d15e..17b146d85 100644 --- a/.github/workflows/label-issue-project.yml +++ b/.github/workflows/label-issue-project.yml @@ -10,7 +10,7 @@ jobs: name: Add issue to project runs-on: ubuntu-latest steps: - - uses: actions/add-to-project@v1.0.1 + - uses: actions/add-to-project@v1.0.2 with: project-url: https://github.com/users/Badgerati/projects/2 github-token: ${{ secrets.PROJECT_TOKEN }} diff --git a/.github/workflows/open-issue-project.yml b/.github/workflows/open-issue-project.yml index 5928986fc..6a8d134bb 100644 --- a/.github/workflows/open-issue-project.yml +++ b/.github/workflows/open-issue-project.yml @@ -10,7 +10,7 @@ jobs: name: Add issue to project runs-on: ubuntu-latest steps: - - uses: actions/add-to-project@v1.0.1 + - uses: actions/add-to-project@v1.0.2 with: project-url: https://github.com/users/Badgerati/projects/2 github-token: ${{ secrets.PROJECT_TOKEN }} diff --git a/.gitignore b/.gitignore index aa5c0e490..079cb2f3a 100644 --- a/.gitignore +++ b/.gitignore @@ -265,3 +265,4 @@ examples/PetStore/data/PetData.json packers/choco/pode.nuspec packers/choco/tools/ChocolateyInstall.ps1 +docs/Getting-Started/Samples.md diff --git a/.vscode/settings.json b/.vscode/settings.json index 559d3ecf2..6f817660d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,6 +21,7 @@ "powershell.codeFormatting.whitespaceBetweenParameters": false, "powershell.codeFormatting.whitespaceInsideBrace": true, "powershell.scriptAnalysis.settingsPath": "PSScriptAnalyzerSettings.psd1", + "powershell.scriptAnalysis.enable": true, "files.trimTrailingWhitespace": true, "files.associations": { "*.pode": "html" diff --git a/PSScriptAnalyzerSettings.psd1 b/PSScriptAnalyzerSettings.psd1 index 2141ac49d..15486f3d7 100644 --- a/PSScriptAnalyzerSettings.psd1 +++ b/PSScriptAnalyzerSettings.psd1 @@ -1,15 +1,33 @@ -# PSScriptAnalyzerSettings.psd1 @{ - Severity = @('Error', 'Warning', 'Information') + Severity = @('Error', 'Warning', 'Information') + IncludeDefaultRules = $true - Rules = @{ + CustomRulePath = @( + './analyzers/AvoidNewObjectRule.psm1' + ) + + Rules = @{ PSReviewUnusedParameter = @{ CommandsToTraverse = @( - 'Where-Object','Remove-PodeRoute' + 'Where-Object', + 'Remove-PodeRoute' ) } + AvoidNewObjectRule = @{ + Severity = 'Warning' + } } - ExcludeRules = @('PSAvoidUsingCmdletAliases' ,'PSAvoidUsingPlainTextForPassword','PSAvoidUsingWriteHost','PSAvoidUsingInvokeExpression','PSUseShouldProcessForStateChangingFunctions', - 'PSAvoidUsingUsernameAndPasswordParams','PSUseProcessBlockForPipelineCommand','PSAvoidUsingConvertToSecureStringWithPlainText','PSUseSingularNouns','PSReviewUnusedParameter' ) + + ExcludeRules = @( + 'PSAvoidUsingPlainTextForPassword', + 'PSUseShouldProcessForStateChangingFunctions', + 'PSAvoidUsingUsernameAndPasswordParams', + 'PSUseProcessBlockForPipelineCommand', + 'PSAvoidUsingConvertToSecureStringWithPlainText', + 'PSReviewUnusedParameter', + 'PSAvoidAssignmentToAutomaticVariable', + 'PSUseBOMForUnicodeEncodedFile', + 'PSAvoidTrailingWhitespace' + ) } \ No newline at end of file diff --git a/README.md b/README.md index 4d14d2289..3ffdf9f6f 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,16 @@ Pode is a Cross-Platform framework for creating web servers to host [REST APIs](https://badgerati.github.io/Pode/Tutorials/Routes/Overview/), [Web Pages](https://badgerati.github.io/Pode/Tutorials/Routes/Examples/WebPages/), and [SMTP/TCP](https://badgerati.github.io/Pode/Servers/) Servers. Pode also allows you to render dynamic files using [`.pode`](https://badgerati.github.io/Pode/Tutorials/Views/Pode/) files, which are just embedded PowerShell, or other [Third-Party](https://badgerati.github.io/Pode/Tutorials/Views/ThirdParty/) template engines. Plus many more features, including [Azure Functions](https://badgerati.github.io/Pode/Hosting/AzureFunctions/) and [AWS Lambda](https://badgerati.github.io/Pode/Hosting/AwsLambda/) support! -
- -
+```powershell + +Start-PodeServer -ScriptBlock { + Add-PodeEndPoint -Address localhost -port 32005 -Protocol Http + Add-PodeRoute -Method Get -Path '/ping' -ScriptBlock { + Write-PodeJsonResponse -Value @{value = 'pong' } + } +} + +``` See [here](https://badgerati.github.io/Pode/Getting-Started/FirstApp) for building your first app! Don't know HTML, CSS, or JavaScript? No problem! [Pode.Web](https://github.com/Badgerati/Pode.Web) is currently a work in progress, and lets you build web pages using purely PowerShell! @@ -49,8 +56,9 @@ Then navigate to `http://127.0.0.1:8000` in your browser. * Cross-platform using PowerShell Core (with support for PS5) * Docker support, including images for ARM/Raspberry Pi * Azure Functions, AWS Lambda, and IIS support -* OpenAPI, Swagger, and ReDoc support -* Listen on a single or multiple IP address/hostnames +* OpenAPI specification version 3.0.x and 3.1.0 +* OpenAPI documentation with Swagger, Redoc, RapidDoc, StopLight, OpenAPI-Explorer and RapiPdf +* Listen on a single or multiple IP(v4/v6) address/hostnames * Cross-platform support for HTTP(S), WS(S), SSE, SMTP(S), and TCP(S) * Host REST APIs, Web Pages, and Static Content (with caching) * Support for custom error pages @@ -73,6 +81,8 @@ Then navigate to `http://127.0.0.1:8000` in your browser. * Support for File Watchers * In-memory caching, with optional support for external providers (such as Redis) * (Windows) Open the hosted server as a desktop application +* FileBrowsing support +* Localization (i18n) in Arabic, German, Spanish, France, Italian, Japanese, Korean, Polish, Portuguese, and Chinese ## 📦 Install diff --git a/analyzers/AvoidNewObjectRule.psm1 b/analyzers/AvoidNewObjectRule.psm1 new file mode 100644 index 000000000..c6b47f231 --- /dev/null +++ b/analyzers/AvoidNewObjectRule.psm1 @@ -0,0 +1,38 @@ +function Measure-AvoidNewObjectRule { + [CmdletBinding()] + [OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]])] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.Management.Automation.Language.ScriptBlockAst] + $ScriptBlockAst + ) + + # Initialize an empty array to collect diagnostic records + $diagnostics = @() + + try { + # Traverse the AST to find all instances of New-Object cmdlet + $ScriptBlockAst.FindAll({ + param($Ast) + $Ast -is [System.Management.Automation.Language.CommandAst] -and + $Ast.CommandElements[0].Extent.Text -eq 'New-Object' + }, $true) | ForEach-Object { + $diagnostics += [PSCustomObject]@{ + Message = "Avoid using 'New-Object' and use '::new()' instead." + Extent = $_.Extent + RuleName = 'AvoidNewObjectRule' + Severity = 'Warning' + ScriptName = $FileName + } + } + + # Return the diagnostic records + return $diagnostics + } + catch { + $PSCmdlet.ThrowTerminatingError($PSItem) + } +} + +Export-ModuleMember -Function Measure-AvoidNewObjectRule \ No newline at end of file diff --git a/docs/Getting-Started/Debug.md b/docs/Getting-Started/Debug.md index a4647d3c5..0baef9a75 100644 --- a/docs/Getting-Started/Debug.md +++ b/docs/Getting-Started/Debug.md @@ -174,3 +174,75 @@ The steps to attach to the Pode process are as follows: 5. You'll also be able to query variables as well, such as `$WebEvent` and other variables you might have created. 6. When you are done debugging the current request, hit the `d` key. + + + +## Managing Runspace Names + +### Internal Runspace Naming + +In Pode, internal runspaces are automatically assigned distinct names. This built-in naming convention is crucial for identifying and managing runspaces efficiently, particularly during debugging or when monitoring multiple concurrent processes. + +Pode uses specific naming patterns for its internal runspaces, which include: + +- **Pode_Web_Listener_1** +- **Pode_Signals_Broadcaster_1** +- **Pode_Signals_Listener_1** +- **Pode_Web_KeepAlive_1** +- **Pode_Files_Watcher_1** +- **Pode_Main_Logging_1** +- **Pode_Timers_Scheduler_1** +- **Pode_Schedules_[Schedule Name]_1** – where `[Schedule Name]` is the name of the schedule. +- **Pode_Tasks_[Task Name]_1** – where `[Task Name]` is the name of the task. + +These default names are automatically assigned by Pode, making it easier to identify the purpose of each runspace during execution. + +### Customizing Runspace Names + +By default, Pode’s Tasks, Schedules, and Timers label their associated runspaces with their own names (as shown above). This simplifies the identification of runspaces when debugging or reviewing logs. + +However, if a different runspace name is needed, Pode allows you to customize it. Inside the script block of `Add-PodeTask`, `Add-PodeSchedule`, or `Add-PodeTimer`, you can use the `Set-PodeCurrentRunspaceName` cmdlet to assign any custom name you prefer. + +```powershell +Set-PodeCurrentRunspaceName -Name 'Custom Runspace Name' +``` + +This cmdlet sets a custom name for the runspace, making it easier to track during execution. + +#### Example + +Here’s an example that demonstrates how to set a custom runspace name in a Pode task: + +```powershell +Add-PodeTask -Name 'Test2' -ScriptBlock { + param($value) + # Set a custom name for the current runspace + Set-PodeCurrentRunspaceName -Name 'My Fancy Runspace' + Start-Sleep -Seconds 10 + "A $($value) is never late, it arrives exactly when it means to" | Out-Default +} +``` + +In this example, the `Set-PodeCurrentRunspaceName` cmdlet assigns the custom name `'My Fancy Runspace'` to the task's runspace. This is especially useful for debugging or when examining logs, as the custom name makes the runspace more recognizable. + +### Retrieving Runspace Names + +Pode also provides the `Get-PodeCurrentRunspaceName` cmdlet to retrieve the name of the current runspace. This is particularly helpful when you need to log or display the runspace name dynamically during execution. + +```powershell +Get-PodeCurrentRunspaceName +``` + +This cmdlet returns the name of the current runspace, allowing for easier tracking and management in complex scenarios with multiple concurrent runspaces. + +#### Example + +Here’s an example that uses `Get-PodeCurrentRunspaceName` to output the runspace name during the execution of a schedule: + +```powershell +Add-PodeSchedule -Name 'TestSchedule' -Cron '@hourly' -ScriptBlock { + Write-PodeHost "Runspace name: $(Get-PodeCurrentRunspaceName)" +} +``` + +In this example, the schedule outputs the name of the runspace executing the script block every hour. This can be useful for logging and monitoring purposes when dealing with multiple schedules or tasks. diff --git a/docs/Getting-Started/FirstApp.md b/docs/Getting-Started/FirstApp.md index df2cdc0da..c1e52ceab 100644 --- a/docs/Getting-Started/FirstApp.md +++ b/docs/Getting-Started/FirstApp.md @@ -36,6 +36,16 @@ Success, saved package.json Import-Module -Name Pode -MaximumVersion 2.99.99 ``` +* To ensure that any errors during the import process are caught and handled appropriately, use a try-catch block: + +```powershell +try { + Import-Module -Name 'Pode' -MaximumVersion 2.99.99 -ErrorAction Stop +} catch { + # exception management code +} +``` + * Within your `server.ps1` file, first you need to start the Server. This is where the main script will go that defines how the server should function: ```powershell diff --git a/docs/Getting-Started/GitHubCodespace.md b/docs/Getting-Started/GitHubCodespace.md new file mode 100644 index 000000000..78fa890de --- /dev/null +++ b/docs/Getting-Started/GitHubCodespace.md @@ -0,0 +1,55 @@ + +# GitHub Codespace and Pode + +GitHub Codespaces provides a cloud-based development environment directly integrated with GitHub. This allows you to set up your development environment with pre-configured settings, tools, and extensions. In this guide, we will walk you through using GitHub Codespace to work with Pode, a web framework for building web applications and APIs in PowerShell. + +## Prerequisites + +- A GitHub account +- A repository set up for your Pode project, including the `devcontainer.json` configuration file. + +## Launching GitHub Codespace + +1. **Open GitHub Codespace:** + + Go to your GitHub repository on the web. Click on the green `Code` button, and then select `Open with Codespaces`. If you don't have any Codespaces created, you can create a new one by clicking `New codespace`. + +2. **Codespace Initialization:** + + Once the Codespace is created, it will use the existing `devcontainer.json` configuration to set up the environment. This includes installing the necessary VS Code extensions and PowerShell modules specified in the configuration. + +3. **Verify the Setup:** + + - The terminal in the Codespace will default to PowerShell (`pwsh`). + - Check that the required PowerShell modules are installed by running: + + ```powershell + Get-Module -ListAvailable + ``` + + You should see `InvokeBuild` and `Pester` listed among the available modules. + +## Running a Pode Application + +1. **Use an Example Pode Project:** + + Pode comes with several examples in the `examples` folder. You can run one of these examples to verify that your setup is working. For instance, let's use the `HelloWorld` example. + +2. **Open HelloWorld** + + Navigate to the `examples/HelloWorld` directory and open the `HelloWorld.ps1` file + +3. **Run the sample** + + Run the Pode server by executing the `HelloWorld.ps1` script in the PowerShell terminal: + + ```powershell + ./examples/HelloWorld/HelloWorld.ps1 + ``` + or using the `Run/Debug` on the UI + +4. **Access the Pode Application:** + + Once the Pode server is running, you can access your Pode application by navigating to the forwarded port provided by GitHub Codespaces. This is usually indicated by a URL in the terminal or in the Codespaces interface. + +For more information on using Pode and its features, refer to the [Pode documentation](https://badgerati.github.io/Pode/). diff --git a/docs/Getting-Started/KnownIssues.md b/docs/Getting-Started/KnownIssues.md index 9ceee09a5..c4ca375ea 100644 --- a/docs/Getting-Started/KnownIssues.md +++ b/docs/Getting-Started/KnownIssues.md @@ -18,13 +18,30 @@ New-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Services\HTTP\Parameters' ## PowerShell Classes -Pode uses Runspaces to deal with multithreading and other background tasks. Due to this, PowerShell classes do not work as intended and are unsafe to use. +Pode utilizes Runspaces for multithreading and other background tasks, which makes PowerShell classes behave unpredictably and renders them unsafe to use. This is primarily because an instance of a class created in one Runspace will always be marshaled back to the original Runspace whenever it is accessed again, potentially causing Routes and Middleware to become contaminated. -You can find more information about this issue [here on PowerShell](https://github.com/PowerShell/PowerShell/issues/3651). +For more details on this issue, you can refer to the [PowerShell GitHub issue](https://github.com/PowerShell/PowerShell/issues/3651). -The crux of the issue is that if you create an instance of a class in one Runspace, then every time you try to use that instance again it will always be marshaled back to the original Runspace. This means Routes and Middleware can become contaminated. +To avoid these problems, it is recommended to use Hashtables or PSObjects instead. -It's recommended to switch to either Hashtables or PSObjects, but if you need to use classes then the following should let classes work: +However, if you need to use classes, PowerShell 7.4 introduces the `[NoRunspaceAffinity()]` attribute that makes classes thread-safe by solving this issue. + +Here's an example of a class definition with the `[NoRunspaceAffinity()]` attribute: + +```powershell +# Class definition with NoRunspaceAffinity attribute +[NoRunspaceAffinity()] +class SafeClass { + static [object] ShowRunspaceId($val) { + return [PSCustomObject]@{ + ThreadId = [Threading.Thread]::CurrentThread.ManagedThreadId + RunspaceId = [runspace]::DefaultRunspace.Id + } + } +} +``` + +If you need to support versions prior to PowerShell 7.4, you can use the following approach: * Create a module (CreateClassInstanceHelper.psm1) with the content: diff --git a/docs/Servers/TCP.md b/docs/Servers/TCP.md index 7a2acd44a..10425897c 100644 --- a/docs/Servers/TCP.md +++ b/docs/Servers/TCP.md @@ -172,14 +172,14 @@ Start-PodeServer { Verbs will be passed the `$TcpEvent` object, that contains the Request, Response, and other properties: -| Name | Type | Description | -| ---- | ---- | ----------- | -| Request | object | The raw Request object | -| Response | object | The raw Response object | -| Lockable | hashtable | A synchronized hashtable that can be used with `Lock-PodeObject` | -| Endpoint | hashtable | Contains the Address and Protocol of the endpoint being hit - such as "pode.example.com" or "127.0.0.2", or HTTP or HTTPS for the Protocol | -| Parameters | hashtable | Contains the parsed parameter values from the Verb's path | -| Timestamp | datetime | The current date and time of the Request | +| Name | Type | Description | +| ---------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| Request | object | The raw Request object | +| Response | object | The raw Response object | +| Lockable | hashtable | A synchronized hashtable that can be used with `Lock-PodeObject` | +| Endpoint | hashtable | Contains the Address and Protocol of the endpoint being hit - such as "pode.example.com" or "127.0.0.2", or HTTP or HTTPS for the Protocol | +| Parameters | hashtable | Contains the parsed parameter values from the Verb's path | +| Timestamp | datetime | The current date and time of the Request | ## Test Send @@ -189,11 +189,11 @@ The following function can be used to test sending messages to a TCP server. Thi function Send-TCPMessage($Endpoint, $Port, $Message) { # Setup connection $Address = [System.Net.IPAddress]::Parse([System.Net.Dns]::GetHostAddresses($EndPoint)) - $Socket = New-Object System.Net.Sockets.TCPClient($Address,$Port) + $Socket = [System.Net.Sockets.TcpClient]::new($Address, $Port) - # Setup stream wrtier + # Setup stream writer $Stream = $Socket.GetStream() - $Writer = New-Object System.IO.StreamWriter($Stream) + $Writer = [System.IO.StreamWriter]::new($Stream) # Write message to stream $Writer.WriteLine($Message) diff --git a/docs/Tutorials/Authentication/Inbuilt/UserFile.md b/docs/Tutorials/Authentication/Inbuilt/UserFile.md index 564c54dbc..4b9109809 100644 --- a/docs/Tutorials/Authentication/Inbuilt/UserFile.md +++ b/docs/Tutorials/Authentication/Inbuilt/UserFile.md @@ -20,14 +20,14 @@ The default users file is `./users.json` at the root of the server. You can supp The users file is a JSON array of user objects, each user object must contain the following (metadata is optional): -| Name | Type | Description | -| ---- | ---- | ----------- | -| Username | string | The user's username | -| Name | string | The user's fullname | -| Email | string | The user's email address | -| Password | string | Either a SHA256 or an HMAC SHA256 of the user's password | -| Groups | string[] | An array of groups which the the user is a member | -| Metadata | psobject | Custom metadata for the user | +| Name | Type | Description | +| -------- | -------- | -------------------------------------------------------- | +| Username | string | The user's username | +| Name | string | The user's fullname | +| Email | string | The user's email address | +| Password | string | Either a SHA256 or an HMAC SHA256 of the user's password | +| Groups | string[] | An array of groups which the the user is a member | +| Metadata | psobject | Custom metadata for the user | For example: @@ -66,7 +66,7 @@ Regardless of whether the password is a standard SHA256 hash or HMAC hash, the h ```powershell function ConvertTo-SHA256([string]$String) { - $SHA256 = New-Object System.Security.Cryptography.SHA256Managed + $SHA256 = [System.Security.Cryptography.SHA256Managed]::new() $SHA256Hash = $SHA256.ComputeHash([Text.Encoding]::ASCII.GetBytes($String)) $SHA256HashString = [Convert]::ToBase64String($SHA256Hash) return $SHA256HashString @@ -77,7 +77,7 @@ function ConvertTo-SHA256([string]$String) ```powershell function ConvertTo-HMACSHA256([string]$String, [string]$Secret) { - $HMACSHA256 = New-Object System.Security.Cryptography.HMACSHA256 + $HMACSHA256 = [System.Security.Cryptography.HMACSHA256]::new() $HMACSHA256.Secret = [Text.Encoding]::ASCII.GetBytes($Secret) $HMACSHA256Hash = $HMACSHA256.ComputeHash([Text.Encoding]::ASCII.GetBytes($String)) $HMACSHA256HashString = [Convert]::ToBase64String($HMACSHA256Hash) @@ -89,13 +89,13 @@ function ConvertTo-HMACSHA256([string]$String, [string]$Secret) { The User object returned, and accessible on Routes, and other functions via the [web event](../../../WebEvent)'s `$WebEvent.Auth.User` property, will contain the following information: -| Name | Type | Description | -| ---- | ---- | ----------- | -| Username | string | The user's username | -| Name | string | The user's fullname | -| Email | string | The user's email address | -| Groups | string[] | An array of groups which the the user is a member | -| Metadata | psobject | Custom metadata for the user | +| Name | Type | Description | +| -------- | -------- | ------------------------------------------------- | +| Username | string | The user's username | +| Name | string | The user's fullname | +| Email | string | The user's email address | +| Groups | string[] | An array of groups which the the user is a member | +| Metadata | psobject | Custom metadata for the user | Such as: diff --git a/docs/Tutorials/Basics.md b/docs/Tutorials/Basics.md index 482ba68ae..137aa8b29 100644 --- a/docs/Tutorials/Basics.md +++ b/docs/Tutorials/Basics.md @@ -1,15 +1,25 @@ # Basics + !!! Warning +You can initiate only one server per PowerShell instance. To run multiple servers, start additional PowerShell, or pwsh, sessions. Each session can run its own server. This is fundamental to how Pode operates, so consider it when designing your scripts and infrastructure. -!!! warning - You can only start one server in your script - -Although not required, it is recommended to import the Pode module using a maximum version, to avoid any breaking changes from new major versions: +While it’s not mandatory, we strongly recommend importing the Pode module with a specified maximum version. This practice helps to prevent potential issues arising from breaking changes introduced in new major versions: ```powershell Import-Module -Name Pode -MaximumVersion 2.99.99 ``` +To further enhance the robustness of your code, consider wrapping the import statement within a try/catch block. This way, if the module fails to load, your script won’t proceed, preventing possible errors or unexpected behavior: + +```powershell +try { + Import-Module -Name Pode -MaximumVersion 2.99.99 +} catch { + Write-Error "Failed to load the Pode module" + throw +} +``` + The script for your server should be set in the [`Start-PodeServer`](../../Functions/Core/Start-PodeServer) function, via the `-ScriptBlock` parameter. The following example will listen over HTTP on port 8080, and expose a simple HTML page of running processes at `http://localhost:8080/processes`: ```powershell @@ -72,3 +82,29 @@ PS> Start-PodeServer -FilePath './File.ps1' !!! tip Normally when you restart your Pode server any changes to the main scriptblock don't reflect. However, if you reference a file instead, then restarting the server will reload the scriptblock from that file - so any changes will reflect. + +## Internationalization + +Pode has built-in support for internationalization (i18n). By default, Pode uses the `$PsUICulture` variable to determine the User Interface Culture (UICulture). + +You can enforce a specific localization when importing the Pode module by using the UICulture argument. This argument accepts a culture code, which specifies the language and regional settings to use. + +Here’s an example of how to enforce Korean localization: + +```powershell +Import-Module -Name Pode -ArgumentList 'ko-KR' +``` + +In this example, 'ko-KR' is the culture code for Korean as used in South Korea. You can replace 'ko-KR' with the culture code for any other language or region. + +As an alternative to specifying the UICulture when importing the Pode module, you can also change the UICulture within the PowerShell environment itself. + +This can be done using the following command: + +```powershell +[System.Threading.Thread]::CurrentThread.CurrentUICulture = 'ko-KR' +``` + +This command changes the UICulture for the current PowerShell session to Korean as used in South Korea. + +Please note that this change is temporary and will only affect the current session. If you open a new PowerShell session, it will use the default UICulture. \ No newline at end of file diff --git a/docs/Tutorials/CORS.md b/docs/Tutorials/CORS.md new file mode 100644 index 000000000..b4496cb55 --- /dev/null +++ b/docs/Tutorials/CORS.md @@ -0,0 +1,93 @@ + +# CORS + +## What is CORS? +Cross-Origin Resource Sharing (CORS) is a security feature implemented by web browsers to restrict web pages from making requests to a different domain than the one that served the web page. This is a critical aspect of web security, helping to prevent malicious sites from accessing sensitive data from another domain. + +## CORS Challenges +When developing web applications, you may encounter situations where your web page needs to request resources from a different domain. This can lead to CORS errors if the appropriate headers are not set to allow these cross-origin requests. Common challenges include: +- Handling pre-flight requests. +- Allowing specific methods and headers. +- Managing credentials in cross-origin requests. +- Setting the appropriate origins. + +## Addressing CORS Challenges + +Pode simplifies handling CORS by providing the `Set-PodeSecurityAccessControl` function, which allows you to define the necessary headers to manage cross-origin requests effectively. + +### Key Headers for CORS + +1. **Access-Control-Allow-Origin**: Specifies which origins are permitted to access the resource. +2. **Access-Control-Allow-Methods**: Lists the HTTP methods that are allowed when accessing the resource. +3. **Access-Control-Allow-Headers**: Indicates which HTTP headers can be used during the actual request. +4. **Access-Control-Max-Age**: Specifies how long the results of a pre-flight request can be cached. +5. **Access-Control-Allow-Credentials**: Indicates whether credentials are allowed in the request. + +### Setting CORS Headers instead + +The `Set-PodeSecurityAccessControl` function allows you to set these headers easily. Here’s how you can address common CORS challenges using this function: + +1. **Allowing All Origins** + ```powershell + Set-PodeSecurityAccessControl -Origin '*' + ``` + This sets the `Access-Control-Allow-Origin` header to allow requests from any origin. + +2. **Specifying Allowed Methods** + ```powershell + Set-PodeSecurityAccessControl -Methods 'GET', 'POST', 'OPTIONS' + ``` + This sets the `Access-Control-Allow-Methods` header to allow only the specified methods. + +3. **Specifying Allowed Headers** + ```powershell + Set-PodeSecurityAccessControl -Headers 'Content-Type', 'Authorization' + ``` + This sets the `Access-Control-Allow-Headers` header to allow the specified headers. + +4. **Handling Credentials** + ```powershell + Set-PodeSecurityAccessControl -Credentials + ``` + This sets the `Access-Control-Allow-Credentials` header to allow credentials in requests. + +5. **Setting Cache Duration for Pre-flight Requests** + ```powershell + Set-PodeSecurityAccessControl -Duration 3600 + ``` + This sets the `Access-Control-Max-Age` header to cache the pre-flight request for one hour. + +6. **Automatic Header and Method Detection** + ```powershell + Set-PodeSecurityAccessControl -AutoHeaders -AutoMethods + ``` + These parameters automatically populate the list of allowed headers and methods based on your OpenApi definition and defined routes, respectively. + +7. **Enabling Global OPTIONS Route** + ```powershell + Set-PodeSecurityAccessControl -WithOptions + ``` + This creates a global OPTIONS route to handle pre-flight requests automatically. + +8. **Additional Security with Cross-Domain XHR Requests** + ```powershell + Set-PodeSecurityAccessControl -CrossDomainXhrRequests + ``` + This adds the 'x-requested-with' header to the list of allowed headers, enhancing security. + +### Example Configuration + +Here is an example of configuring CORS settings in Pode using `Set-PodeSecurityAccessControl`: + +```powershell +Set-PodeSecurityAccessControl -Origin 'https://example.com' -Methods 'GET', 'POST' -Headers 'Content-Type', 'Authorization' -Duration 7200 -Credentials -WithOptions -AutoHeaders -AutoMethods -CrossDomainXhrRequests +``` + +This example sets up CORS to allow requests from `https://example.com`, allows `GET` and `POST` methods, permits `Content-Type` and `Authorization` headers, enables credentials, caches pre-flight requests for two hours, automatically detects headers and methods, and allows cross-domain XHR requests. + +### More Information on CORS + +For more information on CORS, you can refer to the following resources: +- [Fetch Living Standard](https://fetch.spec.whatwg.org/) +- [CORS in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-7.0#credentials-in-cross-origin-requests) +- [MDN Web Docs on CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) diff --git a/docs/Tutorials/Compression/Requests.md b/docs/Tutorials/Compression/Requests.md index 804cea5e8..f5604a307 100644 --- a/docs/Tutorials/Compression/Requests.md +++ b/docs/Tutorials/Compression/Requests.md @@ -94,8 +94,8 @@ $message = ($data | ConvertTo-Json) $bytes = [System.Text.Encoding]::UTF8.GetBytes($message) # compress the message using gzip -$ms = New-Object -TypeName System.IO.MemoryStream -$gzip = New-Object System.IO.Compression.GZipStream($ms, [IO.Compression.CompressionMode]::Compress, $true) +$ms = [System.IO.MemoryStream]::new() +$gzip = [System.IO.Compression.GZipStream]::new($ms, [IO.Compression.CompressionMode]::Compress, $true) $gzip.Write($bytes, 0, $bytes.Length) $gzip.Close() $ms.Position = 0 diff --git a/docs/Tutorials/Configuration.md b/docs/Tutorials/Configuration.md index 89c59d724..6b18e04fa 100644 --- a/docs/Tutorials/Configuration.md +++ b/docs/Tutorials/Configuration.md @@ -68,19 +68,22 @@ A "path" like `Server.Ssl.Protocols` looks like the below in the file: } ``` -| Path | Description | Docs | -| -------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------ | -| Server.Ssl.Protocols | Indicates the SSL Protocols that should be used | [link](../Certificates) | -| Server.Request | Defines request timeout and maximum body size | [link](../RequestLimits) | -| Server.AutoImport | Defines the AutoImport scoping rules for Modules, SnapIns and Functions | [link](../Scoping) | -| Server.Logging | Defines extra configuration for Logging, like masking sensitive data | [link](../Logging/Overview) | -| Server.Root | Overrides root path of the server | [link](../Misc/ServerRoot) | -| Server.Restart | Defines configuration for automatically restarting the server | [link](../Restarting/Types/AutoRestarting) | -| Server.FileMonitor | Defines configuration for restarting the server based on file updates | [link](../Restarting/Types/FileMonitoring) | -| Web.OpenApi.DefaultDefinitionTag | Define the primary tag name for OpenAPI ( 'default' is the default) | [link](../OpenAPI/OpenAPI) | -| Web.Static.ValidateLast | Changes the way routes are processed. | [link](../Routes/Utilities/StaticContent) | -| Web.TransferEncoding | Sets the Request TransferEncoding | [link](../Compression/Requests) | -| Web.Compression | Sets any compression to use on the Response | [link](../Compression/Responses) | -| Web.ContentType | Define expected Content Types for certain Routes | [link](../Routes/Utilities/ContentTypes) | -| Web.ErrorPages | Defines configuration for custom error pages | [link](../Routes/Utilities/ErrorPages) | -| Web.Static | Defines configuration for static content, such as caching | [link](../Routes/Utilities/StaticContent) | +| Path | Description | Docs | +| -------------------------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------- | +| Server.Ssl.Protocols | Indicates the SSL Protocols that should be used | [link](../Certificates) | +| Server.Request | Defines request timeout and maximum body size | [link](../RequestLimits) | +| Server.AutoImport | Defines the AutoImport scoping rules for Modules, SnapIns and Functions | [link](../Scoping) | +| Server.Logging | Defines extra configuration for Logging, like masking sensitive data | [link](../Logging/Overview) | +| Server.Root | Overrides root path of the server | [link](../Misc/ServerRoot) | +| Server.Restart | Defines configuration for automatically restarting the server | [link](../Restarting/Types/AutoRestarting) | +| Server.FileMonitor | Defines configuration for restarting the server based on file updates | [link](../Restarting/Types/FileMonitoring) | +| Server.ReceiveTimeout | Define the amount of time a Receive method call will block waiting for data | [link](../Endpoints/Basic/StaticContent/#server-timeout) | +| Server.DefaultFolders | Set the Default Folders paths | [link](../Routes/Utilities/StaticContent/#changing-the-default-folders) | +| Web.OpenApi.DefaultDefinitionTag | Define the primary tag name for OpenAPI ( `default` is the default) | [link](../OpenAPI/Overview) | +| Web.OpenApi.UsePodeYamlInternal | Force the use of the internal YAML converter (`False` is the default) | | +| Web.Static.ValidateLast | Changes the way routes are processed. | [link](../Routes/Utilities/StaticContent) | +| Web.TransferEncoding | Sets the Request TransferEncoding | [link](../Compression/Requests) | +| Web.Compression | Sets any compression to use on the Response | [link](../Compression/Responses) | +| Web.ContentType | Define expected Content Types for certain Routes | [link](../Routes/Utilities/ContentTypes) | +| Web.ErrorPages | Defines configuration for custom error pages | [link](../Routes/Utilities/ErrorPages) | +| Web.Static | Defines configuration for static content, such as caching | [link](../Routes/Utilities/StaticContent) | \ No newline at end of file diff --git a/docs/Tutorials/Endpoints/Basics.md b/docs/Tutorials/Endpoints/Basics.md index f4dbd93f8..888a529ba 100644 --- a/docs/Tutorials/Endpoints/Basics.md +++ b/docs/Tutorials/Endpoints/Basics.md @@ -174,3 +174,17 @@ The following is the structure of the Endpoint object internally, as well as the | Protocol | string | The protocol of the Endpoint. Such as: HTTP, HTTPS, WS, etc. | | Type | string | The type of the Endpoint. Such as: HTTP, WS, SMTP, TCP | | Certificate | hashtable | Details about the certificate that will be used for SSL Endpoints | + +## Server timeout + +The timeout is configurable using the `ReceiveTimeout` property in the `server.psd1` configuration file, the amount of time is in milliseconds. The meaning is that the server will wait for data to be received before timing out. This is useful for controlling how long the server should wait during data reception operations, enhancing the performance and responsiveness. + +To set this property, include it in `server.psd1` configuration file as shown below: + +```powershell +@{ + Server = @{ + ReceiveTimeout = 5000 # Timeout in milliseconds + } +} +``` diff --git a/docs/Tutorials/Middleware/Types/Sessions.md b/docs/Tutorials/Middleware/Types/Sessions.md index ba622c0e2..90221f1dc 100644 --- a/docs/Tutorials/Middleware/Types/Sessions.md +++ b/docs/Tutorials/Middleware/Types/Sessions.md @@ -111,7 +111,7 @@ For example, the following is a mock up of a Storage for Redis. Note that the fu ```powershell # create the object -$store = New-Object -TypeName psobject +$store = [psobject]::new() # add a Get property for retreiving a session's data by SessionId $store | Add-Member -MemberType NoteProperty -Name Get -Value { diff --git a/docs/Tutorials/OpenAPI/1Overview.md b/docs/Tutorials/OpenAPI/1Overview.md new file mode 100644 index 000000000..65cf5ebe6 --- /dev/null +++ b/docs/Tutorials/OpenAPI/1Overview.md @@ -0,0 +1,312 @@ +# Overview + +Pode has built-in support for converting your routes into OpenAPI 3.0 definitions. There is also support for enabling simple Swagger and/or ReDoc viewers and others. + +The OpenApi module has been extended with many more functions, and some old ones have been improved. + +For more detailed information regarding OpenAPI and Pode, please refer to [OpenAPI Specification and Pode](../Specification/v3_0_3.md) + +You can enable OpenAPI in Pode, and a straightforward definition will be generated. However, to get a more complex definition with request bodies, parameters, and response payloads, you'll need to use the relevant OpenAPI functions detailed below. + +## Enabling OpenAPI + +To enable support for generating OpenAPI definitions you'll need to use the [`Enable-PodeOpenApi`](../../../Functions/OpenApi/Enable-PodeOpenApi) function. This will allow you to set a title and version for your API. You can also set a default route to retrieve the OpenAPI definition for tools like Swagger or ReDoc, the default is at `/openapi`. + +You can also set a route filter (such as `/api/*`, the default is `/*` for everything), so only those routes are included in the definition. + +An example of enabling OpenAPI is a follows: + +```powershell +Enable-PodeOpenApi -Title 'My Awesome API' -Version 9.0.0.1 +``` + +An example of setting the OpenAPI route is a follows. This will create a route accessible at `/docs/openapi`: + +```powershell +Enable-PodeOpenApi -Path '/docs/openapi' -Title 'My Awesome API' -Version 9.0.0.1 +``` + +### Default Setup + +In the very simplest of scenarios, just enabling OpenAPI will generate a minimal definition. It can be viewed in Swagger/ReDoc etc, but won't be usable for trying calls. + +When you enable OpenAPI, and don't set any other OpenAPI data, the following is the minimal data that is included: + +* Every route will have a 200 and Default response +* Although routes will be included, no request bodies, parameters or response payloads will be defined +* If you have multiple endpoints, then the servers section will be included +* Any authentication will be included + +This can be changed with [`Enable-PodeOpenApi`](../../../Functions/OpenApi/Enable-PodeOpenApi) + +For example to change the default response 404 and 500 + +```powershell +Enable-PodeOpenApi -Path '/docs/openapi' -OpenApiVersion '3.0.3' -DefaultResponses ( + New-PodeOAResponse -StatusCode 404 -Description 'User not found' | Add-PodeOAResponse -StatusCode 500 + ) +``` + +For disabling the Default Response use: + +```powershell +Enable-PodeOpenApi -Path '/docs/openapi' -OpenApiVersion '3.0.3' -NoDefaultResponses +``` + +For disabling the Minimal Definitions feature use: + +```powershell +Enable-PodeOpenApi -Path '/docs/openapi' -OpenApiVersion '3.0.3' -DisableMinimalDefinitions +``` + +### Get Definition + +Instead of defining a route to return the definition, you can write the definition to the response whenever you want, and in any route, using the [`Get-PodeOADefinition`](../../../Functions/OpenApi/Get-PodeOADefinition) function. This could be useful in certain scenarios like in Azure Functions, where you can enable OpenAPI, and then write the definition to the response of a GET request if some query parameter is set; eg: `?openapi=1`. + +For example: + +```powershell +Add-PodeRoute -Method Get -Path '/' -ScriptBlock { + if ($WebEvent.Query.openapi -eq 1) { + Get-PodeOpenApiDefinition | Write-PodeJsonResponse + } +} +``` + +## OpenAPI Info object + +In previous releases some of the Info object properties like Version and Title were defined by [`Enable-PodeOpenApi`](../../../Functions/OpenApi/Enable-PodeOpenApi). +Starting from version 2.10 a new [`Add-PodeOAInfo`](../../../Functions/OpenApi/Add-PodeOAInfo) function has been added to create a full OpenAPI Info spec. + +```powershell +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 'apiteam@swagger.io' +``` + +## OpenAPI configuration Best Practice + +Pode is rich of functions to create and configure an complete OpenApi spec. Here is a typical code you should use to initiate an OpenApi spec + +```powershell +#Initialize OpenApi +Enable-PodeOpenApi -Path '/docs/openapi' -Title 'Swagger Petstore - OpenAPI 3.0' ` + -OpenApiVersion 3.1 -DisableMinimalDefinitions -NoDefaultResponses + +# OpenApi Info +Add-PodeOAInfo -Title 'Swagger Petstore - OpenAPI 3.0' ` + -Version 1.0.17 ` + -Description 'This is a sample Pet Store Server based on the OpenAPI 3.0 specification. ...' ` + -TermsOfService 'http://swagger.io/terms/' ` + -LicenseName 'Apache 2.0' ` + -LicenseUrl 'http://www.apache.org/licenses/LICENSE-2.0.html' ` + -ContactName 'API Support' ` + -ContactEmail 'apiteam@swagger.io' ` + -ContactUrl 'http://example.com/support' + +# Endpoint for the API + Add-PodeOAServerEndpoint -url '/api/v3.1' -Description 'default endpoint' + + # OpenApi external documentation links + $extDoc = New-PodeOAExternalDoc -Name 'SwaggerDocs' -Description 'Find out more about Swagger' -Url 'http://swagger.io' + $extDoc | Add-PodeOAExternalDoc + + # OpenApi documentation viewer + Enable-PodeOAViewer -Type Swagger -Path '/docs/swagger' + Enable-PodeOAViewer -Type ReDoc -Path '/docs/redoc' + Enable-PodeOAViewer -Type RapiDoc -Path '/docs/rapidoc' + Enable-PodeOAViewer -Type StopLight -Path '/docs/stoplight' + Enable-PodeOAViewer -Type Explorer -Path '/docs/explorer' + Enable-PodeOAViewer -Bookmarks -Path '/docs' +``` + +## Authentication + +Any authentication defined, either by [`Add-PodeAuthMiddleware`](../../../Functions/Authentication/Add-PodeAuthMiddleware), or using the `-Authentication` parameter on Routes, will be automatically added to the `security` section of the OpenAPI definition. + + +## Tags + +In OpenAPI, a "tag" is used to group related operations. Tags are often used to organize and categorize endpoints in an API specification, making it easier to understand and navigate the API documentation. Each tag can be associated with one or more API operations, and these tags are then used in tools like Swagger UI to group and display operations in a more organized way. + +Here's an example of how to define and use tags: + +```powershell +# create an External Doc reference +$swaggerDocs = New-PodeOAExternalDoc -Description 'Find out more about Swagger' -Url 'http://swagger.io' + +# create a Tag +Add-PodeOATag -Name 'pet' -Description 'Everything about your Pets' -ExternalDoc $swaggerDocs + +Add-PodeRoute -PassThru -Method get -Path '/pet/findByStatus' -Authentication 'Login-OAuth2' -Scope 'read' -AllowAnon -ScriptBlock { + #route code +} | Set-PodeOARouteInfo -Summary 'Finds Pets by status' -Description 'Multiple status values can be provided with comma-separated strings' ` + -Tags 'pet' -OperationId 'findPetsByStatus' +``` + +## Routes + +To extend the definition of a route, you can use the `-PassThru` switch on the [`Add-PodeRoute`](../../../Functions/Routes/Add-PodeRoute) function. This will cause the route that was created to be returned, so you can pass it down the pipe into more OpenAPI functions. + +To add metadata to a route's definition you can use the [`Set-PodeOARouteInfo`](../../../Functions/OpenApi/Set-PodeOARouteInfo) function. This will allow you to define a summary/description for the route, as well as tags for grouping: + +```powershell +Add-PodeRoute -Method Get -Path "/api/resources" -ScriptBlock { + Set-PodeResponseStatus -Code 200 +} -PassThru | + Set-PodeOARouteInfo -Summary 'Retrieve some resources' -Tags 'Resources' +``` + +Each of the following OpenAPI functions have a `-PassThru` switch, allowing you to chain many of them together. + +### Responses + +You can define multiple responses for a route, but only one of each status code, using the [`Add-PodeOAResponse`](../../../Functions/OpenApi/Add-PodeOAResponse) function. You can either just define the response and status code, with a custom description, or with a schema defining the payload of the response. + +The following is an example of defining simple 200 and 404 responses on a route: + +```powershell +Add-PodeRoute -Method Get -Path "/api/user/:userId" -ScriptBlock { + # logic +} -PassThru | + Add-PodeOAResponse -StatusCode 200 -PassThru | + Add-PodeOAResponse -StatusCode 404 -Description 'User not found' +``` + +Whereas the following is a more complex definition, which also defines the responses JSON payload. This payload is defined as an object with a string Name, and integer UserId: + +```powershell +Add-PodeRoute -Method Get -Path '/api/users/:userId' -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Name = 'Rick' + UserId = $WebEvent.Parameters['userId'] + } +} -PassThru | + Add-PodeOAResponse -StatusCode 200 -Description 'A user object' --Content @{ + 'application/json' = (New-PodeOAStringProperty -Name 'Name'| + New-PodeOAIntProperty -Name 'UserId'| New-PodeOAObjectProperty) + } +``` + +the JSON response payload defined is as follows: + +```json +{ + "Name": [string], + "UserId": [integer] +} +``` + +In case the response JSON payload is an array + +```powershell +Add-PodeRoute -Method Get -Path '/api/users/:userId' -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Name = 'Rick' + UserId = $WebEvent.Parameters['userId'] + } + } -PassThru | + Add-PodeOAResponse -StatusCode 200 -Description 'A user object' -Content ( + New-PodeOAContentMediaType -ContentType 'application/json' -Array -Content ( + New-PodeOAStringProperty -Name 'Name' | + New-PodeOAIntProperty -Name 'UserId' | + New-PodeOAObjectProperty + ) + ) +``` + +```json +[ + { + "Name": [string], + "UserId": [integer] + } +] +``` + +Internally, each route is created with an empty default 200 and 500 response. You can remove these, or other added responses, by using [`Remove-PodeOAResponse`](../../../Functions/OpenApi/Remove-PodeOAResponse): + +```powershell +Add-PodeRoute -Method Get -Path "/api/user/:userId" -ScriptBlock { + # route logic +} -PassThru | + Remove-PodeOAResponse -StatusCode 200 +``` + +### Requests + +#### Parameters + +You can set route parameter definitions, such as parameters passed in the path/query, by using the [`Set-PodeOARequest`](../../../Functions/OpenApi/Set-PodeOARequest) function with the `-Parameters` parameter. The parameter takes an array of properties converted into parameters, using the [`ConvertTo-PodeOAParameter`](../../../Functions/OpenApi/ConvertTo-PodeOAParameter) function. + +For example, to create some integer `userId` parameter that is supplied in the path of the request, the following will work: + +```powershell +Add-PodeRoute -Method Get -Path '/api/users/:userId' -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Name = 'Rick' + UserId = $WebEvent.Parameters['userId'] + } +} -PassThru | + Set-PodeOARequest -Parameters @( + (New-PodeOAIntProperty -Name 'userId' -Required | ConvertTo-PodeOAParameter -In Path) + ) +``` + +Whereas you could use the next example to define 2 query parameters, both strings: + +```powershell +Add-PodeRoute -Method Get -Path '/api/users' -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Name = 'Rick' + UserId = $WebEvent.Query['name'] + } +} -PassThru | + Set-PodeOARequest -Parameters ( + (New-PodeOAStringProperty -Name 'name' -Required | ConvertTo-PodeOAParameter -In Query), + (New-PodeOAStringProperty -Name 'city' -Required | ConvertTo-PodeOAParameter -In Query) + ) +``` + +#### Payload + +You can set request payload schemas by using the [`Set-PodeOARequest`](../../../Functions/OpenApi/Set-PodeOARequest)function, with the `-RequestBody` parameter. The request body can be defined using the [`New-PodeOARequestBody`](../../../Functions/OpenApi/New-PodeOARequestBody) function, and supplying schema definitions for content types - this works in very much a similar way to defining responses above. + +For example, to define a request JSON payload of some `userId` and `name` you could use the following: + +```powershell +Add-PodeRoute -Method Patch -Path '/api/users' -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Name = $WebEvent.Data.name + UserId = $WebEvent.Data.userId + } +} -PassThru | + Set-PodeOARequest -RequestBody ( + New-PodeOARequestBody -Required -Content ( + New-PodeOAContentMediaType -ContentType 'application/json','application/xml' -Content ( New-PodeOAStringProperty -Name 'Name'| New-PodeOAIntProperty -Name 'UserId'| New-PodeOAObjectProperty ) ) + + ) +``` + +The expected payload would look as follows: + +```json +{ + "name": [string], + "userId": [integer] +} +``` + +```xml + + +``` + diff --git a/docs/Tutorials/OpenAPI/2Properties.md b/docs/Tutorials/OpenAPI/2Properties.md new file mode 100644 index 000000000..207c94966 --- /dev/null +++ b/docs/Tutorials/OpenAPI/2Properties.md @@ -0,0 +1,297 @@ + +# Properties + +Properties are used to create all Parameters and Schemas in OpenAPI. You can use the simple types on their own, or you can combine multiple of them together to form complex objects. + +### Simple Types + +There are 5 simple property types: Integers, Numbers, Strings, Booleans, and Schemas. Each of which can be created using the following functions: + +* [`New-PodeOAIntProperty`](../../../Functions/OAProperties/New-PodeOAIntProperty) +* [`New-PodeOANumberProperty`](../../../Functions/OAProperties/New-PodeOANumberProperty) +* [`New-PodeOAStringProperty`](../../../Functions/OAProperties/New-PodeOAStringProperty) +* [`New-PodeOABoolProperty`](../../../Functions/OAProperties/New-PodeOABoolProperty) +* [`New-PodeOASchemaProperty`](../../../Functions//New-PodeOASchemaProperty) +* [`New-PodeOAMultiTypeProperty`](../../../Functions/OAProperties/New-PodeOAMultiTypeProperty) (Note: OpenAPI 3.1 only) + +These properties can be created with a Name, and other flags such as Required and/or a Description: + +```powershell +# simple integer +New-PodeOAIntProperty -Name 'userId' + +# a float number with a max value of 100 +New-PodeOANumberProperty -Name 'ratio' -Format Float -Maximum 100 + +# a string with a default value, and enum of options +New-PodeOAStringProperty -Name 'type' -Default 'admin' -Enum @('admin', 'user') + +# a boolean that's required +New-PodeOABoolProperty -Name 'enabled' -Required + +# a schema property that references another component schema +New-PodeOASchemaProperty -Name 'Config' -Reference 'ConfigSchema' + +# a string or an integer or a null value (only available with OpenAPI 3.1) +New-PodeOAMultiTypeProperty -Name 'multi' -Type integer,string -Nullable +``` + +On their own, like above, the simple properties don't really do much. However, you can combine that together to make complex objects/arrays as defined below. + +### Arrays + +There isn't a dedicated function to create an array property, instead there is an `-Array` switch on each of the property functions - both Object and the above simple properties. + +If you supply the `-Array` switch to any of the above simple properties, this will define an array of that type - the `-Name` parameter can also be omitted if only a simple array if required. + +For example, the below will define an integer array: + +```powershell +New-PodeOAIntProperty -Array +``` + +When used in a Response, this could return the following JSON example: + +```json +[ + 0, + 1, + 2 +] +``` + +### Objects + +An object property is a combination of multiple other properties - both simple, array of more objects. + +There are two ways to define objects: + +1. Similar to arrays, you can use the `-Object` switch on the simple properties. +2. You can use the [`New-PodeOAObjectProperty`](../../../Functions/OAProperties/New-PodeOAObjectProperty) function to combine multiple properties. + +#### Simple + +If you use the `-Object` switch on the simple property function, this will automatically wrap the property as an object. The Name for this is required. + +For example, the below will define a simple `userId` integer object: + +```powershell +New-PodeOAIntProperty -Name 'userId' -Object +``` + +In a response as JSON, this could look as follows: + +```json +{ + "userId": 0 +} +``` + +Furthermore, you can also supply both `-Array` and `-Object` switches: + +```powershell +New-PodeOAIntProperty -Name 'userId' -Object -Array +``` + +This wil result in something like the following JSON: + +```json +{ + "userId": [ 0, 1, 2 ] +} +``` + +#### Complex + +Unlike the `-Object` switch that simply converts a single property into an object, the [`New-PodeOAObjectProperty`](../../../Functions/OAProperties/New-PodeOAObjectProperty) function can combine and convert multiple properties. + +For example, the following will create an object using an Integer, String, and a Boolean: + +Legacy Definition + +```powershell +New-PodeOAObjectProperty -Properties ( + (New-PodeOAIntProperty -Name 'userId'), + (New-PodeOAStringProperty -Name 'name'), + (New-PodeOABoolProperty -Name 'enabled') +) +``` + +Using piping (new in Pode 2.10) + +```powershell +New-PodeOAIntProperty -Name 'userId'| New-PodeOAStringProperty -Name 'name'| + New-PodeOABoolProperty -Name 'enabled' |New-PodeOAObjectProperty +``` + +As JSON, this could look as follows: + +```json +{ + "userId": 0, + "name": "Gary Goodspeed", + "enabled": true +} +``` + +You can also supply the `-Array` switch to the [`New-PodeOAObjectProperty`](../../../Functions/OAProperties/New-PodeOAObjectProperty) function. This will result in an array of objects. For example, if we took the above: + +```powershell +New-PodeOAIntProperty -Name 'userId'| New-PodeOAStringProperty -Name 'name'| + New-PodeOABoolProperty -Name 'enabled' |New-PodeOAObjectProperty -Array +``` + +As JSON, this could look as follows: + +```json +[ + { + "userId": 0, + "name": "Gary Goodspeed", + "enabled": true + }, + { + "userId": 1, + "name": "Kevin", + "enabled": false + } +] +``` + +You can also combine objects into other objects: + +```powershell +$usersArray = New-PodeOAIntProperty -Name 'userId'| New-PodeOAStringProperty -Name 'name'| + New-PodeOABoolProperty -Name 'enabled' |New-PodeOAObjectProperty -Array + +New-PodeOAObjectProperty -Properties @( + (New-PodeOAIntProperty -Name 'found'), + $usersArray +) +``` + +As JSON, this could look as follows: + +```json +{ + "found": 2, + "users": [ + { + "userId": 0, + "name": "Gary Goodspeed", + "enabled": true + }, + { + "userId": 1, + "name": "Kevin", + "enabled": false + } + ] +} +``` + +### oneOf, anyOf and allOf Keywords + +OpenAPI 3.x provides several keywords which you can use to combine schemas. You can use these keywords to create a complex schema or validate a value against multiple criteria. + +* oneOf - validates the value against exactly one of the sub-schemas +* allOf - validates the value against all the sub-schemas +* anyOf - validates the value against any (one or more) of the sub-schemas + +You can use the [`Merge-PodeOAProperty`](../../../Functions/OAProperties/Merge-PodeOAProperty) will instead define a relationship between the properties. + +Unlike [`New-PodeOAObjectProperty`](../../../Functions/OAProperties/New-PodeOAObjectProperty) which combines and converts multiple properties into an Object, [`Merge-PodeOAProperty`](../../../Functions/OAProperties/Merge-PodeOAProperty) will instead define a relationship between the properties. + +For example, the following will create an something like an C Union object using an Integer, String, and a Boolean: + +```powershell +Merge-PodeOAProperty -Type OneOf -ObjectDefinitions @( + (New-PodeOAIntProperty -Name 'userId' -Object), + (New-PodeOAStringProperty -Name 'name' -Object), + (New-PodeOABoolProperty -Name 'enabled' -Object) + ) +``` + +Or + +```powershell +New-PodeOAIntProperty -Name 'userId' -Object | + New-PodeOAStringProperty -Name 'name' -Object | + New-PodeOABoolProperty -Name 'enabled' -Object | + Merge-PodeOAProperty -Type OneOf +``` + +As JSON, this could look as follows: + +```json +{ + "oneOf": [ + { + "type": "object", + "properties": { + "userId": { + "type": "integer" + } + } + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false + } + } + } + ] +} +``` + +You can also supply a Component Schema created using [`Add-PodeOAComponentSchema`](../../../Functions/OAComponents/Add-PodeOAComponentSchema). For example, if we took the above: + +```powershell + New-PodeOAIntProperty -Name 'id'-Format Int64 -Example 1 -ReadOnly | + New-PodeOAStringProperty -Name 'username' -Example 'theUser' -Required | + New-PodeOAStringProperty -Name 'firstName' -Example 'John' | + New-PodeOAStringProperty -Name 'lastName' -Example 'James' | + New-PodeOAStringProperty -Name 'email' -Format email -Example 'john@email.com' | + New-PodeOAStringProperty -Name 'lastName' -Example 'James' | + New-PodeOAStringProperty -Name 'password' -Format Password -Example '12345' -Required | + New-PodeOAStringProperty -Name 'phone' -Example '12345' | + New-PodeOAIntProperty -Name 'userStatus'-Format int32 -Description 'User Status' -Example 1| + New-PodeOAObjectProperty -Name 'User' -XmlName 'user' | + Add-PodeOAComponentSchema + + New-PodeOAStringProperty -Name 'street' -Example '437 Lytton' -Required | + New-PodeOAStringProperty -Name 'city' -Example 'Palo Alto' -Required | + New-PodeOAStringProperty -Name 'state' -Example 'CA' -Required | + New-PodeOAStringProperty -Name 'zip' -Example '94031' -Required | + New-PodeOAObjectProperty -Name 'Address' -XmlName 'address' -Description 'Shipping Address' | + Add-PodeOAComponentSchema + + Merge-PodeOAProperty -Type AllOf -ObjectDefinitions 'Address','User' + +``` + +As JSON, this could look as follows: + +```json +{ + "allOf": [ + { + "$ref": "#/components/schemas/Address" + }, + { + "$ref": "#/components/schemas/User" + } + ] +} +``` \ No newline at end of file diff --git a/docs/Tutorials/OpenAPI/3Components.md b/docs/Tutorials/OpenAPI/3Components.md new file mode 100644 index 000000000..2610eb604 --- /dev/null +++ b/docs/Tutorials/OpenAPI/3Components.md @@ -0,0 +1,231 @@ +# Components + +You can define reusable OpenAPI components in Pode. Currently supported are Schemas, Parameters, Request Bodies, and Responses. + +### Schemas + +To define a reusable schema that can be used in request bodies, and responses, you can use the [`Add-PodeOAComponentSchema`](../../../Functions/OAComponents/Add-PodeOAComponentSchema) function. You'll need to supply a Name, and a Schema that can be reused. + +The following is an example of defining a schema which is a object of Name, UserId, and Age: + +```powershell +# define a reusable schema user object +New-PodeOAStringProperty -Name 'Name' | + New-PodeOAIntProperty -Name 'UserId' | + New-PodeOAIntProperty -Name 'Age' | + New-PodeOAObjectProperty | + Add-PodeOAComponentSchema -Name 'UserSchema' + +# reuse the above schema in a response +Add-PodeRoute -Method Get -Path '/api/users/:userId' -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Name = 'Rick' + UserId = $WebEvent.Parameters['userId'] + Age = 42 + } +} -PassThru | + Add-PodeOAResponse -StatusCode 200 -Description 'A list of users' -Content @{ + 'application/json' = 'UserSchema' + } +``` + +### Request Bodies + +To define a reusable request bodies you can use the [`Add-PodeOAComponentRequestBody`](../../../Functions/OAComponents/Add-PodeOAComponentRequestBody) function. You'll need to supply a Name, as well as the needed schemas for each content type. + +The following is an example of defining a JSON object that a Name, UserId, and an Enable flag: + +```powershell +# define a reusable request body +New-PodeOAContentMediaType -ContentType 'application/json', 'application/x-www-form-urlencoded' -Content ( + New-PodeOAStringProperty -Name 'Name' | + New-PodeOAIntProperty -Name 'UserId' | + New-PodeOABoolProperty -Name 'Enabled' | + New-PodeOAObjectProperty + ) | Add-PodeOAComponentRequestBody -Name 'UserBody' -Required + +# use the request body in a route +Add-PodeRoute -Method Patch -Path '/api/users' -ScriptBlock { + Set-PodeResponseStatus -StatusCode 200 +} -PassThru | + Set-PodeOARequest -RequestBody (New-PodeOARequestBody -Reference 'UserBody') +``` + +The JSON payload expected is of the format: + +```json +{ + "Name": [string], + "UserId": [integer], + "Enabled": [boolean] +} +``` + +### Parameters + +To define reusable parameters that are used on requests, you can use the [`Add-PodeOAComponentParameter`](../../../Functions/OAComponents/Add-PodeOAComponentParameter) function. You'll need to supply a Name and the Parameter definition. + +The following is an example of defining an integer path parameter for a `userId`, and then using that parameter on a route. + +```powershell +# define a reusable {userid} path parameter +New-PodeOAIntProperty -Name 'userId' -Required | ConvertTo-PodeOAParameter -In Path |Add-PodeOAComponentParameter -Name 'UserId' + +# use this parameter in a route +Add-PodeRoute -Method Get -Path '/api/users/:userId' -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Name = 'Rick' + UserId = $WebEvent.Parameters['userId'] + } +} -PassThru | + Set-PodeOARequest -Parameters @(ConvertTo-PodeOAParameter -Reference 'UserId') +``` + +### Responses + +To define a reusable response definition you can use the [`Add-PodeOAComponentResponse`](../../../Functions/OAComponents/Add-PodeOAComponentResponse) function. You'll need to supply a Name, and optionally any Content/Header schemas that define the responses payload. + +The following is an example of defining a 200 response with a JSON payload of an array of objects for Name/UserId. The Response component can be used by a route referencing the name: + +```powershell +# defines a response with a json payload using New-PodeOAContentMediaType +Add-PodeOAComponentResponse -Name 'OK' -Description 'A user object' -Content ( + New-PodeOAContentMediaType -ContentType 'application/json' -Array -Content ( + New-PodeOAStringProperty -Name 'Name' | + New-PodeOAIntProperty -Name 'UserId' | + New-PodeOAObjectProperty + ) + ) + +# reuses the above response on a route using its "OK" name +Add-PodeRoute -Method Get -Path "/api/users" -ScriptBlock { + Write-PodeJsonResponse -Value @( + @{ Name = 'Rick'; UserId = 123 }, + @{ Name = 'Geralt'; UserId = 124 } + ) +} -PassThru | + Add-PodeOAResponse -StatusCode 200 -Reference 'OK' +``` + +the JSON response payload defined is as follows: + +```json +[ + { + "Name": [string], + "UserId": [integer] + } +] +``` + + +### Examples + +To define a reusable example definition you can use the [`Add-PodeOAComponentExample`](../../../Functions/OAComponents/Add-PodeOAComponentExample) function. You'll need to supply a Name, a Summary and a list of value representing the object. + +The following is an example that defines three Pet examples request bodies, and how they're used in a Route's OpenAPI definition: + +```powershell + # defines the frog example +Add-PodeOAComponentExample -name 'frog-example' -Summary "An example of a frog with a cat's name" -Value @{ + name = 'Jaguar'; petType = 'Panthera'; color = 'Lion'; gender = 'Male'; breed = 'Mantella Baroni' +} +# defines the cat example +Add-PodeOAComponentExample -Name 'cat-example' -Summary 'An example of a cat' -Value @{ + name = 'Fluffy'; petType = 'Cat'; color = 'White'; gender = 'male'; breed = 'Persian' +} +# defines the dog example +Add-PodeOAComponentExample -Name 'dog-example' -Summary "An example of a dog with a cat's name" -Value @{ + name = 'Puma'; petType = 'Dog'; color = 'Black'; gender = 'Female'; breed = 'Mixed' +} + +# reuses the examples +Add-PodeRoute -PassThru -Method Put -Path '/pet/:petId' -ScriptBlock { + # route code +} | Set-PodeOARouteInfo -Summary 'Updates a pet in the store with form data' -Tags 'pet' ` + -OperationId 'updatepet' -PassThru | + Set-PodeOARequest -Parameters @( + (New-PodeOAStringProperty -Name 'petId' -Description 'ID of pet that needs to be updated' | ConvertTo-PodeOAParameter -In Path -Required) + ) -RequestBody ( + New-PodeOARequestBody -Description 'user to add to the system' -Content @{ 'application/json' = 'Pet' } -Examples ( + New-PodeOAExample -ContentType 'application/json', 'application/xml' -Reference 'cat-example' | + New-PodeOAExample -ContentType 'application/json', 'application/xml' -Reference 'dog-example' | + New-PodeOAExample -ContentType 'application/json', 'application/xml' -Reference 'frog-example' + ) + ) -PassThru | + Add-PodeOAResponse -StatusCode 200 -Description 'Pet updated.' +``` + +### Headers + +To define a reusable header definition you can use the [`Add-PodeOAComponentHeader`](../../../Functions/OAComponents/Add-PodeOAComponentHeader) function. You'll need to supply a Name, and optionally any Content/Header schemas that define the responses payload. + +```powershell + # define Headers +New-PodeOAIntProperty -Format Int32 -Description 'calls per hour allowed by the user' | + Add-PodeOAComponentHeader -Name 'X-Rate-Limit' +New-PodeOAStringProperty -Format Date-Time -Description 'date in UTC when token expires' | + Add-PodeOAComponentHeader -Name 'X-Expires-After' + +Add-PodeRoute -PassThru -Method Get -Path '/user/login' -ScriptBlock { + # route code +} | Set-PodeOARouteInfo -Summary 'Logs user into the system.' -Description 'Logs user into the system.' ` + -Tags 'user' -OperationId 'loginUser' -PassThru | + Add-PodeOAResponse -StatusCode 200 -Description 'Successful operation' ` + -Header @('X-Rate-Limit', 'X-Expires-After') -Content ( + New-PodeOAContentMediaType -ContentType 'application/json', 'application/xml' -Content 'string' + ) -PassThru | + Add-PodeOAResponse -StatusCode 400 -Description 'Invalid username/password supplied' +``` + + +### CallBacks + +To define a reusable callback definition you can use the [`Add-PodeOAComponentCallBack`](../../../Functions/OAComponents/Add-PodeOAComponentCallBack) function. You'll need to supply a Name, and optionally any Content/Header schemas that define the responses payload. + +```powershell +Add-PodeRoute -PassThru -Method Post -Path '/petcallbackReference' -Authentication 'Login-OAuth2' ` + -Scope 'write' -ScriptBlock { + #route code +} | Set-PodeOARouteInfo -Summary 'Add a new pet to the store' -Description 'Add a new pet to the store' ` + -Tags 'pet' -OperationId 'petcallbackReference' -PassThru | + Set-PodeOARequest -RequestBody ( New-PodeOARequestBody -Reference 'PetBodySchema' ) -PassThru | + Add-PodeOAResponse -StatusCode 200 -Description 'Successful operation' -Content ( + New-PodeOAContentMediaType -ContentType 'application/json', 'application/xml' -Content 'Pet' + ) -PassThru | + Add-PodeOAResponse -StatusCode 405 -Description 'Validation exception' -Content @{ + 'application / json' = ( New-PodeOAStringProperty -Name 'result' | + New-PodeOAStringProperty -Name 'message' | + New-PodeOAObjectProperty ) + } -PassThru | + Add-PodeOACallBack -Name 'test1' -Reference 'test' +``` + +### Response Links + +To define a reusable response link definition you can use the [`Add-PodeOAComponentResponseLink`](../../../Functions/OAComponents/Add-PodeOAComponentResponseLink) function. You'll need to supply a Name, and optionally any Content/Header schemas that define the responses payload. + +```powershell +#Add link reference +Add-PodeOAComponentResponseLink -Name 'address' -OperationId 'getUserByName' -Parameters @{ + 'username' = '$request.path.username' +} + +#use link reference +Add-PodeRoute -PassThru -Method Put -Path '/userLinkByRef/:username' -ScriptBlock { + Write-PodeJsonResponse -Value 'done' -StatusCode 200 +} | Set-PodeOARouteInfo -Summary 'Update user' -Description 'This can only be done by the logged in user.' ` + -Tags 'user' -OperationId 'updateUserLinkByRef' -PassThru | + Set-PodeOARequest -Parameters ( + ( New-PodeOAStringProperty -Name 'username' -Description ' name that need to be updated.' -Required | ConvertTo-PodeOAParameter -In Path ) + ) -RequestBody ( + New-PodeOARequestBody -Required -Content ( + New-PodeOAContentMediaType -ContentType 'application/json', 'application/xml', 'application/x-www-form-urlencoded' -Content 'User' ) + ) -PassThru | + Add-PodeOAResponse -StatusCode 200 -Content @{'application/json' = 'User' } -PassThru -Links ( + New-PodeOAResponseLink -Name 'address2' -Reference 'address' + ) | + Add-PodeOAResponse -StatusCode 400 -Description 'Invalid username supplied' -PassThru | + Add-PodeOAResponse -StatusCode 404 -Description 'User not found' -PassThru | + Add-PodeOAResponse -StatusCode 405 -Description 'Invalid Input' +``` \ No newline at end of file diff --git a/docs/Tutorials/OpenAPI/4DocumentationTools.md b/docs/Tutorials/OpenAPI/4DocumentationTools.md new file mode 100644 index 000000000..34b6822b7 --- /dev/null +++ b/docs/Tutorials/OpenAPI/4DocumentationTools.md @@ -0,0 +1,41 @@ + +# Documentation Tools + +If you're not using a custom OpenAPI viewer, then you can use one or more of the inbuilt which Pode supports: ones with Pode: + +* Swagger +* ReDoc +* RapiDoc +* StopLight +* Explorer +* RapiPdf + +For each you can customise the Route path to access the page on, but by default Swagger is at `/swagger`, ReDoc is at `/redoc`, etc. If you've written your own custom OpenAPI definition then you can also set a custom Route path to fetch the definition on. + +To enable a viewer you can use the [`Enable-PodeOAViewer`](../../../Functions/OpenApi/Enable-PodeOAViewer) function: + +```powershell +# for swagger at "/docs/swagger" +Enable-PodeOAViewer -Type Swagger -Path '/docs/swagger' -DarkMode + +# and ReDoc at the default "/redoc" +Enable-PodeOAViewer -Type ReDoc -Path '/docs/redoc' + +# and RapiDoc at "/docs/rapidoc" +Enable-PodeOAViewer -Type RapiDoc -Path '/docs/rapidoc' + +# and StopLight at "/docs/stoplight" +Enable-PodeOAViewer -Type StopLight -Path '/docs/stoplight' + +# and Explorer at "/docs/explorer" +Enable-PodeOAViewer -Type Explorer -Path '/docs/explorer' + +# and RapiPdf at "/docs/rapipdf" +Enable-PodeOAViewer -Type RapiPdf -Path '/docs/rapipdf' + +# plus a bookmark page with the link to all documentation +Enable-PodeOAViewer -Bookmarks -Path '/docs' + +# there is also an OpenAPI editor (only for v3.0.x) +Enable-PodeOAViewer -Editor -Path '/docs/swagger-editor' +``` diff --git a/docs/Tutorials/OpenAPI/5ParameterValidation.md b/docs/Tutorials/OpenAPI/5ParameterValidation.md new file mode 100644 index 000000000..e1693997a --- /dev/null +++ b/docs/Tutorials/OpenAPI/5ParameterValidation.md @@ -0,0 +1,50 @@ + +## Parameter Validation + +Is possible to validate any parameter submitted by clients against an OpenAPI schema, ensuring adherence to defined standards. + + +First, schema validation has to be enabled using : + +```powershell +Enable-PodeOpenApi -EnableSchemaValidation #any other parameters needed +``` + +This command activates the OpenAPI feature with schema validation enabled, ensuring strict adherence to specified schemas. + +Next, is possible to validate any route using `Test-PodeOAJsonSchemaCompliance`. +In this example, we'll create a route for updating a pet: + +```powershell +Add-PodeRoute -PassThru -Method Post -Path '/user' -ScriptBlock { + $contentType = Get-PodeHeader -Name 'Content-Type' + $responseMediaType = Get-PodeHeader -Name 'Accept' + switch ($contentType) { + 'application/xml' { + $user = ConvertFrom-PodeXml -node $WebEvent.data | ConvertTo-Json + } + 'application/json' { $user = ConvertTo-Json $WebEvent.data } + 'application/x-www-form-urlencoded' { $user = ConvertTo- Json $WebEvent.data } + default { + Write-PodeHtmlResponse -StatusCode 415 + return + } + } + $Validate = Test-PodeOAJsonSchemaCompliance -Json $user -SchemaReference 'User' + if ($Validate.result) { + $newUser = Add-user -User (convertfrom-json -InputObject $user -AsHashtable) + Save-PodeState -Path $using:PetDataJson + switch ($responseMediaType) { + 'application/xml' { Write-PodeXmlResponse -Value $newUser -StatusCode 200 } + 'application/json' { Write-PodeJsonResponse -Value $newUser -StatusCode 200 } + default { Write-PodeHtmlResponse -StatusCode 415 } + } + } + else { + Write-PodeHtmlResponse -StatusCode 405 -Value ($Validate.message -join ', ') + } +} | Set-PodeOARouteInfo -Summary 'Create user.' -Description 'This can only be done by the logged in user.' -Tags 'user' -OperationId 'createUser' -PassThru | + Set-PodeOARequest -RequestBody (New-PodeOARequestBody -Content (New-PodeOAContentMediaType -ContentType 'application/json', 'application/xml', 'application/x-www-form-urlencoded' -Content 'User' )) -PassThru | + Add-PodeOAResponse -StatusCode 405 -Description 'Invalid Input' -PassThru | + Add-PodeOAResponse -Default -Content (New-PodeOAContentMediaType -ContentType 'application/json', 'application/xml' -Content 'User' ) +``` diff --git a/docs/Tutorials/OpenAPI/6MultipleDefinitions.md b/docs/Tutorials/OpenAPI/6MultipleDefinitions.md new file mode 100644 index 000000000..c3278723f --- /dev/null +++ b/docs/Tutorials/OpenAPI/6MultipleDefinitions.md @@ -0,0 +1,129 @@ + +# Multiple definitions + +It's possible to create multiple OpenAPI definitions inside the same Server instance. This feature could be useful in situations such as: + +* Multiple versions of the OpenAPI specification for different use cases +* The same OpenAPI definition, but one using OpenAPI v3.0.3 and another using v3.1.0 +* Different APIs based on the IP or URL + + +### How to use it +Any Pode function that interacts with OpenAPI has a `-DefinitionTag [string[]]` parameter available. This allows you to specify within which OpenAPI definition(s) the API's definition should be available. + +!!! note + These functions accept a simple string, and not an array + + * Get-PodeOADefinition + * Enable-PodeOpenApi + * Enable-PodeOAViewer + * Add-PodeOAInfo + * Test-PodeOAJsonSchemaCompliance + +A new OpenAPI definition has to be created using the `Enable-PodeOpenApi` function + +```powershell +Enable-PodeOpenApi -Path '/docs/openapi/v3.0' -OpenApiVersion '3.0.3' -DefinitionTag 'v3' +Enable-PodeOpenApi -Path '/docs/openapi/v3.1' -OpenApiVersion '3.1.0' -DefinitionTag 'v3.1' +Enable-PodeOpenApi -Path '/docs/openapi/admin' -OpenApiVersion '3.1.0' -DefinitionTag 'admin' +``` + +There is also [`Select-PodeOADefinition`](../../../Functions/OpenApi/Select-PodeOADefinition), which simplifies the selection of which OpenAPI definition to use as a wrapper around multiple OpenAPI functions, or Route functions. Meaning you don't have to specify `-DefinitionTag` on embedded OpenAPI/Route calls: + +```powershell +Select-PodeOADefinition -Tag 'v3', 'v3.1' -Scriptblock { + Add-PodeRouteGroup -Path '/api/v5' -Routes { + Add-PodeRoute -Method Get -Path '/petbyRef/:petId' -ScriptBlock { + Write-PodeJsonResponse -Value 'done' -StatusCode 2005 + } + } +} + +Select-PodeOADefinition -Tag 'admin' -ScriptBlock { + # your admin definition +} +``` + +The default `Definition Tag` is named "default". This can be changed using the `Server.psd1` file and the `Web.OpenApi.DefaultDefinitionTag` property + +```powershell +@{ + Web=@{ + OpenApi=@{ + DefaultDefinitionTag= 'NewDefault' + } + } +} +``` + +### Renaming a Definition Tag + +A Definition Tag can be renamed at any time using the `Rename-PodeOADefinitionTagName` function. This allows you to update the tag name for an existing OpenAPI definition, ensuring your tags remain organized and meaningful. + +```powershell +Rename-PodeOADefinitionTagName -Tag 'v.3' -NewTag 'v.3.0.3' +``` + +In this example, the tag `'v.3'` is renamed to `'v.3.0.3'`. + +### Renaming the Default Definition Tag + +You can also rename the default `Definition Tag` without specifying the `Tag` parameter. This updates the default tag to the new name provided. + +```powershell +Rename-PodeOADefinitionTagName -NewTag 'NewDefault' +``` + +In this example, the default definition tag is renamed to `'NewDefault'`. + +!!! note + The `Rename-PodeOADefinitionTagName` function cannot be used inside a `Select-PodeOADefinition` `[Scriptblock]`. Attempting to do so will result in an error. + +### OpenAPI example + +A simple OpenAPI definition + +```powershell +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 'apiteam@swagger.io' -DefinitionTag 'v3' + +Add-PodeOAInfo -Title 'Swagger Petstore - OpenAPI 3.1' -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 'apiteam@swagger.io' -DefinitionTag 'v3.1' + +Add-PodeOAServerEndpoint -url '/api/v3' -Description 'default endpoint' -DefinitionTag 'v3', 'v3.1' + +#OpenAPI 3.0 +Enable-PodeOAViewer -Type Swagger -Path '/docs/swagger' -DefinitionTag 'v3' +Enable-PodeOAViewer -Type Bookmarks -Path '/docs' -DefinitionTag 'v3' + +#OpenAPI 3.1 +Enable-PodeOAViewer -Type Swagger -Path '/docs/v3.1/swagger' -DefinitionTag 'v3.1' +Enable-PodeOAViewer -Type ReDoc -Path '/docs/v3.1/redoc' -DarkMode -DefinitionTag 'v3.1' +Enable-PodeOAViewer -Type Bookmarks -Path '/docs/v3.1' -DefinitionTag 'v3.1' + +Select-PodeOADefinition -Tag 'v3', 'v3.1' -ScriptBlock { + New-PodeOAIntProperty -Name 'id'-Format Int64 -Example 10 -Required | + New-PodeOAStringProperty -Name 'name' -Example 'doggie' -Required | + New-PodeOASchemaProperty -Name 'category' -Reference 'Category' | + New-PodeOAStringProperty -Name 'photoUrls' -Array -XmlWrapped -XmlItemName 'photoUrl' -Required | + New-PodeOASchemaProperty -Name 'tags' -Reference 'Tag' -Array -XmlWrapped | + New-PodeOAStringProperty -Name 'status' -Description 'pet status in the store' -Enum @('available', 'pending', 'sold') | + New-PodeOAObjectProperty -XmlName 'pet' | + Add-PodeOAComponentSchema -Name 'Pet' + + + Add-PodeRouteGroup -Path '/api/v3' -Routes { + Add-PodeRoute -PassThru -Method Put -Path '/pet' -Authentication 'merged_auth_nokey' -Scope 'write:pets', 'read:pets' -ScriptBlock { + #code + } | Set-PodeOARouteInfo -Summary 'Update an existing pet' -Description 'Update an existing pet by Id' -Tags 'pet' -OperationId 'updatePet' -PassThru | + Set-PodeOARequest -RequestBody ( + New-PodeOARequestBody -Description 'Update an existent pet in the store' -Required -Content ( + New-PodeOAContentMediaType -ContentType 'application/json', 'application/xml' -Content 'Pet' ) + ) -PassThru | + Add-PodeOAResponse -StatusCode 200 -Description 'Successful operation' -Content (New-PodeOAContentMediaType -ContentType 'application/json', 'application/xml' -Content 'Pet' ) -PassThru | + Add-PodeOAResponse -StatusCode 400 -Description 'Invalid ID supplied' -PassThru | + Add-PodeOAResponse -StatusCode 404 -Description 'Pet not found' -PassThru | + Add-PodeOAResponse -StatusCode 405 -Description 'Validation exception' + } +} +``` diff --git a/docs/Tutorials/OpenAPI/Overview.md b/docs/Tutorials/OpenAPI/Overview.md deleted file mode 100644 index 66098b2e5..000000000 --- a/docs/Tutorials/OpenAPI/Overview.md +++ /dev/null @@ -1,1043 +0,0 @@ -# Overview - -Pode has built-in support for converting your routes into OpenAPI 3.0 definitions. There is also support for enabling simple Swagger and/or ReDoc viewers and others. - -The OpenApi module has been extended with many more functions, and some old ones have been improved. - -For more detailed information regarding OpenAPI and Pode, please refer to [OpenAPI Specification and Pode](../Specification/v3_0_3.md) - -You can enable OpenAPI in Pode, and a straightforward definition will be generated. However, to get a more complex definition with request bodies, parameters, and response payloads, you'll need to use the relevant OpenAPI functions detailed below. - -## Enabling OpenAPI - -To enable support for generating OpenAPI definitions you'll need to use the [`Enable-PodeOpenApi`](../../../Functions/OpenApi/Enable-PodeOpenApi) function. This will allow you to set a title and version for your API. You can also set a default route to retrieve the OpenAPI definition for tools like Swagger or ReDoc, the default is at `/openapi`. - -You can also set a route filter (such as `/api/*`, the default is `/*` for everything), so only those routes are included in the definition. - -An example of enabling OpenAPI is a follows: - -```powershell -Enable-PodeOpenApi -Title 'My Awesome API' -Version 9.0.0.1 -``` - -An example of setting the OpenAPI route is a follows. This will create a route accessible at `/docs/openapi`: - -```powershell -Enable-PodeOpenApi -Path '/docs/openapi' -Title 'My Awesome API' -Version 9.0.0.1 -``` - -### Default Setup - -In the very simplest of scenarios, just enabling OpenAPI will generate a minimal definition. It can be viewed in Swagger/ReDoc etc, but won't be usable for trying calls. - -When you enable OpenAPI, and don't set any other OpenAPI data, the following is the minimal data that is included: - -* Every route will have a 200 and Default response -* Although routes will be included, no request bodies, parameters or response payloads will be defined -* If you have multiple endpoints, then the servers section will be included -* Any authentication will be included - -This can be changed with [`Enable-PodeOpenApi`](../../../Functions/OpenApi/Enable-PodeOpenApi) - -For example to change the default response 404 and 500 - -```powershell -Enable-PodeOpenApi -Path '/docs/openapi' -OpenApiVersion '3.0.3' -DefaultResponses ( - New-PodeOAResponse -StatusCode 404 -Description 'User not found' | Add-PodeOAResponse -StatusCode 500 - ) -``` - -For disabling the Default Response use: - -```powershell -Enable-PodeOpenApi -Path '/docs/openapi' -OpenApiVersion '3.0.3' -NoDefaultResponses -``` - -For disabling the Minimal Definitions feature use: - -```powershell -Enable-PodeOpenApi -Path '/docs/openapi' -OpenApiVersion '3.0.3' -DisableMinimalDefinitions -``` - -### Get Definition - -Instead of defining a route to return the definition, you can write the definition to the response whenever you want, and in any route, using the [`Get-PodeOADefinition`](../../../Functions/OpenApi/Get-PodeOADefinition) function. This could be useful in certain scenarios like in Azure Functions, where you can enable OpenAPI, and then write the definition to the response of a GET request if some query parameter is set; eg: `?openapi=1`. - -For example: - -```powershell -Add-PodeRoute -Method Get -Path '/' -ScriptBlock { - if ($WebEvent.Query.openapi -eq 1) { - Get-PodeOpenApiDefinition | Write-PodeJsonResponse - } -} -``` - -## OpenAPI Info object - -In previous releases some of the Info object properties like Version and Title were defined by [`Enable-PodeOpenApi`](../../../Functions/OpenApi/Enable-PodeOpenApi). -Starting from version 2.10 a new [`Add-PodeOAInfo`](../../../Functions/OpenApi/Add-PodeOAInfo) function has been added to create a full OpenAPI Info spec. - -```powershell -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 'apiteam@swagger.io' -``` - -## OpenAPI configuration Best Practice - -Pode is rich of functions to create and configure an complete OpenApi spec. Here is a typical code you should use to initiate an OpenApi spec - -```powershell -#Initialize OpenApi -Enable-PodeOpenApi -Path '/docs/openapi' -Title 'Swagger Petstore - OpenAPI 3.0' ` - -OpenApiVersion 3.1 -DisableMinimalDefinitions -NoDefaultResponses - -# OpenApi Info -Add-PodeOAInfo -Title 'Swagger Petstore - OpenAPI 3.0' ` - -Version 1.0.17 ` - -Description 'This is a sample Pet Store Server based on the OpenAPI 3.0 specification. ...' ` - -TermsOfService 'http://swagger.io/terms/' ` - -LicenseName 'Apache 2.0' ` - -LicenseUrl 'http://www.apache.org/licenses/LICENSE-2.0.html' ` - -ContactName 'API Support' ` - -ContactEmail 'apiteam@swagger.io' ` - -ContactUrl 'http://example.com/support' - -# Endpoint for the API - Add-PodeOAServerEndpoint -url '/api/v3.1' -Description 'default endpoint' - - # OpenApi external documentation links - $extDoc = New-PodeOAExternalDoc -Name 'SwaggerDocs' -Description 'Find out more about Swagger' -Url 'http://swagger.io' - $extDoc | Add-PodeOAExternalDoc - - # OpenApi documentation viewer - Enable-PodeOAViewer -Type Swagger -Path '/docs/swagger' - Enable-PodeOAViewer -Type ReDoc -Path '/docs/redoc' - Enable-PodeOAViewer -Type RapiDoc -Path '/docs/rapidoc' - Enable-PodeOAViewer -Type StopLight -Path '/docs/stoplight' - Enable-PodeOAViewer -Type Explorer -Path '/docs/explorer' - Enable-PodeOAViewer -Bookmarks -Path '/docs' -``` - -## Authentication - -Any authentication defined, either by [`Add-PodeAuthMiddleware`](../../../Functions/Authentication/Add-PodeAuthMiddleware), or using the `-Authentication` parameter on Routes, will be automatically added to the `security` section of the OpenAPI definition. - - -## Tags - -In OpenAPI, a "tag" is used to group related operations. Tags are often used to organize and categorize endpoints in an API specification, making it easier to understand and navigate the API documentation. Each tag can be associated with one or more API operations, and these tags are then used in tools like Swagger UI to group and display operations in a more organized way. - -Here's an example of how to define and use tags: - -```powershell -# create an External Doc reference -$swaggerDocs = New-PodeOAExternalDoc -Description 'Find out more about Swagger' -Url 'http://swagger.io' - -# create a Tag -Add-PodeOATag -Name 'pet' -Description 'Everything about your Pets' -ExternalDoc $swaggerDocs - -Add-PodeRoute -PassThru -Method get -Path '/pet/findByStatus' -Authentication 'Login-OAuth2' -Scope 'read' -AllowAnon -ScriptBlock { - #route code -} | Set-PodeOARouteInfo -Summary 'Finds Pets by status' -Description 'Multiple status values can be provided with comma-separated strings' ` - -Tags 'pet' -OperationId 'findPetsByStatus' -``` - -## Routes - -To extend the definition of a route, you can use the `-PassThru` switch on the [`Add-PodeRoute`](../../../Functions/Routes/Add-PodeRoute) function. This will cause the route that was created to be returned, so you can pass it down the pipe into more OpenAPI functions. - -To add metadata to a route's definition you can use the [`Set-PodeOARouteInfo`](../../../Functions/OpenApi/Set-PodeOARouteInfo) function. This will allow you to define a summary/description for the route, as well as tags for grouping: - -```powershell -Add-PodeRoute -Method Get -Path "/api/resources" -ScriptBlock { - Set-PodeResponseStatus -Code 200 -} -PassThru | - Set-PodeOARouteInfo -Summary 'Retrieve some resources' -Tags 'Resources' -``` - -Each of the following OpenAPI functions have a `-PassThru` switch, allowing you to chain many of them together. - -### Responses - -You can define multiple responses for a route, but only one of each status code, using the [`Add-PodeOAResponse`](../../../Functions/OpenApi/Add-PodeOAResponse) function. You can either just define the response and status code, with a custom description, or with a schema defining the payload of the response. - -The following is an example of defining simple 200 and 404 responses on a route: - -```powershell -Add-PodeRoute -Method Get -Path "/api/user/:userId" -ScriptBlock { - # logic -} -PassThru | - Add-PodeOAResponse -StatusCode 200 -PassThru | - Add-PodeOAResponse -StatusCode 404 -Description 'User not found' -``` - -Whereas the following is a more complex definition, which also defines the responses JSON payload. This payload is defined as an object with a string Name, and integer UserId: - -```powershell -Add-PodeRoute -Method Get -Path '/api/users/:userId' -ScriptBlock { - Write-PodeJsonResponse -Value @{ - Name = 'Rick' - UserId = $WebEvent.Parameters['userId'] - } -} -PassThru | - Add-PodeOAResponse -StatusCode 200 -Description 'A user object' --Content @{ - 'application/json' = (New-PodeOAStringProperty -Name 'Name'| - New-PodeOAIntProperty -Name 'UserId'| New-PodeOAObjectProperty) - } -``` - -the JSON response payload defined is as follows: - -```json -{ - "Name": [string], - "UserId": [integer] -} -``` - -In case the response JSON payload is an array - -```powershell -Add-PodeRoute -Method Get -Path '/api/users/:userId' -ScriptBlock { - Write-PodeJsonResponse -Value @{ - Name = 'Rick' - UserId = $WebEvent.Parameters['userId'] - } - } -PassThru | - Add-PodeOAResponse -StatusCode 200 -Description 'A user object' -Content ( - New-PodeOAContentMediaType -ContentMediaType 'application/json' -Array -Content ( - New-PodeOAStringProperty -Name 'Name' | - New-PodeOAIntProperty -Name 'UserId' | - New-PodeOAObjectProperty - ) - ) -``` - -```json -[ - { - "Name": [string], - "UserId": [integer] - } -] -``` - -Internally, each route is created with an empty default 200 and 500 response. You can remove these, or other added responses, by using [`Remove-PodeOAResponse`](../../../Functions/OpenApi/Remove-PodeOAResponse): - -```powershell -Add-PodeRoute -Method Get -Path "/api/user/:userId" -ScriptBlock { - # route logic -} -PassThru | - Remove-PodeOAResponse -StatusCode 200 -``` - -### Requests - -#### Parameters - -You can set route parameter definitions, such as parameters passed in the path/query, by using the [`Set-PodeOARequest`](../../../Functions/OpenApi/Set-PodeOARequest) function with the `-Parameters` parameter. The parameter takes an array of properties converted into parameters, using the [`ConvertTo-PodeOAParameter`](../../../Functions/OpenApi/ConvertTo-PodeOAParameter) function. - -For example, to create some integer `userId` parameter that is supplied in the path of the request, the following will work: - -```powershell -Add-PodeRoute -Method Get -Path '/api/users/:userId' -ScriptBlock { - Write-PodeJsonResponse -Value @{ - Name = 'Rick' - UserId = $WebEvent.Parameters['userId'] - } -} -PassThru | - Set-PodeOARequest -Parameters @( - (New-PodeOAIntProperty -Name 'userId' -Required | ConvertTo-PodeOAParameter -In Path) - ) -``` - -Whereas you could use the next example to define 2 query parameters, both strings: - -```powershell -Add-PodeRoute -Method Get -Path '/api/users' -ScriptBlock { - Write-PodeJsonResponse -Value @{ - Name = 'Rick' - UserId = $WebEvent.Query['name'] - } -} -PassThru | - Set-PodeOARequest -Parameters ( - (New-PodeOAStringProperty -Name 'name' -Required | ConvertTo-PodeOAParameter -In Query), - (New-PodeOAStringProperty -Name 'city' -Required | ConvertTo-PodeOAParameter -In Query) - ) -``` - -#### Payload - -You can set request payload schemas by using the [`Set-PodeOARequest`](../../../Functions/OpenApi/Set-PodeOARequest)function, with the `-RequestBody` parameter. The request body can be defined using the [`New-PodeOARequestBody`](../../../Functions/OpenApi/New-PodeOARequestBody) function, and supplying schema definitions for content types - this works in very much a similar way to defining responses above. - -For example, to define a request JSON payload of some `userId` and `name` you could use the following: - -```powershell -Add-PodeRoute -Method Patch -Path '/api/users' -ScriptBlock { - Write-PodeJsonResponse -Value @{ - Name = $WebEvent.Data.name - UserId = $WebEvent.Data.userId - } -} -PassThru | - Set-PodeOARequest -RequestBody ( - New-PodeOARequestBody -Required -Content ( - New-PodeOAContentMediaType -ContentMediaType 'application/json','application/xml' -Content ( New-PodeOAStringProperty -Name 'Name'| New-PodeOAIntProperty -Name 'UserId'| New-PodeOAObjectProperty ) ) - - ) -``` - -The expected payload would look as follows: - -```json -{ - "name": [string], - "userId": [integer] -} -``` - -```xml - - -``` - -## Components - -You can define reusable OpenAPI components in Pode. Currently supported are Schemas, Parameters, Request Bodies, and Responses. - -### Schemas - -To define a reusable schema that can be used in request bodies, and responses, you can use the [`Add-PodeOAComponentSchema`](../../../Functions/OAComponents/Add-PodeOAComponentSchema) function. You'll need to supply a Name, and a Schema that can be reused. - -The following is an example of defining a schema which is a object of Name, UserId, and Age: - -```powershell -# define a reusable schema user object -New-PodeOAStringProperty -Name 'Name' | - New-PodeOAIntProperty -Name 'UserId' | - New-PodeOAIntProperty -Name 'Age' | - New-PodeOAObjectProperty | - Add-PodeOAComponentSchema -Name 'UserSchema' - -# reuse the above schema in a response -Add-PodeRoute -Method Get -Path '/api/users/:userId' -ScriptBlock { - Write-PodeJsonResponse -Value @{ - Name = 'Rick' - UserId = $WebEvent.Parameters['userId'] - Age = 42 - } -} -PassThru | - Add-PodeOAResponse -StatusCode 200 -Description 'A list of users' -Content @{ - 'application/json' = 'UserSchema' - } -``` - -### Request Bodies - -To define a reusable request bodies you can use the [`Add-PodeOAComponentRequestBody`](../../../Functions/OAComponents/Add-PodeOAComponentRequestBody) function. You'll need to supply a Name, as well as the needed schemas for each content type. - -The following is an example of defining a JSON object that a Name, UserId, and an Enable flag: - -```powershell -# define a reusable request body -New-PodeOAContentMediaType -ContentMediaType 'application/json', 'application/x-www-form-urlencoded' -Content ( - New-PodeOAStringProperty -Name 'Name' | - New-PodeOAIntProperty -Name 'UserId' | - New-PodeOABoolProperty -Name 'Enabled' | - New-PodeOAObjectProperty - ) | Add-PodeOAComponentRequestBody -Name 'UserBody' -Required - -# use the request body in a route -Add-PodeRoute -Method Patch -Path '/api/users' -ScriptBlock { - Set-PodeResponseStatus -StatusCode 200 -} -PassThru | - Set-PodeOARequest -RequestBody (New-PodeOARequestBody -Reference 'UserBody') -``` - -The JSON payload expected is of the format: - -```json -{ - "Name": [string], - "UserId": [integer], - "Enabled": [boolean] -} -``` - -### Parameters - -To define reusable parameters that are used on requests, you can use the [`Add-PodeOAComponentParameter`](../../../Functions/OAComponents/Add-PodeOAComponentParameter) function. You'll need to supply a Name and the Parameter definition. - -The following is an example of defining an integer path parameter for a `userId`, and then using that parameter on a route. - -```powershell -# define a reusable {userid} path parameter -New-PodeOAIntProperty -Name 'userId' -Required | ConvertTo-PodeOAParameter -In Path |Add-PodeOAComponentParameter -Name 'UserId' - -# use this parameter in a route -Add-PodeRoute -Method Get -Path '/api/users/:userId' -ScriptBlock { - Write-PodeJsonResponse -Value @{ - Name = 'Rick' - UserId = $WebEvent.Parameters['userId'] - } -} -PassThru | - Set-PodeOARequest -Parameters @(ConvertTo-PodeOAParameter -Reference 'UserId') -``` - -### Responses - -To define a reusable response definition you can use the [`Add-PodeOAComponentResponse`](../../../Functions/OAComponents/Add-PodeOAComponentResponse) function. You'll need to supply a Name, and optionally any Content/Header schemas that define the responses payload. - -The following is an example of defining a 200 response with a JSON payload of an array of objects for Name/UserId. The Response component can be used by a route referencing the name: - -```powershell -# defines a response with a json payload using New-PodeOAContentMediaType -Add-PodeOAComponentResponse -Name 'OK' -Description 'A user object' -Content ( - New-PodeOAContentMediaType -MediaType 'application/json' -Array -Content ( - New-PodeOAStringProperty -Name 'Name' | - New-PodeOAIntProperty -Name 'UserId' | - New-PodeOAObjectProperty - ) - ) - -# reuses the above response on a route using its "OK" name -Add-PodeRoute -Method Get -Path "/api/users" -ScriptBlock { - Write-PodeJsonResponse -Value @( - @{ Name = 'Rick'; UserId = 123 }, - @{ Name = 'Geralt'; UserId = 124 } - ) -} -PassThru | - Add-PodeOAResponse -StatusCode 200 -Reference 'OK' -``` - -the JSON response payload defined is as follows: - -```json -[ - { - "Name": [string], - "UserId": [integer] - } -] -``` - - -### Examples - -To define a reusable example definition you can use the [`Add-PodeOAComponentExample`](../../../Functions/OAComponents/Add-PodeOAComponentExample) function. You'll need to supply a Name, a Summary and a list of value representing the object. - -The following is an example that defines three Pet examples request bodies, and how they're used in a Route's OpenAPI definition: - -```powershell - # defines the frog example -Add-PodeOAComponentExample -name 'frog-example' -Summary "An example of a frog with a cat's name" -Value @{ - name = 'Jaguar'; petType = 'Panthera'; color = 'Lion'; gender = 'Male'; breed = 'Mantella Baroni' -} -# defines the cat example -Add-PodeOAComponentExample -Name 'cat-example' -Summary 'An example of a cat' -Value @{ - name = 'Fluffy'; petType = 'Cat'; color = 'White'; gender = 'male'; breed = 'Persian' -} -# defines the dog example -Add-PodeOAComponentExample -Name 'dog-example' -Summary "An example of a dog with a cat's name" -Value @{ - name = 'Puma'; petType = 'Dog'; color = 'Black'; gender = 'Female'; breed = 'Mixed' -} - -# reuses the examples -Add-PodeRoute -PassThru -Method Put -Path '/pet/:petId' -ScriptBlock { - # route code -} | Set-PodeOARouteInfo -Summary 'Updates a pet in the store with form data' -Tags 'pet' ` - -OperationId 'updatepet' -PassThru | - Set-PodeOARequest -Parameters @( - (New-PodeOAStringProperty -Name 'petId' -Description 'ID of pet that needs to be updated' | ConvertTo-PodeOAParameter -In Path -Required) - ) -RequestBody ( - New-PodeOARequestBody -Description 'user to add to the system' -Content @{ 'application/json' = 'Pet' } -Examples ( - New-PodeOAExample -ContentMediaType 'application/json', 'application/xml' -Reference 'cat-example' | - New-PodeOAExample -ContentMediaType 'application/json', 'application/xml' -Reference 'dog-example' | - New-PodeOAExample -ContentMediaType 'application/json', 'application/xml' -Reference 'frog-example' - ) - ) -PassThru | - Add-PodeOAResponse -StatusCode 200 -Description 'Pet updated.' -``` - -### Headers - -To define a reusable header definition you can use the [`Add-PodeOAComponentHeader`](../../../Functions/OAComponents/Add-PodeOAComponentHeader) function. You'll need to supply a Name, and optionally any Content/Header schemas that define the responses payload. - -```powershell - # define Headers -New-PodeOAIntProperty -Format Int32 -Description 'calls per hour allowed by the user' | - Add-PodeOAComponentHeader -Name 'X-Rate-Limit' -New-PodeOAStringProperty -Format Date-Time -Description 'date in UTC when token expires' | - Add-PodeOAComponentHeader -Name 'X-Expires-After' - -Add-PodeRoute -PassThru -Method Get -Path '/user/login' -ScriptBlock { - # route code -} | Set-PodeOARouteInfo -Summary 'Logs user into the system.' -Description 'Logs user into the system.' ` - -Tags 'user' -OperationId 'loginUser' -PassThru | - Add-PodeOAResponse -StatusCode 200 -Description 'Successful operation' ` - -Header @('X-Rate-Limit', 'X-Expires-After') -Content ( - New-PodeOAContentMediaType -MediaType 'application/json', 'application/xml' -Content 'string' - ) -PassThru | - Add-PodeOAResponse -StatusCode 400 -Description 'Invalid username/password supplied' -``` - - -### CallBacks - -To define a reusable callback definition you can use the [`Add-PodeOAComponentCallBack`](../../../Functions/OAComponents/Add-PodeOAComponentCallBack) function. You'll need to supply a Name, and optionally any Content/Header schemas that define the responses payload. - -```powershell -Add-PodeRoute -PassThru -Method Post -Path '/petcallbackReference' -Authentication 'Login-OAuth2' ` - -Scope 'write' -ScriptBlock { - #route code -} | Set-PodeOARouteInfo -Summary 'Add a new pet to the store' -Description 'Add a new pet to the store' ` - -Tags 'pet' -OperationId 'petcallbackReference' -PassThru | - Set-PodeOARequest -RequestBody ( New-PodeOARequestBody -Reference 'PetBodySchema' ) -PassThru | - Add-PodeOAResponse -StatusCode 200 -Description 'Successful operation' -Content ( - New-PodeOAContentMediaType -MediaType 'application/json', 'application/xml' -Content 'Pet' - ) -PassThru | - Add-PodeOAResponse -StatusCode 405 -Description 'Validation exception' -Content @{ - 'application / json' = ( New-PodeOAStringProperty -Name 'result' | - New-PodeOAStringProperty -Name 'message' | - New-PodeOAObjectProperty ) - } -PassThru | - Add-PodeOACallBack -Name 'test1' -Reference 'test' -``` - -### Response Links - -To define a reusable response link definition you can use the [`Add-PodeOAComponentResponseLink`](../../../Functions/OAComponents/Add-PodeOAComponentResponseLink) function. You'll need to supply a Name, and optionally any Content/Header schemas that define the responses payload. - -```powershell -#Add link reference -Add-PodeOAComponentResponseLink -Name 'address' -OperationId 'getUserByName' -Parameters @{ - 'username' = '$request.path.username' -} - -#use link reference -Add-PodeRoute -PassThru -Method Put -Path '/userLinkByRef/:username' -ScriptBlock { - Write-PodeJsonResponse -Value 'done' -StatusCode 200 -} | Set-PodeOARouteInfo -Summary 'Update user' -Description 'This can only be done by the logged in user.' ` - -Tags 'user' -OperationId 'updateUserLinkByRef' -PassThru | - Set-PodeOARequest -Parameters ( - ( New-PodeOAStringProperty -Name 'username' -Description ' name that need to be updated.' -Required | ConvertTo-PodeOAParameter -In Path ) - ) -RequestBody ( - New-PodeOARequestBody -Required -Content ( - New-PodeOAContentMediaType -MediaType 'application/json', 'application/xml', 'application/x-www-form-urlencoded' -Content 'User' ) - ) -PassThru | - Add-PodeOAResponse -StatusCode 200 -Content @{'application/json' = 'User' } -PassThru -Links ( - New-PodeOAResponseLink -Name 'address2' -Reference 'address' - ) | - Add-PodeOAResponse -StatusCode 400 -Description 'Invalid username supplied' -PassThru | - Add-PodeOAResponse -StatusCode 404 -Description 'User not found' -PassThru | - Add-PodeOAResponse -StatusCode 405 -Description 'Invalid Input' -``` - -## Properties - -Properties are used to create all Parameters and Schemas in OpenAPI. You can use the simple types on their own, or you can combine multiple of them together to form complex objects. - -### Simple Types - -There are 5 simple property types: Integers, Numbers, Strings, Booleans, and Schemas. Each of which can be created using the following functions: - -* [`New-PodeOAIntProperty`](../../../Functions/OAProperties/New-PodeOAIntProperty) -* [`New-PodeOANumberProperty`](../../../Functions/OAProperties/New-PodeOANumberProperty) -* [`New-PodeOAStringProperty`](../../../Functions/OAProperties/New-PodeOAStringProperty) -* [`New-PodeOABoolProperty`](../../../Functions/OAProperties/New-PodeOABoolProperty) -* [`New-PodeOASchemaProperty`](../../../Functions//New-PodeOASchemaProperty) -* [`New-PodeOAMultiTypeProperty`](../../../Functions/OAProperties/New-PodeOAMultiTypeProperty) (Note: OpenAPI 3.1 only) - -These properties can be created with a Name, and other flags such as Required and/or a Description: - -```powershell -# simple integer -New-PodeOAIntProperty -Name 'userId' - -# a float number with a max value of 100 -New-PodeOANumberProperty -Name 'ratio' -Format Float -Maximum 100 - -# a string with a default value, and enum of options -New-PodeOAStringProperty -Name 'type' -Default 'admin' -Enum @('admin', 'user') - -# a boolean that's required -New-PodeOABoolProperty -Name 'enabled' -Required - -# a schema property that references another component schema -New-PodeOASchemaProperty -Name 'Config' -Reference 'ConfigSchema' - -# a string or an integer or a null value (only available with OpenAPI 3.1) -New-PodeOAMultiTypeProperty -Name 'multi' -Type integer,string -Nullable -``` - -On their own, like above, the simple properties don't really do much. However, you can combine that together to make complex objects/arrays as defined below. - -### Arrays - -There isn't a dedicated function to create an array property, instead there is an `-Array` switch on each of the property functions - both Object and the above simple properties. - -If you supply the `-Array` switch to any of the above simple properties, this will define an array of that type - the `-Name` parameter can also be omitted if only a simple array if required. - -For example, the below will define an integer array: - -```powershell -New-PodeOAIntProperty -Array -``` - -When used in a Response, this could return the following JSON example: - -```json -[ - 0, - 1, - 2 -] -``` - -### Objects - -An object property is a combination of multiple other properties - both simple, array of more objects. - -There are two ways to define objects: - -1. Similar to arrays, you can use the `-Object` switch on the simple properties. -2. You can use the [`New-PodeOAObjectProperty`](../../../Functions/OAProperties/New-PodeOAObjectProperty) function to combine multiple properties. - -#### Simple - -If you use the `-Object` switch on the simple property function, this will automatically wrap the property as an object. The Name for this is required. - -For example, the below will define a simple `userId` integer object: - -```powershell -New-PodeOAIntProperty -Name 'userId' -Object -``` - -In a response as JSON, this could look as follows: - -```json -{ - "userId": 0 -} -``` - -Furthermore, you can also supply both `-Array` and `-Object` switches: - -```powershell -New-PodeOAIntProperty -Name 'userId' -Object -Array -``` - -This wil result in something like the following JSON: - -```json -{ - "userId": [ 0, 1, 2 ] -} -``` - -#### Complex - -Unlike the `-Object` switch that simply converts a single property into an object, the [`New-PodeOAObjectProperty`](../../../Functions/OAProperties/New-PodeOAObjectProperty) function can combine and convert multiple properties. - -For example, the following will create an object using an Integer, String, and a Boolean: - -Legacy Definition - -```powershell -New-PodeOAObjectProperty -Properties ( - (New-PodeOAIntProperty -Name 'userId'), - (New-PodeOAStringProperty -Name 'name'), - (New-PodeOABoolProperty -Name 'enabled') -) -``` - -Using piping (new in Pode 2.10) - -```powershell -New-PodeOAIntProperty -Name 'userId'| New-PodeOAStringProperty -Name 'name'| - New-PodeOABoolProperty -Name 'enabled' |New-PodeOAObjectProperty -``` - -As JSON, this could look as follows: - -```json -{ - "userId": 0, - "name": "Gary Goodspeed", - "enabled": true -} -``` - -You can also supply the `-Array` switch to the [`New-PodeOAObjectProperty`](../../../Functions/OAProperties/New-PodeOAObjectProperty) function. This will result in an array of objects. For example, if we took the above: - -```powershell -New-PodeOAIntProperty -Name 'userId'| New-PodeOAStringProperty -Name 'name'| - New-PodeOABoolProperty -Name 'enabled' |New-PodeOAObjectProperty -Array -``` - -As JSON, this could look as follows: - -```json -[ - { - "userId": 0, - "name": "Gary Goodspeed", - "enabled": true - }, - { - "userId": 1, - "name": "Kevin", - "enabled": false - } -] -``` - -You can also combine objects into other objects: - -```powershell -$usersArray = New-PodeOAIntProperty -Name 'userId'| New-PodeOAStringProperty -Name 'name'| - New-PodeOABoolProperty -Name 'enabled' |New-PodeOAObjectProperty -Array - -New-PodeOAObjectProperty -Properties @( - (New-PodeOAIntProperty -Name 'found'), - $usersArray -) -``` - -As JSON, this could look as follows: - -```json -{ - "found": 2, - "users": [ - { - "userId": 0, - "name": "Gary Goodspeed", - "enabled": true - }, - { - "userId": 1, - "name": "Kevin", - "enabled": false - } - ] -} -``` - -### oneOf, anyOf and allOf Keywords - -OpenAPI 3.x provides several keywords which you can use to combine schemas. You can use these keywords to create a complex schema or validate a value against multiple criteria. - -* oneOf - validates the value against exactly one of the sub-schemas -* allOf - validates the value against all the sub-schemas -* anyOf - validates the value against any (one or more) of the sub-schemas - -You can use the [`Merge-PodeOAProperty`](../../../Functions/OAProperties/Merge-PodeOAProperty) will instead define a relationship between the properties. - -Unlike [`New-PodeOAObjectProperty`](../../../Functions/OAProperties/New-PodeOAObjectProperty) which combines and converts multiple properties into an Object, [`Merge-PodeOAProperty`](../../../Functions/OAProperties/Merge-PodeOAProperty) will instead define a relationship between the properties. - -For example, the following will create an something like an C Union object using an Integer, String, and a Boolean: - -```powershell -Merge-PodeOAProperty -Type OneOf -ObjectDefinitions @( - (New-PodeOAIntProperty -Name 'userId' -Object), - (New-PodeOAStringProperty -Name 'name' -Object), - (New-PodeOABoolProperty -Name 'enabled' -Object) - ) -``` - -Or - -```powershell -New-PodeOAIntProperty -Name 'userId' -Object | - New-PodeOAStringProperty -Name 'name' -Object | - New-PodeOABoolProperty -Name 'enabled' -Object | - Merge-PodeOAProperty -Type OneOf -``` - -As JSON, this could look as follows: - -```json -{ - "oneOf": [ - { - "type": "object", - "properties": { - "userId": { - "type": "integer" - } - } - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - } - } - }, - { - "type": "object", - "properties": { - "enabled": { - "type": "boolean", - "default": false - } - } - } - ] -} -``` - -You can also supply a Component Schema created using [`Add-PodeOAComponentSchema`](../../../Functions/OAComponents/Add-PodeOAComponentSchema). For example, if we took the above: - -```powershell - New-PodeOAIntProperty -Name 'id'-Format Int64 -Example 1 -ReadOnly | - New-PodeOAStringProperty -Name 'username' -Example 'theUser' -Required | - New-PodeOAStringProperty -Name 'firstName' -Example 'John' | - New-PodeOAStringProperty -Name 'lastName' -Example 'James' | - New-PodeOAStringProperty -Name 'email' -Format email -Example 'john@email.com' | - New-PodeOAStringProperty -Name 'lastName' -Example 'James' | - New-PodeOAStringProperty -Name 'password' -Format Password -Example '12345' -Required | - New-PodeOAStringProperty -Name 'phone' -Example '12345' | - New-PodeOAIntProperty -Name 'userStatus'-Format int32 -Description 'User Status' -Example 1| - New-PodeOAObjectProperty -Name 'User' -XmlName 'user' | - Add-PodeOAComponentSchema - - New-PodeOAStringProperty -Name 'street' -Example '437 Lytton' -Required | - New-PodeOAStringProperty -Name 'city' -Example 'Palo Alto' -Required | - New-PodeOAStringProperty -Name 'state' -Example 'CA' -Required | - New-PodeOAStringProperty -Name 'zip' -Example '94031' -Required | - New-PodeOAObjectProperty -Name 'Address' -XmlName 'address' -Description 'Shipping Address' | - Add-PodeOAComponentSchema - - Merge-PodeOAProperty -Type AllOf -ObjectDefinitions 'Address','User' - -``` - -As JSON, this could look as follows: - -```json -{ - "allOf": [ - { - "$ref": "#/components/schemas/Address" - }, - { - "$ref": "#/components/schemas/User" - } - ] -} -``` -## Implementing Parameter Validation - -Is possible to validate any parameter submitted by clients against an OpenAPI schema, ensuring adherence to defined standards. - - -First, schema validation has to be enabled using : - -```powershell -Enable-PodeOpenApi -EnableSchemaValidation #any other parameters needed -``` - -This command activates the OpenAPI feature with schema validation enabled, ensuring strict adherence to specified schemas. - -Next, is possible to validate any route using `PodeOAJsonSchemaCompliance`. -In this example, we'll create a route for updating a pet: - -```powershell -Add-PodeRoute -PassThru -Method Post -Path '/user' -ScriptBlock { - $contentType = Get-PodeHeader -Name 'Content-Type' - $responseMediaType = Get-PodeHeader -Name 'Accept' - switch ($contentType) { - 'application/xml' { - $user = ConvertFrom-PodeXml -node $WebEvent.data | ConvertTo-Json - } - 'application/json' { $user = ConvertTo-Json $WebEvent.data } - 'application/x-www-form-urlencoded' { $user = ConvertTo-Json $WebEvent.data } - default { - Write-PodeHtmlResponse -StatusCode 415 - return - } - } - $Validate = Test-PodeOAJsonSchemaCompliance -Json $user -SchemaReference 'User' - if ($Validate.result) { - $newUser = Add-user -User (convertfrom-json -InputObject $user -AsHashtable) - Save-PodeState -Path $using:PetDataJson - switch ($responseMediaType) { - 'application/xml' { Write-PodeXmlResponse -Value $newUser -StatusCode 200 } - 'application/json' { Write-PodeJsonResponse -Value $newUser -StatusCode 200 } - default { Write-PodeHtmlResponse -StatusCode 415 } - } - } - else { - Write-PodeHtmlResponse -StatusCode 405 -Value ($Validate.message -join ', ') - } -} | Set-PodeOARouteInfo -Summary 'Create user.' -Description 'This can only be done by the logged in user.' -Tags 'user' -OperationId 'createUser' -PassThru | - Set-PodeOARequest -RequestBody (New-PodeOARequestBody -Content (New-PodeOAContentMediaType -MediaType 'application/json', 'application/xml', 'application/x-www-form-urlencoded' -Content 'User' )) -PassThru | - Add-PodeOAResponse -StatusCode 405 -Description 'Invalid Input' -PassThru | - Add-PodeOAResponse -Default -Content (New-PodeOAContentMediaType -MediaType 'application/json', 'application/xml' -Content 'User' ) -``` -#### Explanation -- The route handles different content types (JSON/XML) and converts them to JSON for validation. -- It validates the received pet object against the 'User' schema using the 'Test-PodeOAJsonSchemaCompliance' function. -- Depending on the validation result, appropriate HTTP responses are returned. -- OpenAPI metadata such as summary, description, request body, and responses are also defined for documentation purposes. - - - -## OpenApi Documentation pages - -If you're not using a custom OpenAPI viewer, then you can use one or more of the inbuilt which Pode supports: ones with Pode: - -* Swagger -* ReDoc -* RapiDoc -* StopLight -* Explorer -* RapiPdf - -For each you can customise the Route path to access the page on, but by default Swagger is at `/swagger`, ReDoc is at `/redoc`, etc. If you've written your own custom OpenAPI definition then you can also set a custom Route path to fetch the definition on. - -To enable a viewer you can use the [`Enable-PodeOAViewer`](../../../Functions/OpenApi/Enable-PodeOAViewer) function: - -```powershell -# for swagger at "/docs/swagger" -Enable-PodeOpenApiViewer -Type Swagger -Path '/docs/swagger' -DarkMode - -# and ReDoc at the default "/redoc" -Enable-PodeOpenApiViewer -Type ReDoc - -# and RapiDoc at "/docs/rapidoc" -Enable-PodeOAViewer -Type RapiDoc -Path '/docs/rapidoc' -DarkMode - -# and StopLight at "/docs/stoplight" -Enable-PodeOAViewer -Type StopLight -Path '/docs/stoplight' - -# and Explorer at "/docs/explorer" -Enable-PodeOAViewer -Type Explorer -Path '/docs/explorer' - -# and RapiPdf at "/docs/rapipdf" -Enable-PodeOAViewer -Type RapiPdf -Path '/docs/rapipdf' - -# plus a bookmark page with the link to all documentation -Enable-PodeOAViewer -Bookmarks -Path '/docs' - -# there is also an OpenAPI editor (only for v3.0.x) -Enable-PodeOAViewer -Editor -Path '/docs/swagger-editor' -``` - -## Multiple OpenAPI definition - -It's possible to create multiple OpenAPI definitions inside the same Server instance. This feature could be useful in situations such as: - -* Multiple versions of the OpenAPI specification for different use cases -* The same OpenAPI definition, but one using OpenAPI v3.0.3 and another using v3.1.0 -* Different APIs based on the IP or URL - - -### How to use it -Any Pode function that interacts with OpenAPI has a `-DefinitionTag [string[]]` parameter available. This allows you to specify within which OpenAPI definition(s) the API's definition should be available. - -!!! note - These functions accept a simple string, and not an array - - * Get-PodeOADefinition - * Enable-PodeOpenApi - * Enable-PodeOAViewer - * Add-PodeOAInfo - * Test-PodeOAJsonSchemaCompliance - -A new OpenAPI definition has to be created using the `Enable-PodeOpenApi` function - -```powershell -Enable-PodeOpenApi -Path '/docs/openapi/v3.0' -OpenApiVersion '3.0.3' -DefinitionTag 'v3' -Enable-PodeOpenApi -Path '/docs/openapi/v3.1' -OpenApiVersion '3.1.0' -DefinitionTag 'v3.1' -Enable-PodeOpenApi -Path '/docs/openapi/admin' -OpenApiVersion '3.1.0' -DefinitionTag 'admin' -``` - -There is also [`Select-PodeOADefinition`](../../../Functions/OpenApi/Select-PodeOADefinition), which simplifies the selection of which OpenAPI definition to use as a wrapper around multiple OpenAPI functions, or Route functions. Meaning you don't have to specify `-DefinitionTag` on embedded OpenAPI/Route calls: - -```powershell -Select-PodeOADefinition -Tag 'v3', 'v3.1' -Scriptblock { - Add-PodeRouteGroup -Path '/api/v5' -Routes { - Add-PodeRoute -Method Get -Path '/petbyRef/:petId' -ScriptBlock { - Write-PodeJsonResponse -Value 'done' -StatusCode 2005 - } - } -} - -Select-PodeOADefinition -Tag 'admin' -ScriptBlock { - # your admin definition -} -``` - -The default `Definition Tag` is named "default". This can be changed using the `Server.psd1` file and the `Web.OpenApi.DefaultDefinitionTag` property - -```powershell -@{ - Web=@{ - OpenApi=@{ - DefaultDefinitionTag= 'NewDfault' - } - } -} -``` - -### OpenAPI example - -A simple OpenAPI definition - -```powershell -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 'apiteam@swagger.io' -DefinitionTag 'v3' - -Add-PodeOAInfo -Title 'Swagger Petstore - OpenAPI 3.1' -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 'apiteam@swagger.io' -DefinitionTag 'v3.1' - -Add-PodeOAServerEndpoint -url '/api/v3' -Description 'default endpoint' -DefinitionTag 'v3', 'v3.1' - -#OpenAPI 3.0 -Enable-PodeOAViewer -Type Swagger -Path '/docs/swagger' -DefinitionTag 'v3' -Enable-PodeOAViewer -Type Bookmarks -Path '/docs' -DefinitionTag 'v3' - -#OpenAPI 3.1 -Enable-PodeOAViewer -Type Swagger -Path '/docs/v3.1/swagger' -DefinitionTag 'v3.1' -Enable-PodeOAViewer -Type ReDoc -Path '/docs/v3.1/redoc' -DarkMode -DefinitionTag 'v3.1' -Enable-PodeOAViewer -Type Bookmarks -Path '/docs/v3.1' -DefinitionTag 'v3.1' - -Select-PodeOADefinition -Tag 'v3', 'v3.1' -ScriptBlock { - New-PodeOAIntProperty -Name 'id'-Format Int64 -Example 10 -Required | - New-PodeOAStringProperty -Name 'name' -Example 'doggie' -Required | - New-PodeOASchemaProperty -Name 'category' -Reference 'Category' | - New-PodeOAStringProperty -Name 'photoUrls' -Array -XmlWrapped -XmlItemName 'photoUrl' -Required | - New-PodeOASchemaProperty -Name 'tags' -Reference 'Tag' -Array -XmlWrapped | - New-PodeOAStringProperty -Name 'status' -Description 'pet status in the store' -Enum @('available', 'pending', 'sold') | - New-PodeOAObjectProperty -XmlName 'pet' | - Add-PodeOAComponentSchema -Name 'Pet' - - - Add-PodeRouteGroup -Path '/api/v3' -Routes { - Add-PodeRoute -PassThru -Method Put -Path '/pet' -Authentication 'merged_auth_nokey' -Scope 'write:pets', 'read:pets' -ScriptBlock { - #code - } | Set-PodeOARouteInfo -Summary 'Update an existing pet' -Description 'Update an existing pet by Id' -Tags 'pet' -OperationId 'updatePet' -PassThru | - Set-PodeOARequest -RequestBody ( - New-PodeOARequestBody -Description 'Update an existent pet in the store' -Required -Content ( - New-PodeOAContentMediaType -ContentMediaType 'application/json', 'application/xml' -Content 'Pet' ) - ) -PassThru | - Add-PodeOAResponse -StatusCode 200 -Description 'Successful operation' -Content (New-PodeOAContentMediaType -ContentMediaType 'application/json', 'application/xml' -Content 'Pet' ) -PassThru | - Add-PodeOAResponse -StatusCode 400 -Description 'Invalid ID supplied' -PassThru | - Add-PodeOAResponse -StatusCode 404 -Description 'Pet not found' -PassThru | - Add-PodeOAResponse -StatusCode 405 -Description 'Validation exception' - } -} -``` diff --git a/docs/Tutorials/OpenAPI/Specification/v3.0.3.md b/docs/Tutorials/OpenAPI/Specification/v3.0.3.md index 55274d89b..9d9ae153f 100644 --- a/docs/Tutorials/OpenAPI/Specification/v3.0.3.md +++ b/docs/Tutorials/OpenAPI/Specification/v3.0.3.md @@ -161,10 +161,10 @@ Types that are not accompanied by a `format` property follow the type definition The formats defined by the OAS are: -| [`type`](#dataTypes) | [`format`](#dataTypeFormat) | [`Pode CmdLet`](https://badgerati.github.io/Pode/Tutorials/OpenAPI/) | Comments | -| -------------------- | --------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -| `integer` | `int32` | [`New-PodeOAIntProperty -Name 'anInteger' -Format Int32`] | signed 32 bits | -| `integer` | `int64` | [`New-PodeOAIntProperty -Name 'aLong' -Format Int64`] | signed 64 bits (a.k.a long) | +| [`type`](#dataTypes) | [`format`](#dataTypeFormat) | [`Pode CmdLet`](https://badgerati.github.io/Pode/Tutorials/OpenAPI/) | Comments | +|----------------------|-----------------------------|----------------------------------------------------------------------|-----------------------------| +| `integer` | `int32` | [`New-PodeOAIntProperty -Name 'anInteger' -Format Int32`] | signed 32 bits | +| `integer` | `int64` | [`New-PodeOAIntProperty -Name 'aLong' -Format Int64`] | signed 64 bits (a.k.a long) | | `number` | `float` | [`New-PodeOANumberProperty -Name 'aFloat' -Format Float`] | | `number` | `double` | [`New-PodeOANumberProperty -Name 'aDouble' -Format Double`] | | `string` | | [`New-PodeOAStringProperty -Name 'aString'`] | @@ -198,7 +198,7 @@ This is the root document object of the [OpenAPI document](#oasDocument). ##### Fixed Fields | Field Name | Type | Pode CmdLets | Description | -| ------------------------------------------ | :-----------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +|--------------------------------------------|:-------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | openapi | `string` | [`Enable-PodeOpenApi`](../../../../Functions/OpenApi/Enable-PodeOpenApi) | **REQUIRED**. This string MUST be the [semantic version number](https://semver.org/spec/v2.0.0.html) of the [OpenAPI Specification version](#versions) that the OpenAPI document uses. The `openapi` field SHOULD be used by tooling specifications and clients to interpret the OpenAPI document. This is *not* related to the API [`info.version`](#infoVersion) string. | | info | [Info Object](#infoObject) | [`Add-PodeOAInfo`](../../../../Functions/OpenApi/Add-PodeOAInfo) | **REQUIRED**. Provides metadata about the API. The metadata MAY be used by tooling as required. | | servers | [[Server Object](#serverObject)] | [`Add-PodeOAServerEndpoint`](../../../../Functions/OpenApi/Add-PodeOAServerEndpoint) | An array of Server Objects, which provide connectivity information to a target server. If the `servers` property is not provided, or is an empty array, the default value would be a [Server Object](#serverObject) with a [url](#serverUrl) value of `/`. | @@ -218,7 +218,7 @@ The metadata MAY be used by the clients if needed, and MAY be presented in editi ##### Fixed Fields | Field Name | Type | `Add-PodeOAInfo` | Description | -| ----------------------------------------------- | :------------------------------: | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | +|-------------------------------------------------|:--------------------------------:|----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| | title | `string` | `-Title` | **REQUIRED**. The title of the API. | | description | `string` | `-Description` | A short description of the API. [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation. | | termsOfService | `string` | `-TermOfService` | A URL to the Terms of Service for the API. MUST be in the format of a URL. | @@ -278,7 +278,7 @@ Contact information for the exposed API. ##### Fixed Fields | Field Name | Type | `Add-PodeOAInfo` | Description | -| -------------------------------- | :------: | ---------------- | ------------------------------------------------------------------------------------------------ | +|----------------------------------|:--------:|------------------|--------------------------------------------------------------------------------------------------| | name | `string` | `-ContactName` | The identifying name of the contact person/organization. | | url | `string` | `-ContactUrl` | The URL pointing to the contact information. MUST be in the format of a URL. | | email | `string` | `-ContactEmail` | The email address of the contact person/organization. MUST be in the format of an email address. | @@ -312,7 +312,7 @@ License information for the exposed API. ##### Fixed Fields | Field Name | Type | `Add-PodeOAInfo` | Description | -| ------------------------------ | :------: | ---------------- | ---------------------------------------------------------------------- | +|--------------------------------|:--------:|------------------|------------------------------------------------------------------------| | name | `string` | `-LicenseName` | **REQUIRED**. The license name used for the API. | | url | `string` | `-LicenseUrl` | A URL to the license used for the API. MUST be in the format of a URL. | @@ -342,7 +342,7 @@ An object representing a Server. ##### Fixed Fields | Field Name | Type | `Add-PodeOAServerEndpoint` | Description | -| ------------------------------------------- | :------------------------------------------------------------: | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +|---------------------------------------------|:--------------------------------------------------------------:|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | url | `string` | `-Url` | **REQUIRED**. A URL to the target host. This URL supports Server Variables and MAY be relative, to indicate that the host location is relative to the location where the OpenAPI document is being served. Variable substitutions will be made when a variable is named in `{`brackets`}`. | | description | `string` | `-Description` | An optional string describing the host designated by the URL. [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation. | | variables | Map[`string`, [Server Variable Object](#serverVariableObject)] | `-Variable` | A map between a variable name and its value. The value is used for substitution in the server's URL template. In Pode the OpenAPI Object's [`servers`](#oasServers) with variables can be defined using a `[ordered]@{}` [System.Collections.Specialized.OrderedDictionary](https://learn.microsoft.com/en-us/dotnet/api/system.collections.specialized.ordereddictionary?view=net-7.0) | @@ -481,7 +481,7 @@ An object representing a Server Variable for server URL template substitution. ##### Fixed Fields | Field Name | Type | Description | -| --------------------------------------------------- | :--------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +|-----------------------------------------------------|:----------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | enum | [`string`] | An enumeration of string values to be used if the substitution options are from a limited set. The array SHOULD NOT be empty. | | default | `string` | **REQUIRED**. The default value to use for substitution, which SHALL be sent if an alternate value is _not_ supplied. Note this behavior is different than the [Schema Object's](#schemaObject) treatment of default values, because in those cases parameter values are optional. If the [`enum`](#serverVariableEnum) is defined, the value SHOULD exist in the enum's values. | | description | `string` | An optional description for the server variable. [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation. | @@ -496,10 +496,10 @@ All objects defined within the components object will have no effect on the API ##### Fixed Fields -| Field Name | Type | Pode | Description | -| -------------------------------------------------------- | :----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -| schemas | Map[`string`, [Schema Object](#schemaObject) \| [Reference Object](#referenceObject)] | [`Add-PodeOAComponentSchema`](../../../../Functions/OAComponents/Add-PodeOAComponentSchema) | An object to hold reusable [Schema Objects](#schemaObject). | -| responses | Map[`string`, [Response Object](#responseObject) \| [Reference Object](#referenceObject)] | [`Add-PodeOAComponentResponse`](../../../../Functions/OAComponents/Add-PodeOAComponentResponse) | An object to hold reusable [Response Objects](#responseObject). | +| Field Name | Type | Pode | Description | +|----------------------------------------------|:------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|-----------------------------------------------------------------| +| schemas | Map[`string`, [Schema Object](#schemaObject) \| [Reference Object](#referenceObject)] | [`Add-PodeOAComponentSchema`](../../../../Functions/OAComponents/Add-PodeOAComponentSchema) | An object to hold reusable [Schema Objects](#schemaObject). | +| responses | Map[`string`, [Response Object](#responseObject) \| [Reference Object](#referenceObject)] | [`Add-PodeOAComponentResponse`](../../../../Functions/OAComponents/Add-PodeOAComponentResponse) | An object to hold reusable [Response Objects](#responseObject). | | parameters | Map[`string`, [Parameter Object](#parameterObject) \| [Reference Object](#referenceObject)] | [`Add-PodeOAComponentParameter`](../../../../Functions/OAComponents/Add-PodeOAComponentParameter) | An object to hold reusable [Parameter Objects](#parameterObject). | PodeOAComponentExample | | examples | Map[`string`, [Example Object](#exampleObject) \| [Reference Object](#referenceObject)] | [`Add-PodeOAComponentExample`](../../../../Functions/OAComponents/Add-PodeOAComponentExample) | An object to hold reusable [Example Objects](#exampleObject). | | requestBodies | Map[`string`, [Request Body Object](#requestBodyObject) \| [Reference Object](#referenceObject)] | [`Add-PodeOAComponentRequestBody`](../../../../Functions/OAComponents/Add-PodeOAComponentRequestBody) | An object to hold reusable [Request Body Objects](#requestBodyObject). | @@ -752,7 +752,7 @@ The path is appended to the URL from the [`Server Object`](#serverObject) in ord ##### Patterned Fields | Field Pattern | Type | Description | -| ------------------------------- | :---------------------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +|---------------------------------|:-----------------------------------:|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | /{path} | [Path Item Object](#pathItemObject) | A relative path to an individual endpoint. The field name MUST begin with a forward slash (`/`). The path is **appended** (no relative URL resolution) to the expanded URL from the [`Server Object`](#serverObject)'s `url` field in order to construct the full URL. [Path templating](#pathTemplating) is allowed. When matching URLs, concrete (non-templated) paths would be matched before their templated counterparts. Templated paths with the same hierarchy but different templated names MUST NOT exist as they are identical. In case of ambiguous matching, it's up to the tooling to decide which one to use. | This object MAY be extended with [Specification Extensions](#specificationExtensions). @@ -839,7 +839,7 @@ The path itself is still exposed to the documentation viewer but they will not k ##### Fixed Fields | Field Name | Type | Description | -| --------------------------------------------- | :----------------------------------------------------------------------------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +|-----------------------------------------------|:------------------------------------------------------------------------------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | $ref | `string` | (Unsupported by Pode) Allows for an external definition of this path item. The referenced structure MUST be in the format of a [Path Item Object](#pathItemObject). In case a Path Item Object field appears both in the defined object and the referenced object, the behavior is undefined. | | summary | `string` | An optional, string summary, intended to apply to all operations in this path. | | description | `string` | An optional, string description, intended to apply to all operations in this path. [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation. | @@ -1096,7 +1096,7 @@ $Route = Add-PodeRoute -PassThru -Method Get -Path '/pet/:petId' -ScriptBlock { ``` | Field Name | Type | `Set-PodeOARouteInfo` | Description | -| ------------------------------------------------ | :---------------------------------------------------------------------------------------: | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +|--------------------------------------------------|:-----------------------------------------------------------------------------------------:|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | tags | `string` | `-Tags` | A list of tags for API documentation control. Tags can be used for logical grouping of operations by resources or any other qualifier. | | summary | `string` | `-Summary` | A short summary of what the operation does. | | description | `string` | `-Description` | A verbose explanation of the operation behavior. [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation. | @@ -1107,25 +1107,25 @@ $Route = Add-PodeRoute -PassThru -Method Get -Path '/pet/:petId' -ScriptBlock { | servers | [[Server Object](#serverObject)] | TBD | An alternative `server` array to service this operation. If an alternative `server` object is specified at the Path Item Object or Root level, it will be overridden by this value. | | Field Name | Type | `Set-PodeOARequest` | Description | -| ---------------------------------------------- | :-------------------------------------------------------------------------------: | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +|------------------------------------------------|:---------------------------------------------------------------------------------:|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | parameters | [[Parameter Object](#parameterObject) \| [Reference Object](#referenceObject)] | `-Parameters` | A list of parameters that are applicable for this operation. If a parameter is already defined at the [Path Item](#pathItemParameters), the new definition will override it but can never remove it. The list MUST NOT include duplicated parameters. A unique parameter is defined by a combination of a [name](#parameterName) and [location](#parameterIn). The list can use the [Reference Object](#referenceObject) to link to parameters that are defined at the [OpenAPI Object's components/parameters](#componentsParameters). | | requestBody | [Request Body Object](#requestBodyObject) \| [Reference Object](#referenceObject) | `-RequestBody` | The request body applicable for this operation. The `requestBody` is only supported in HTTP methods where the HTTP 1.1 specification [RFC7231](https://tools.ietf.org/html/rfc7231#section-4.3.1) has explicitly defined semantics for request bodies. In other cases where the HTTP spec is vague, `requestBody` SHALL be ignored by consumers. | | Field Name | Type | `Set-PodeOAResponse` | Description | -| ------------------------------------------ | :----------------------------------: | -------------------- | ------------------------------------------------------------------------------------------------ | +|--------------------------------------------|:------------------------------------:|----------------------|--------------------------------------------------------------------------------------------------| | responses | [Responses Object](#responsesObject) | | **REQUIRED**. The list of possible responses as they are returned from executing this operation. | | Field Name | Type | `Add-PodeRoute` | Description | -| ---------------------------------------- | :---------------------------------------------------------: | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +|------------------------------------------|:-----------------------------------------------------------:|-------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | security | [[Security Requirement Object](#securityRequirementObject)] | `-Authentication` `-Scope ` | A declaration of which security mechanisms can be used for this operation. The list of values includes alternative security requirement objects that can be used. Only one of the security requirement objects need to be satisfied to authorize a request. To make security optional, an empty security requirement (`{}`) can be included in the array. This definition overrides any declared top-level [`security`](#oasSecurity). To remove a top-level security declaration, an empty array can be used. | | Field Name | Type | Unsupported | Description | -| ------------------------------------------ | :---------------------------------------------------------------------------------------: | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +|--------------------------------------------|:-----------------------------------------------------------------------------------------:|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | callbacks | Map[`string`, [Callback Object](#callbackObject) \| [Reference Object](#referenceObject)] | | A map of possible out-of band callbacks related to the parent operation. The key is a unique identifier for the Callback Object. Each value in the map is a [Callback Object](#callbackObject) that describes a request that may be initiated by the API provider and the expected responses. | | servers | [[Server Object](#serverObject)] | | An alternative `server` array to service this operation. If an alternative `server` object is specified at the Path Item Object or Root level, it will be overridden by this value. | @@ -1275,7 +1275,7 @@ Allows referencing an external resource for extended documentation. ##### Fixed Fields | Field Name | Type | `New-PodeOAExternalDoc` | Description | -| ------------------------------------------------ | :------: | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +|--------------------------------------------------|:--------:|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------| | description | `string` | `-Description` | A short description of the target documentation. [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation. | | url | `string` | `-Url` | **REQUIRED**. The URL for the target documentation. Value MUST be in the format of a URL. | @@ -1317,7 +1317,7 @@ There are four possible parameter locations specified by the `in` field: ##### Fixed Fields | Field Name | Type | `ConvertTo-PodeOAParameter` | Description | -| ------------------------------------------------------- | :-------: | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +|---------------------------------------------------------|:---------:|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | name | `string` | `-Name` | **REQUIRED**. The name of the parameter. Parameter names are *case sensitive*.