AnyChain.psm1
<#
.SYNOPSIS This module can be used to use the Forte AnyChain API from a PowerShell command line or script. .NOTES To install the `powershell-yaml` module, which can be used by this module to parse a YAML-encoded OpenAPI definition of the AnyChain API (though JSON is preferred), execute the following command: > Install-Module -Name powershell-yaml .DESCRIPTION This module provides several functions for accessing the Forte AnyChain API. The basic functionality provided is: * Persist profile settings for specific Forte environments, relieving the user of the need to specify the configuration values with each endpoint request. * Manage the acquisition of tokens for accessing the API. NOTE: This module does not monitor for expired tokens; it leaves token management to the user. * Constructs HTTP requests by converting strings or hashtables into the HTML body, query, and path parameters. * Use the OpenAPI Specification file (JSON or YAML format) to provide context-sensitive parameter prompts when accessing AnyChain endpoints and programmatic access to the AnyChain API specification. #> Set-StrictMode -Version latest $ErrorActionPreference = 'Stop' <# .SYNOPSIS Creates a new AnyChain profile. WARNING: Be aware the PowerShell has a global variable named $profile; do not assign a value to that variable name. .DESCRIPTION This cmdlet creates a new AnyChain profile. Note that it does not activate it or persist the profile. The profile can be persisted using the Export-AnyChainProfile or activated via Use-AnyChainProfile. The profile values for an AnyChain profile can be specified explicitly, implicitly by passing an existing profile to this cmdlet via the pipeline, or a combination of both, which allows copying a profile and overriding some of the settings. .EXAMPLE New-AnyChainProfile -Description "<config-description>" -BaseUrl "https://<app-domain>.anychain.forte.io" -ApplicationID <appid> -AnyChainOpenAPISpecPath "$env:USERPROFILE\\.forte-restish\\spec\\anychain_api.json" | Export-AnyChainProfile -Path <profile-path>.clixml This example create a new profile using explicit parameters, then exports the profile to a file via Export-AnyChainProfile. .EXAMPLE Get-AnyChainAppProfile | New-AnyChainProfile -Description 'Test description' | Export-AnyChainProfile -Path '.\test2.clixml' This example creates a new Forte configuration by copying the current one, overriding its description and saving it to a new profile path. #> function New-AnyChainProfile { [CmdletBinding()] param ( # The Forte client secret specified as an unsecured string. [Parameter(Mandatory=$true, ParameterSetName='UnsecureSecret', ValueFromPipelineByPropertyName)] [Alias('secret','client_secret')] [string] $UnsecuredSecret, # The Forte client secret. To be prompted securely, either leave this unspecified or use Read-Host with the -AsSecureString parameter. [Parameter(Mandatory=$false, ParameterSetName='SecureSecret', ValueFromPipelineByPropertyName)] [SecureString] $ClientSecret, # The Forte application ID. [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName)] [Alias('application_id','client_id','id')] [string] $ApplicationId, # The base URL of the Forte environment. [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName)] [Alias('base_url')] [Uri] $BaseUrl, # A description of the profile. [Parameter(Mandatory=$false, ValueFromPipelineByPropertyName)] [string] $Description, # The optional path to the Forte OpenAPI specification file (e.g., `anychain_api.json`). # This is used to provide argument auto-completion for Forte endpoints, verbs, and bodies. [Parameter(Mandatory=$false, ValueFromPipelineByPropertyName)] [ValidateScript({ Test-Path -Path $_ -PathType Leaf -IsValid })] [ValidateScript({ [IO.Path]::GetExtension($_) -in '.yaml','.json' })] [string] $AnyChainOpenAPISpecPath, # Use the imported profile as the active profile. [switch] $Use ) Process { # If a secret was not specified, prompt for one. if ( -not $ClientSecret -and -not $UnsecuredSecret ) { $ClientSecret = (Read-Host -Prompt "Specify the app's client secret" -AsSecureString) } # Define the profile explicitly from parameters $config = [PSCustomObject] @{ # Version = $MyInvocation.MyCommand.Module.Version Description = $Description ClientSecret = $ClientSecret ?? ($UnsecuredSecret | ConvertTo-SecureString -AsPlainText) BaseUrl = [Uri] $BaseUrl ApplicationId = [string] $ApplicationId AnyChainOpenAPISpecPath = [string] $AnyChainOpenAPISpecPath } # If specified, set the profile as active. if ( $Use ) { $config | Use-AnyChainProfile } # Return the profile $config } } <# .SYNOPSIS Sets the specified AnyChain profile to be used by subsequent AnyChain operations. .EXAMPLE Import-AnyChainProfile -Path <profile-path>.clixml | Use-AnyChainProfile This example imports an AnyChain profile and pipes it to Use-AnyChainProfile to make it active. #> function Use-AnyChainProfile { [CmdletBinding()] param ( # The AnyChain profile to set as active. [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [PSCustomObject] $ProfileObject ) Process { # Import the configuration and make it available internally within this module New-Variable -Name AnyChainProfile -Option ReadOnly -Scope Script -Value $ProfileObject -Force Write-Verbose -Message ($AnyChainProfile | Out-String) # Because there are endpoints that require the app_id be specified, # we'll make it available as a read-only variable. New-Variable -Name app_id -Value ($AnyChainProfile.ApplicationId) -Option ReadOnly,AllScope -Scope Global -Force -Description 'The application ID of the current AnyChain application' New-Variable -Name AnyChainAppID -Value ($AnyChainProfile.ApplicationId) -Option ReadOnly,AllScope -Scope Global -Force -Description 'The application ID of the current AnyChain application' # If the OpenAPI spec was provided, parse and store it. if ( $AnyChainProfile.AnyChainOpenAPISpecPath ) { # Is the OpenAPI specified in a YAML or JSON file? $openAPISpec = switch ( [System.IO.Path]::GetExtension( $AnyChainProfile.AnyChainOpenAPISpecPath ) ) { '.yaml' { # Import the Forte OpenAPI spec in YAML format Get-Content -Path $ExecutionContext.InvokeCommand.ExpandString($AnyChainProfile.AnyChainOpenAPISpecPath) -Raw | convertfrom-yaml } '.json' { # Import the Forte OpenAPI spec in JSON format Get-Content -Path $ExecutionContext.InvokeCommand.ExpandString($AnyChainProfile.AnyChainOpenAPISpecPath) -Raw | ConvertFrom-Json -NoEnumerate -AsHashtable } } # Store the OpenAPI specification in a variable for internal use implementing argument completion. # To allow users to view and navigate the OpenAPI specification, make it available via a variable. New-Variable -Name AnyChainOpenAPISpecification -Scope Global -Option ReadOnly,AllScope -Value $openAPISpec -Force Write-Verbose -Message $AnyChainOpenAPISpecification } else { # Ensure previously assigned OpenAPI specification is removed. Remove-Variable -Name AnyChainOpenAPISpecification -Scope Global -Force -ErrorAction SilentlyContinue } } } <# .SYNOPSIS Returns the currently active AnyChain profile. #> function Get-AnyChainProfile { $AnyChainProfile } <# .SYNOPSIS Imports the specified AnyChain profile from a CLIXML file and optionally uses it. .EXAMPLE Import-AnyChainProfile -Path <profile-path>.clixml This example imports an AnyChain profile. .EXAMPLE Import-AnyChainProfile -Path <profile-path>.clixml -Use This example imports an AnyChain profile and specifies the -Use flag to make it active. #> function Import-AnyChainProfile { [CmdletBinding()] param ( # The file from which to import the AnyChain profile. [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [ValidateScript({ Test-Path -Path $_ -PathType Leaf -IsValid })] [string] $Path, # Use the imported profile as the active profile. [switch] $Use, # Do not return the imported profile. [switch] $Silent ) Process { # Import the configuration and make it available internally within this module $config = Import-Clixml -Path $Path Write-Verbose -Message $config # If specified, set the profile as active. if ( $Use ) { $config | Use-AnyChainProfile } # Return the profile if ( -not $Silent ) { $config } } } <# .SYNOPSIS Exports an AnyChain profile to a CLIXML file. .NOTES The client secret will be persisted as a SecureString, which is securely encrypted and accessible only to the user account that created the CLIXML file. #> function Export-AnyChainProfile { [CmdletBinding()] Param ( # The AnyChain profile to be exported. [Parameter(Mandatory=$false,ValueFromPipeline=$true)] [PSCustomObject] $Profile, # The configuration values for the application specified in a CLIXML file (.clixml). [Parameter(Mandatory=$true)] [ValidateScript({ Test-Path -Path $_ -PathType Leaf -IsValid })] [string] $Path ) Process { # Export the configuration to a CLIXML file. # Note that this stores the client secret encrypted, accessible only to the current user. $Profile ?? $AnyChainProfile | Export-Clixml -Path $Path -Force -Depth 25 } } <# .SYNOPSIS Return the Forte OpenAPI specification for the current profile. #> function Get-AnyChainOpenApiSpecification { $AnyChainOpenAPISpecification } <# .SYNOPSIS Get an access token that can be used to access other Forte AnyChain API endpoints. #> function New-AnyChainAccessToken { [CmdletBinding()] param ( # Indicates the access token should not be returned, though still used for future requests. [switch] $Silent, # Indicates one or more requested scopes (app, admin, and/or console) to include in the token request. [Parameter(DontShow)] [ValidateSet('app','admin','console')] [string[]] $Scope = @( 'app' ) ) # Construct the AnyChain request to obtain an access token. $body = @{ grant_type = 'client_credentials' scope = $Scope -join ' ' client_id = $AnyChainProfile.ApplicationId client_secret = $AnyChainProfile.ClientSecret | ConvertFrom-SecureString -AsPlainText } $headers = @{ Accept = "application/json" } # if ( $Scope -eq 'console' ) { $headers['console-request-id'] = (New-Guid).Guid } # TODO: Experimenting; not a final implementation # Invoke the AnyChain request to obtain an access token. $response = Invoke-RestMethod -Uri "$($AnyChainProfile.BaseUrl)/token" -Method POST -Headers $headers -ContentType 'application/x-www-form-urlencoded' -Body $body # Store the token (as a SecureString) and its approximate expiration time New-Variable -Name AccessTokenExpiration -Option ReadOnly -Scope Script -Value ([DateTime]::Now.AddSeconds($response.expires_in)) -Force New-Variable -Name AccessToken -Option ReadOnly -Scope Script -Value (ConvertTo-SecureString -String $response.access_token -AsPlainText -Force) -Force #New-Variable -Name AccessTokenInfo -Option ReadOnly,AllScope -Scope Global -Value $response -Force # Return the unencrypted token Write-Verbose -Message "New AnyChain access token: $($response.access_token)" if ( -not $Silent ) { $response.access_token } } <# .SYNOPSIS Revoke the current access token so it can no longer be used. #> function Revoke-AnyChainAccessToken { [CmdletBinding()] Param ( # Indicates the access token should not be returned. [switch] $Silent ) # Set the token's expires_in to zero Invoke-AnyChain -Method post -Endpoint /token/expire | Where-Object -FilterScript { -not $Silent } # Invalidate the cached token information $AccessToken = $null #$AccessTokenInfo = $null $AccessTokenExpiration = [DateTime]::MinValue } <# .SYNOPSIS Gets the expiration time of the current token. #> function Get-AnyChainAccessTokenExpiration { $AccessTokenExpiration } <# .SYNOPSIS Invoke an AnyChain endpoint. .DESCRIPTION The Invoke-AnyChain cmdlet submits an HTTP request to the AnyChain web service. The HTTP verb, URL (with embedded path parameters), query parameters, and body are all specified via parameters. It will use the most recently acquired access token unless `-NewToken` is specified, in which case a new token will be requested and used. To retrieve the results of an asynchronous endpoint, pass the operation_id value returned by this cmdlet to Get-AnyChainAsyncNotification (which can also accept the operation_id via the pipeline). .LINK https://github.com/fortelabsinc/AnyChainPSModule/wiki/using-the-anychain-module .EXAMPLE Invoke-AnyChain -Method get -Endpoint "/users/73d3ca2a-55ed-4677-9d8b-cca7941b18de" -NewToken This example retrieves a new token and gets details for user ID 73d3ca2a-55ed-4677-9d8b-cca7941b18de. .EXAMPLE Invoke-AnyChain -Method get -Endpoint /nonfungibles -NewToken -ExpandProperty nonfungible_types This example gets all the nonfungbiles and returns the list of nonfungible types. .EXAMPLE Invoke-AnyChain -Method get -Endpoint /marketplaces/list-price -NewToken -ExpandProperty marketplaces | foreach { Invoke-AnyChain -Method get -Endpoint "/marketplaces/list-price/$($_.id)/listings" -ExpandProperty listings } This example gets a list of marketplaces, the retrieves the listings for each marketplace. .EXAMPLE Invoke-AnyChain -Method post -Endpoint /nonfungibles -NewToken ` -Body @{ contract_detail = @{ chain_instance_id='33107d1e-a585-4e89-b772-4ebc8a0d0d0f' }; name = "Mabel's NFT type"; symbol = "MabelNFT" } This example defines a nonfungible type as a hashtable and uses it as the body of a request to create that type. .EXAMPLE Invoke-AnyChain -Method post -Endpoint /nonfungibles -NewToken ` -Body @{ contract_detail = @{ chain_instance_id='33107d1e-a585-4e89-b772-4ebc8a0d0d0f' }; name = "Dipper's NFT type"; symbol = "DipperNFT" } ` | Wait-AnyChainAsyncNotification -ResponseOnly This example creates a new nonfungible type, which is an asynchronous operation, then uses the Wait-AnyChainAsyncNotification to wait for the asynchronous request to complete and returns the async response, which contains the new nonfungible type. .EXAMPLE Invoke-AnyChain -Method get -Endpoint /users -ExpandProperty users -NewToken:$((Get-AnyChainAccessTokenExpiration) -lt (Get-Date)) This example retrieves a new token *if and only if* the current token has expired, then retrieves a list of users. #> function Invoke-AnyChainEndpoint { [CmdletBinding(SupportsShouldProcess=$true,DefaultParameterSetName='UserToken')] [Alias('Invoke-AnyChain')] [Alias('iac')] Param ( # Indicates that a new access token should be generated for this call. [Parameter(Mandatory=$false,ParameterSetName='UserToken')] [switch] $NewToken, # Indicates that a new admin access token should be generated for this call. [Parameter(Mandatory=$true,ParameterSetName='AdminToken')] [switch] $NewAdminToken, # The HTTP method to use (i.e., Get, Post, Patch, or Delete). [Parameter(Mandatory=$true,Position=0)] [ArgumentCompleter( { GetEndpointArguments @args } )] [ValidateScript({ $_ -in [Enum]::GetNames([Microsoft.PowerShell.Commands.WebRequestMethod]) })] [Alias('Verb')] [string] $Method, # The AnyChain REST endpoint (after the configured base URL). [Parameter(Mandatory=$true,Position=1)] [ValidateNotNullOrEmpty()] [ArgumentCompleter( { GetEndpointArguments @args } )] [Alias('Path')] [string] $Endpoint, # The endpoint's query parameters, either as a URI query string or a hashtable of name/value pairs. [ArgumentCompleter( { GetEndpointArguments @args } )] $QueryParams, # The endpoint's body specified as a JSON string, or as a hashtable of name/value pairs # which will be converted to a JSON string. [ArgumentCompleter( { GetEndpointArguments @args } )] $Body, # Expand the specified property of the returned object(s). # This is comparable to piping the results to the Select-Object cmdlet and specifying its ExpandProperty parameter. [ArgumentCompleter( { GetEndpointArguments @args } )] [string] $ExpandProperty, # Indicates that multiple requests should be processed to ensure all pages of data # have been retrieved. [switch] $All, # Indicates that the request should be made as a web request instead of a REST request. # This is primarily for debugging or if the caller wants access to the HTTP response information # in addition to the REST response data. [switch] $UseWebRequest ) # If requested, get a new access token. if ( $NewToken -or $NewAdminToken ) { New-AnyChainAccessToken -Silent -Scope:($NewAdminToken ? 'admin' : 'app') } # If necessary, convert the body to JSON if ( $Body -is [System.Collections.IDictionary] ) { $Body = $Body | ConvertTo-Json -Compress -Depth 50 } # Ensure query string is represented as a hashtable if ( $QueryParams -is [string] ) { $QueryParams = $QueryParams -split '&' -join "`n" | ConvertFrom-StringData } # If all pages were requested (and this is a Get), request multiple pages of results. if ( $All -and $Method -eq 'get' ) { if ( -not $QueryParams ) { $QueryParams = @{} } if ( -not $queryParams.ContainsKey('cursor') ) { $QueryParams['cursor'] = 1 } if ( -not $queryParams.ContainsKey('per_page') ) { $QueryParams['per_page'] = 100 } } do { # Convert $QueryParams into a URL query string [string] $query = if ( $QueryParams -and $QueryParams.Count ) { # Generate the query string from the specified QueryParams dictionary $QueryParams.GetEnumerator() | ForEach-Object { "{0}={1}" -f [Web.HttpUtility]::UrlEncode($_.Name), [Web.HttpUtility]::UrlEncode($_.Value) } | Join-String -Separator '&' } # Construct and process the REST request $headers = @{ Accept = "application/json" } # Construct the complete URL by combining the URI and the query string $uri = [UriBuilder]::new( $AnyChainProfile.BaseUrl.ToString().TrimEnd('/') + '/' + $Endpoint.TrimStart('/') ) $uri.Query = $query # Write verbose description of REST request "Invoking AnyChain request: {0}" -f (@{ uri = $uri.Uri; method = $Method; headers = $headers; body = $body } | ConvertTo-Json -Compress) | Write-Verbose # Check for -WhatIf or -Confirm flags $response = if ( $PSCmdlet.ShouldProcess( $Method + " " + $uri.Uri + " " + $Body ) ) { # Invoke the AnyChain request (using either Invoke-WebRequest or Invoke-RestMethod) if ( $UseWebRequest ) { Invoke-WebRequest -Uri $uri.Uri -Method $Method -Headers $headers -ContentType 'application/json' -Body $Body -Authentication Bearer -Token $AccessToken } else { Invoke-RestMethod -Uri $uri.Uri -Method $Method -Headers $headers -ContentType 'application/json' -Body $Body -Authentication Bearer -Token $AccessToken } } # Forward web request result to the stream. If requested, expand the specified property. $response | Select-Object -ExpandProperty $ExpandProperty # If retrieving all pages, look for the next page. Break out if none specified. if ( $All -and $Method -eq 'get' ) { try { $QueryParams['cursor'] = $response.pagination.next_cursor } catch { break } $NewToken = $false } } while ( $All ) } <# .SYNOPSIS This cmdlet returns the latest notification (operation and response) for the specified async operation, polling every WaitMilliseconds milliseconds waiting for the operation to either complete or fail. .NOTES This is a convenience function to provide simple and easy access to the result of an asynchronous endpoint. It does not support paging or returning results for more than one operation. To access the full functionality of the /apps/{app_id}/notifications endpoint, use the Invoke-AnyChain cmdlet to access it explicitly. .EXAMPLE Invoke-AnyChain -Method post -Endpoint /nonfungibles -NewToken ` -Body @{ contract_detail = @{ chain_instance_id='33107d1e-a585-4e89-b772-4ebc8a0d0d0f' }; name = "Dipper's NFT type"; symbol = "DipperNFT" } ` | Wait-AnyChainAsyncNotification -ResponseOnly This example creates a new nonfungible type, which is an asynchronous operation, then uses the Wait-AnyChainAsyncNotification to wait for the asynchronous request to complete and returns the async response, which contains the new nonfungible type. .EXAMPLE Invoke-AnyChain -Method post -Endpoint /nonfungibles -NewToken ` -Body @{ contract_detail = @{ chain_instance_id='33107d1e-a585-4e89-b772-4ebc8a0d0d0f' }; name = "Mabel's NFT type"; symbol = "MabelNFT" } ` | Wait-AnyChainAsyncNotification -ResponseOnly -ExpandProperty nonfungible_type This example uses the Wait-AnyChainAsyncNotification to wait for the asynchronous request to complete and returns the nonfungible_type property of the async response. #> filter Wait-AnyChainAsyncNotification { [Alias('wac')] [CmdletBinding()] Param ( # The operation for which a response is requested. [Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName)] [Alias('operation_id')] [string] $OperationID, # Return the response(s) instead of the full notification. [switch] $ResponseOnly, # Expand the specified property of the returned object(s). # This is comparable to piping the results to the Select-Object cmdlet and specifying its ExpandProperty parameter. [string] $ExpandProperty, # How frequently (in milliseconds) to poll for a result. To return without waiting, specify zero (0) milliseconds. [int] $WaitMilliseconds = 100, # The application from which to retrieve the response(s) (default to ApplicationId of current profile). [string] $ApplicationId ) Process { # Initialize the application ID (when done as a parameter initialization, it acts like a breakpoint when debugging) if ( -not $ApplicationId ) { $ApplicationId = $AnyChainProfile.ApplicationId } # Retrieve the async notification $getNewToken = try { ( (Get-Date).AddMilliseconds(10000) -gt $AccessTokenExpiration ) } catch { $true } $rawResponse = Invoke-AnyChain -Endpoint "/apps/$ApplicationId/notifications" -Method get -NewToken:$getNewToken -QueryParams @{ operation_id = $OperationID } # If the operation is still pending and a wait interval was requested (the default), sleep and try again. while ( $WaitMilliseconds -and ($rawResponse.notifications | Sort-Object -Bottom 1 -Property sequence_number).operation.status -eq 'pending' ) { # Give the operation some time to complete Start-Sleep -Milliseconds $WaitMilliseconds # Check the status of the operation again, requesting a new token if within 10 seconds of the current token's expiration. $getNewToken = ( (Get-Date).AddMilliseconds(10000) -gt $AccessTokenExpiration ) $rawResponse = Invoke-AnyChain -Endpoint "/apps/$ApplicationId/notifications" -Method get -NewToken:$getNewToken -QueryParams @{ operation_id = $OperationID } } # Return either the latest response or the latest notification, depending on the ResponseOnly parameter. $rawResponse.notifications | Sort-Object -Property sequence_number -Bottom 1 | ForEach-Object { $ResponseOnly ? $_.response : $_ | Select-Object -ExpandProperty $ExpandProperty } } } <# .SYNOPSIS Gets the AnyChain definition for the specified endpoint/method pair. If External is requested, open the reference web page for this command. #> function Get-AnyChainEndpointSpecification { Param ( # The AnyChain REST endpoint (after the configured base URL). [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [ArgumentCompleter( { GetEndpointArguments @args } )] [Alias('Path')] [string] $Endpoint, # The HTTP method to use (i.e., Get, Post, Patch, or Delete). [Parameter(Mandatory=$true)] [ArgumentCompleter( { GetEndpointArguments @args } )] [ValidateScript({ $_ -in [Enum]::GetNames([Microsoft.PowerShell.Commands.WebRequestMethod]) })] [Alias('Verb')] [string] $Method, # Open the external help web page (as specified in the API specification as 'externalDocs.url' under the $Endpoint/$Method-specified 'paths' property). [switch] $External ) if ( $External ) { try { Start-Process -FilePath $AnyChainOpenAPISpecification.paths[ $Endpoint][ $Method ].externalDocs.url.ToLower() } catch { } } $AnyChainOpenAPISpecification.paths[ $Endpoint][ $Method ] } <# .SYNOPSIS Returns the specified OpenAPI specification "reference" from the AnyChain specification. A "reference" is an OpenAPI element that contains a $ref property, which indicates another element in the specification which defines the current referencing element. .EXAMPLE (Get-AnyChainEndpointSpecification -Endpoint /fungibles -Method post).requestBody | Get-AnyChainOpenApiReference #> function Get-AnyChainOpenApiReference { Param ( [Parameter(Mandatory=$true,ValueFromPipeline=$true)] $Element ) Process { # If the OpenAPI element contains a reference ($ref), dereference it. Otherwise, return the element. if ( $Element -is [System.Collections.IDictionary] -and $Element.Keys -contains '$ref' ) { # Find the path to the referenced element and retrieve it. $target = DerefOASItem -RefItem $Element # If the referencing element has properties, add them. $target += $Element $target.Remove('$ref') $target } else { $Element } } } <# .SYNOPSIS Returns a list of AnyChain endpoints along with their descriptions, query parameters, and bodies as defined in the OpenAPI specification. If one or more Methods and/or Endpoints are specified, it will return endpoints that match the specified criteria. Wildcards are allowed for endpoints. #> function Get-AnyChainEndpoint { [CmdletBinding()] param ( # The HTTP method(s) to include (i.e., Get, Post, Patch, or Delete). # Default is to include all of them. [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName)] [ArgumentCompleter( { GetEndpointArguments @args } )] [ValidateScript({ $_ -in [Enum]::GetNames([Microsoft.PowerShell.Commands.WebRequestMethod]) })] [Alias('Verb')] [string[]] $Method = [Enum]::GetNames([Microsoft.PowerShell.Commands.WebRequestMethod]), # The AnyChain REST endpoint. [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [SupportsWildcards()] [ArgumentCompleter( { GetEndpointArguments @args } )] [Alias('Path')] [string] $Endpoint ) Process { # Retrieve all endpoints that match the specified endpoint name and method/verb. $AnyChainOpenAPISpecification.paths.GetEnumerator() | ForEach-Object ` { $path = $_.Key if ( -not $Endpoint -or $path -like $Endpoint ) { $_.Value.GetEnumerator() | ForEach-Object ` { $verb = $_.Key if ( $verb -in $Method -and $verb -in [Enum]::GetNames([Microsoft.PowerShell.Commands.WebRequestMethod]) ) { $body = Get-EndpointBody -Method $verb -Endpoint $path $qParams = Get-EndpointQueryParams -Method $verb -Endpoint $path $response = Get-EndpointResponse -Method $verb -Endpoint $path [PSCustomObject] @{ Verb=$verb; Path=$path; Summary = $_.Value.summary; Description = $_.Value.description; Body = $body; QueryParams = $qParams; Response = $response } } } } } } } |