PoshKelvin.psm1

#Region '.\private\_GetPaginatedData.ps1' -1

function _GetPaginatedData {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [uri]$Uri,
        
        [Parameter(Mandatory)]
        [string]$Method,

        [Parameter()]
        [object]$Body,

        [Parameter()]
        [string]$ContentType,

        [Parameter()]
        [hashtable]$Headers,

        [Parameter()]
        $TypeName
    )

    $currentUri = $Uri
    $pageCount = 1
    
    do {
        try {
            Write-Verbose "Requesting data from $currentUri"
            
            $response = Invoke-RestMethod -Uri $currentUri -Method $Method -Body $Body `
                -ContentType $ContentType -Headers $Headers -ErrorAction Stop

            if ($response.PSObject.Properties.Name -contains 'pagination') {
                Write-Verbose "Pagination detected. Processing page $pageCount"
                Write-Verbose $response.pagination | ConvertTo-Json -Compress

                try {
                    $response.data | ForEach-Object {
                        $item = $_
                        if ($TypeName) {
                            $_.PSObject.TypeNames.Insert(0, $TypeName)
                        }
                        Write-Output $item
                    }
                }
                catch {
                    Write-Error "Failed to process page $pageCount data: $($_.Exception.Message)"
                    return
                }
                
                if ($response.pagination.next_page) {
                    try {
                        $builder = [UriBuilder]$Uri
                        $query = [System.Web.HttpUtility]::ParseQueryString($builder.Query)
                        $query['next'] = $response.pagination.next_page
                        $query['previous'] = $response.pagination.previous_page
                        $builder.Query = $query.ToString()
                        $currentUri = $builder.Uri
                        $pageCount++
                    }
                    catch {
                        Write-Error "Failed to build pagination URL: $($_.Exception.Message)"
                        return
                    }
                }
                else {
                    $currentUri = $null
                }
            }
            else {
                Write-Output $response
                $currentUri = $null
            }
        }
        catch [System.Net.WebException] {
            Write-Error "HTTP request failed on page $pageCount`: $_"
            return
        }
        catch {
            Write-Error "Unexpected error on page $pageCount`: $_"
            return
        }
    } while ($currentUri)
}
#EndRegion '.\private\_GetPaginatedData.ps1' 85
#Region '.\private\_GetParams.ps1' -1

<#
.SYNOPSIS
    This function is used to extract parameters from a hashtable based on a mapping.
.DESCRIPTION
    The function takes a hashtable of bound parameters and a mapping hashtable.
    It returns a new hashtable containing only the parameters that are present in the mapping.
    This way, parameters not supplied by the user are not passed to the API call.
#>

Function _GetParams {
    
    $caller = (Get-PSCallStack)[1]
    $invocationInfo = $caller.InvocationInfo
    $callerCommand = $invocationInfo.MyCommand
    $boundParameters = $invocationInfo.BoundParameters

    $allParams = $callerCommand.Parameters.Values `
    | Where-Object { $_.ParameterSets.Keys -contains 'Query' } `
    | Where-Object { $boundParameters.Keys -contains $_.Name }
    
    $result = @{}

    foreach ($parm in $allParams) {

        $key = $parm.Name
        $val = $boundParameters[$key]
        $parmName = _GetParamName $parm

        if ($null -eq $val) { continue }

        $result[$parmName] = $val
    }

    return $result
}

Function _GetParamName ($parm) {

    $paramName = $parm.Name.ToLower()

    if ($null -ne $parm.Aliases) {
        $paramName = $parm.Aliases[0].ToLower()
    }

    if ($null -ne $parm.Alias) {
        $paramName = $parm.Alias[0].ToLower()
    }

    return $paramName
}
#EndRegion '.\private\_GetParams.ps1' 50
#Region '.\public\Connect-KelvinAccount.ps1' -1

<#
.SYNOPSIS
Connects to a Kelvin account using the provided URL, Username, and Password.
 
.DESCRIPTION
The Connect-KelvinAccount function authenticates against the Kelvin service
using the provided URL, Username, and Password. Upon successful authentication,
it stores the URL, credentials, and token in script-scoped variables for later use.
 
.PARAMETER Url
The base URL of the Kelvin service.
 
.PARAMETER Credential
A PSCredential object containing the Username and Password for authentication.
#>


function Connect-KelvinAccount {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [uri] $Url,

        [Parameter(Mandatory = $true)]
        [pscredential] $Credential,

        [Parameter()]
        [ValidateScript({ $_ -match 'v\d+' })]
        [string] $ApiVersion = 'v4'
    )

    $endpointUrl = $Url.AbsoluteUri.TrimEnd('/')
    Write-Verbose "Connecting to Kelvin service at $endpointUrl"

    # Define the authentication endpoint
    $authEndpoint = "$endpointUrl/auth/realms/kelvin/protocol/openid-connect/token"
    Write-Verbose "Authenticating against Kelvin service at $authEndpoint"

    # Prepare the body for the authentication request
    $body = @{
        username   = $Credential.UserName
        password   = $Credential.GetNetworkCredential().Password
        client_id  = "kelvin-client"
        grant_type = "password"
    }

    try {
        # Make the authentication request
        $response = Invoke-RestMethod -Uri $authEndpoint -Method Post -Body $body

        # Check if the response contains a token
        if ($response -and $response.access_token) {
            Write-Verbose "Successfully authenticated against Kelvin service."

            if ($endpointUrl -notmatch '/api/v\d+$') {
                $endpointUrl = "$endpointUrl/api/$ApiVersion"
                Write-Verbose "URL updated to include API version '$ApiVersion': $endpointUrl"
            }
            else {
                Write-Verbose "URL already includes API version '$ApiVersion'. Ignoring the ApiVersion parameter."
            }

            # Store the URL, credentials, and token in script-scoped variables
            $script:KelvinURL = $endpointUrl
            $script:KelvinUsername = $Username
            $script:KelvinPassword = $Password
            $script:KelvinToken = $response.access_token
        }
        else {
            Write-Error "Authentication failed. No token received."
        }
    }
    catch {
        Write-Error "An error occurred during authentication: $_"
    }
}
#EndRegion '.\public\Connect-KelvinAccount.ps1' 76
#Region '.\public\Get-KelvinApp.ps1' -1

<#
.SYNOPSIS
    Gets applications from the App Registry.
.DESCRIPTION
    Retrieves application information from the currently connected Kelvin instance App Registry.
#>

Function Get-KelvinApp {
    [OutputType('Kelvin.App')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [string[]] $Name,

        [Parameter(ParameterSetName = 'Query')]
        [string[]] $Type,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'appregistry/list' -Method Get -TypeName 'Kelvin.App' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "appregistry/$($_.name)/get" -Method Get -TypeName 'Kelvin.App')
            }
            else {
                $ret = $_
            }
            Write-Output $ret
        }
    }
}
#EndRegion '.\public\Get-KelvinApp.ps1' 41
#Region '.\public\Get-KelvinAppManagerResource.ps1' -1

<#
.SYNOPSIS
    Gets app manager for a specific resource.
.DESCRIPTION
    Retrieves app manager details for a specific resource from the currently connected Kelvin instance.
#>

Function Get-KelvinAppManagerResource {
    [OutputType('Kelvin.AppManagerResource')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $true, Position = 0)]
        [Alias('resource_krn')]
        [string] $ResourceKrn
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi "app-manager/resource/$ResourceKrn/get" -Method Get -TypeName 'Kelvin.AppManagerResource' -Parameters $params `
        | ForEach-Object {
            Write-Output $_
        }
    }
}
#EndRegion '.\public\Get-KelvinAppManagerResource.ps1' 26
#Region '.\public\Get-KelvinAppResource.ps1' -1

<#
.SYNOPSIS
    Gets app resources for a specific application.
.DESCRIPTION
    Retrieves resources associated with a specific application from the currently connected Kelvin instance.
#>

Function Get-KelvinAppResource {
    [OutputType('Kelvin.AppResource')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $true, Position = 0)]
        [Alias('app_name')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [string] $AppName,

        [Parameter()]
        [string[]] $Search,

        [Parameter()]
        [string[]] $ResourceName,

        [Parameter()]
        [ValidateSet('running', 'stopped', 'unknown')]
        [string[]] $Status
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi "app-manager/app/$AppName/resources/list" -Method Post -TypeName 'Kelvin.AppResource' -Parameters $params `
        | ForEach-Object {
            Write-Output $_
        }
    }
}
#EndRegion '.\public\Get-KelvinAppResource.ps1' 37
#Region '.\public\Get-KelvinAsset.ps1' -1

<#
.SYNOPSIS
    Gets assets from Kelvin.
.DESCRIPTION
    Retrieves assets from the currently connected Kelvin instance.
#>

Function Get-KelvinAsset {
    [OutputType('Kelvin.Asset')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [Alias('names')]
        [string[]] $Name,

        [Parameter(ParameterSetName = 'Query')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [Alias('asset_type')]
        [string[]] $AssetType,

        [Parameter(ParameterSetName = 'Query')]
        [ValidateSet('online', 'offline', 'unknown')]
        [string[]] $Status,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'assets/list' -Method Get -TypeName 'Kelvin.Asset' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "assets/$($_.name)/get" -Method Get -TypeName 'Kelvin.Asset')
            }
            else {
                $ret = $_
            }
            Write-Output $ret
        }
    }
}
#EndRegion '.\public\Get-KelvinAsset.ps1' 48
#Region '.\public\Get-KelvinAssetType.ps1' -1

<#
.SYNOPSIS
    Gets asset types from Kelvin.
.DESCRIPTION
    Retrieves asset types from the currently connected Kelvin instance.
#>

Function Get-KelvinAssetType {
    [OutputType('Kelvin.AssetType')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [Alias('asset_type_name')]
        [string[]] $Name,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'assets/types/list' -Method Get -TypeName 'Kelvin.AssetType' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "assets/types/$($_.name)/get" -Method Get -TypeName 'Kelvin.AssetType')
            }
            else {
                $ret = $_
            }
            Write-Output $ret
        }
    }
}
#EndRegion '.\public\Get-KelvinAssetType.ps1' 39
#Region '.\public\Get-KelvinAuditLog.ps1' -1

<#
.SYNOPSIS
    Gets audit logs from Kelvin.
.DESCRIPTION
    Retrieves audit logs from the currently connected Kelvin instance.
#>

Function Get-KelvinAuditLog {
    [OutputType('Kelvin.AuditLog')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0)]
        [string[]] $Search,

        [Parameter()]
        [Alias('audit_logger_id')]
        [string[]] $Id,

        [Parameter()]
        [string[]] $User,

        [Parameter()]
        [string[]] $Action,

        [Parameter()]
        [string[]] $Resource,

        [Parameter()]
        [DateTime] $StartTime,

        [Parameter()]
        [DateTime] $EndTime,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        # Convert DateTime objects to ISO 8601 strings if provided
        if ($StartTime) {
            $params['start_time'] = $StartTime.ToString('o')
        }

        if ($EndTime) {
            $params['end_time'] = $EndTime.ToString('o')
        }

        Invoke-KelvinApi 'instance/auditlog/list' -Method Get -TypeName 'Kelvin.AuditLog' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "instance/auditlog/$($_.id)/get" -Method Get -TypeName 'Kelvin.AuditLog')
            }
            else {
                $ret = $_
            }
            Write-Output $ret
        }
    }
}
#EndRegion '.\public\Get-KelvinAuditLog.ps1' 62
#Region '.\public\Get-KelvinBridge.ps1' -1

<#
.SYNOPSIS
    Gets bridges from Kelvin.
.DESCRIPTION
    Retrieves bridges from the currently connected Kelvin instance.
#>

Function Get-KelvinBridge {
    [OutputType('Kelvin.Bridge')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [Alias('bridge_name')]
        [string[]] $Name,

        [Parameter(ParameterSetName = 'Query')]
        [bool] $Running,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'bridges/list' -Method Get -TypeName 'Kelvin.Bridge' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "bridges/$($_.name)/get" -Method Get -TypeName 'Kelvin.Bridge')
            }
            else {
                $ret = $_
            }
            Write-Output $ret
        }
    }
}
#EndRegion '.\public\Get-KelvinBridge.ps1' 42
#Region '.\public\Get-KelvinCluster.ps1' -1

<#
.SYNOPSIS
    Gets clusters.
.DESCRIPTION
    Retrieves clusters from the currently connected Kelvin instance.
#>

Function Get-KelvinCluster {
    [OutputType('Kelvin.Cluster')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [Alias('names')]
        [string[]] $Name,

        [Parameter(ParameterSetName = 'Query')]
        [bool] $Ready,

        [Parameter(ParameterSetName = 'Query')]
        [string[]] $Type,

        [Parameter(ParameterSetName = 'Query')]
        [ValidateSet('pending_provision', 'pending', 'online', 'unreachable', 'requires_attention')]
        [string[]] $Status,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'orchestration/clusters/list' -Method Get -TypeName 'Kelvin.Cluster' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "orchestration/clusters/$($_.name)/get" -Method Get -TypeName 'Kelvin.Cluster')
            }
            else {
                $ret = $_
            }
            Write-Output ($ret | Add-Member -Name 'cluster_name' -Value $ret.name -MemberType NoteProperty -PassThru)
        }
    }
}
#EndRegion '.\public\Get-KelvinCluster.ps1' 49
#Region '.\public\Get-KelvinClusterNode.ps1' -1

<#
.SYNOPSIS
    Gets nodes from a Kelvin cluster.
.DESCRIPTION
    Retrieves the nodes from a specific cluster in the currently connected Kelvin instance.
#>

Function Get-KelvinClusterNode {
    [OutputType('Kelvin.ClusterNode')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)]
        [Alias('cluster_name')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [string] $ClusterName,

        [Parameter(Position = 1)]
        [string[]] $Search,

        [Parameter()]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [Alias('node_name', 'names')]
        [string[]] $Name,

        [Parameter()]
        [ValidateSet('online', 'offline', 'unknown')]
        [string[]] $Status,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi "orchestration/clusters/$ClusterName/nodes/list" -Method Get -TypeName 'Kelvin.ClusterNode' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "orchestration/clusters/$ClusterName/nodes/$($_.name)/get" -Method Get -TypeName 'Kelvin.ClusterNode')
            }
            else {
                $ret = $_
            }
            Write-Output ($ret | Add-Member -Name 'node_name' -Value $ret.name -MemberType NoteProperty -PassThru)
        }
    }
}
#EndRegion '.\public\Get-KelvinClusterNode.ps1' 48
#Region '.\public\Get-KelvinClusterService.ps1' -1

<#
.SYNOPSIS
    Gets services from a Kelvin cluster.
.DESCRIPTION
    Retrieves the services from a specific cluster in the currently connected Kelvin instance.
#>

Function Get-KelvinClusterService {
    [OutputType('Kelvin.ClusterService')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)]
        [Alias('cluster_name')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [string] $ClusterName,

        [Parameter(Position = 1)]
        [string[]] $Search,

        [Parameter()]
        [string[]] $Name,
        
        [Parameter()]
        [string[]] $Type,

        [Parameter()]
        [ValidateSet('running', 'stopped', 'unknown')]
        [string[]] $Status
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi "orchestration/clusters/$ClusterName/services/list" -Method Get -TypeName 'Kelvin.ClusterService' -Parameters $params `
        | ForEach-Object {
            Write-Output $_
        }
    }
}
#EndRegion '.\public\Get-KelvinClusterService.ps1' 40
#Region '.\public\Get-KelvinDataStream.ps1' -1

<#
.SYNOPSIS
    Gets datastreams from Kelvin.
.DESCRIPTION
    Retrieves datastreams from the currently connected Kelvin instance.
#>

Function Get-KelvinDataStream {
    [OutputType('Kelvin.DataStream')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [Alias('data_stream_name')]
        [string[]] $Name,

        [Parameter(ParameterSetName = 'Query')]
        [string[]] $DataType,

        [Parameter(ParameterSetName = 'Query')]
        [string[]] $SemanticType,

        [Parameter(ParameterSetName = 'Query')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [Alias('asset_name')]
        [string[]] $AssetName,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'datastreams/list' -Method Get -TypeName 'Kelvin.DataStream' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "datastreams/$($_.name)/get" -Method Get -TypeName 'Kelvin.DataStream')
            }
            else {
                $ret = $_
            }
            Write-Output $ret
        }
    }
}
#EndRegion '.\public\Get-KelvinDataStream.ps1' 50
#Region '.\public\Get-KelvinDataStreamSemanticType.ps1' -1

<#
.SYNOPSIS
    Gets data stream semantic types from Kelvin.
.DESCRIPTION
    Retrieves data stream semantic types from the currently connected Kelvin instance.
#>

Function Get-KelvinDataStreamSemanticType {
    [OutputType('Kelvin.DataStreamSemanticType')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [Alias('semantic_type_name')]
        [string[]] $Name,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'datastreams/semantic-types/list' -Method Get -TypeName 'Kelvin.DataStreamSemanticType' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "datastreams/semantic-types/$($_.name)/get" -Method Get -TypeName 'Kelvin.DataStreamSemanticType')
            }
            else {
                $ret = $_
            }
            Write-Output $ret
        }
    }
}
#EndRegion '.\public\Get-KelvinDataStreamSemanticType.ps1' 38
#Region '.\public\Get-KelvinDataStreamUnit.ps1' -1

<#
.SYNOPSIS
    Gets data stream units from Kelvin.
.DESCRIPTION
    Retrieves data stream units from the currently connected Kelvin instance.
#>

Function Get-KelvinDataStreamUnit {
    [OutputType('Kelvin.DataStreamUnit')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [Alias('unit_name')]
        [string[]] $Name,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'datastreams/units/list' -Method Get -TypeName 'Kelvin.DataStreamUnit' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "datastreams/units/$($_.name)/get" -Method Get -TypeName 'Kelvin.DataStreamUnit')
            }
            else {
                $ret = $_
            }
            Write-Output $ret
        }
    }
}
#EndRegion '.\public\Get-KelvinDataStreamUnit.ps1' 38
#Region '.\public\Get-KelvinDataTag.ps1' -1

<#
.SYNOPSIS
    Gets datatags from Kelvin.
.DESCRIPTION
    Retrieves datatags from the currently connected Kelvin instance.
#>

Function Get-KelvinDataTag {
    [OutputType('Kelvin.DataTag')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [Alias('datatag_id')]
        [string[]] $Id,

        [Parameter(ParameterSetName = 'Tags')]
        [switch] $Tags,

        [Parameter(ParameterSetName = 'Tags')]
        [Alias('tag_name')]
        [string[]] $TagName,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        if ($Tags.IsPresent) {
            $endpoint = 'datatags/tags/list'
            $detailEndpoint = "datatags/tags/{0}/get"
            $idProperty = "name"
        }
        else {
            $endpoint = 'datatags/list'
            $detailEndpoint = "datatags/{0}/get"
            $idProperty = "id"
        }

        Invoke-KelvinApi $endpoint -Method Get -TypeName 'Kelvin.DataTag' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi ($detailEndpoint -f $_.$idProperty) -Method Get -TypeName 'Kelvin.DataTag')
            }
            else {
                $ret = $_
            }
            Write-Output $ret
        }
    }
}
#EndRegion '.\public\Get-KelvinDataTag.ps1' 56
#Region '.\public\Get-KelvinDataType.ps1' -1

<#
.SYNOPSIS
    Gets data types from Kelvin.
.DESCRIPTION
    Retrieves data types from the currently connected Kelvin instance.
#>

Function Get-KelvinDataType {
    [OutputType('Kelvin.DataType')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0)]
        [string[]] $Search,

        [Parameter()]
        [string[]] $Name
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'datastreams/data-types/list' -Method Get -TypeName 'Kelvin.DataType' -Parameters $params `
        | ForEach-Object {
            Write-Output $_
        }
    }
}
#EndRegion '.\public\Get-KelvinDataType.ps1' 28
#Region '.\public\Get-KelvinFile.ps1' -1

<#
.SYNOPSIS
    Gets files from Kelvin file storage.
.DESCRIPTION
    Retrieves files from the currently connected Kelvin instance file storage.
#>

Function Get-KelvinFile {
    [OutputType('Kelvin.File')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [Alias('file_id')]
        [string[]] $Id,

        [Parameter(ParameterSetName = 'Query')]
        [string[]] $Name,

        [Parameter(ParameterSetName = 'Query')]
        [string[]] $Type,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'filestorage/list' -Method Get -TypeName 'Kelvin.File' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "filestorage/$($_.id)/get" -Method Get -TypeName 'Kelvin.File')
            }
            else {
                $ret = $_
            }
            Write-Output $ret
        }
    }
}
#EndRegion '.\public\Get-KelvinFile.ps1' 44
#Region '.\public\Get-KelvinInstanceSetting.ps1' -1

<#
.SYNOPSIS
    Gets instance settings from Kelvin.
.DESCRIPTION
    Retrieves instance settings from the currently connected Kelvin instance.
#>

Function Get-KelvinInstanceSetting {
    [OutputType('Kelvin.InstanceSetting')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0)]
        [string[]] $Search,

        [Parameter()]
        [Alias('setting_name')]
        [string[]] $Name,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'instance/settings/list' -Method Get -TypeName 'Kelvin.InstanceSetting' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent && $_.name) {
                $ret = (Invoke-KelvinApi "instance/settings/$($_.name)/get" -Method Get -TypeName 'Kelvin.InstanceSetting')
            }
            else {
                $ret = $_
            }
            Write-Output $ret
        }
    }
}
#EndRegion '.\public\Get-KelvinInstanceSetting.ps1' 38
#Region '.\public\Get-KelvinInstanceStatus.ps1' -1

<#
.SYNOPSIS
    Gets the status of the Kelvin instance.
.DESCRIPTION
    Retrieves the current status of the connected Kelvin instance.
#>

Function Get-KelvinInstanceStatus {
    [OutputType('Kelvin.InstanceStatus')]
    [CmdletBinding()]
    Param()

    Process {
        $params = _GetParams

        Invoke-KelvinApi "instance/status/get" -Method Get -TypeName 'Kelvin.InstanceStatus' -Parameters $params `
        | ForEach-Object {
            Write-Output $_
        }
    }
}
#EndRegion '.\public\Get-KelvinInstanceStatus.ps1' 21
#Region '.\public\Get-KelvinParameterDefinition.ps1' -1

<#
.SYNOPSIS
    Gets parameters definitions from Kelvin.
.DESCRIPTION
    Retrieves parameter definitions from the currently connected Kelvin instance.
#>

Function Get-KelvinParameterDefinition {
    [OutputType('Kelvin.ParameterDefinition')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [string[]] $Name,

        [Parameter(ParameterSetName = 'Query')]
        [string[]] $Type
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'parameters/definitions/list' -Method Get -TypeName 'Kelvin.ParameterDefinition' -Parameters $params `
        | ForEach-Object {
            Write-Output $_
        }
    }
}
#EndRegion '.\public\Get-KelvinParameterDefinition.ps1' 31
#Region '.\public\Get-KelvinParameterResource.ps1' -1

<#
.SYNOPSIS
    Gets parameter resources from Kelvin.
.DESCRIPTION
    Retrieves parameter resources from the currently connected Kelvin instance.
#>

Function Get-KelvinParameterResource {
    [OutputType('Kelvin.ParameterResource')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [string[]] $Name,

        [Parameter(ParameterSetName = 'Query')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [Alias('resource_name')]
        [string[]] $ResourceName
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'parameters/resources/list' -Method Get -TypeName 'Kelvin.ParameterResource' -Parameters $params `
        | ForEach-Object {
            Write-Output $_
        }
    }
}
#EndRegion '.\public\Get-KelvinParameterResource.ps1' 33
#Region '.\public\Get-KelvinRecommendation.ps1' -1

<#
.SYNOPSIS
    Gets recommendations from Kelvin.
.DESCRIPTION
    Retrieves recommendations from the currently connected Kelvin instance.
#>

Function Get-KelvinRecommendation {
    [OutputType('Kelvin.Recommendation')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [Alias('recommendation_id')]
        [string[]] $Id,

        [Parameter(ParameterSetName = 'Query')]
        [string[]] $Type,

        [Parameter(ParameterSetName = 'Query')]
        [ValidateSet('accepted', 'rejected', 'pending')]
        [string[]] $Status,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'recommendations/list' -Method Get -TypeName 'Kelvin.Recommendation' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "recommendations/$($_.id)/get" -Method Get -TypeName 'Kelvin.Recommendation')
            }
            else {
                $ret = $_
            }
            Write-Output $ret
        }
    }
}
#EndRegion '.\public\Get-KelvinRecommendation.ps1' 45
#Region '.\public\Get-KelvinRecommendationType.ps1' -1

<#
.SYNOPSIS
    Gets recommendation types from Kelvin.
.DESCRIPTION
    Retrieves recommendation types from the currently connected Kelvin instance.
#>

Function Get-KelvinRecommendationType {
    [OutputType('Kelvin.RecommendationType')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [string[]] $Name,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'recommendations/types/list' -Method Get -TypeName 'Kelvin.RecommendationType' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "recommendations/types/$($_.name)/get" -Method Get -TypeName 'Kelvin.RecommendationType')
            }
            else {
                $ret = $_
            }
            Write-Output $ret
        }
    }
}
#EndRegion '.\public\Get-KelvinRecommendationType.ps1' 37
#Region '.\public\Get-KelvinSecret.ps1' -1

<#
.SYNOPSIS
    Gets secrets from Kelvin.
.DESCRIPTION
    Retrieves secrets from the currently connected Kelvin instance.
#>

Function Get-KelvinSecret {
    [OutputType('Kelvin.Secret')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [Alias('secret_name')]
        [string[]] $Name
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'secrets/list' -Method Get -TypeName 'Kelvin.Secret' -Parameters $params `
        | ForEach-Object {
            Write-Output $_
        }
    }
}
#EndRegion '.\public\Get-KelvinSecret.ps1' 29
#Region '.\public\Get-KelvinThread.ps1' -1

<#
.SYNOPSIS
    Gets threads from Kelvin.
.DESCRIPTION
    Retrieves threads from the currently connected Kelvin instance.
#>

Function Get-KelvinThread {
    [OutputType('Kelvin.Thread')]
    [CmdletBinding()]
    Param
    (
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        [Parameter(ParameterSetName = 'Query')]
        [Alias('thread_id')]
        [string[]] $Id,

        [Parameter(ParameterSetName = 'Query')]
        [string[]] $Title,

        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams

        Invoke-KelvinApi 'threads/list' -Method Get -TypeName 'Kelvin.Thread' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "threads/$($_.id)/get" -Method Get -TypeName 'Kelvin.Thread')
            }
            else {
                $ret = $_
            }
            Write-Output $ret
        }
    }
}
#EndRegion '.\public\Get-KelvinThread.ps1' 41
#Region '.\public\Get-KelvinWorkload.ps1' -1

<#
.SYNOPSIS
    Get a list of workloads.
 
.DESCRIPTION
    This function retrieves a list of workloads from the Kelvin API. It allows for filtering based on various parameters such as name, application name, cluster name, and more.
#>

Function Get-KelvinWorkload {
    [CmdletBinding()]
    [OutputType('Kelvin.Workload')]
    Param 
    (
        # Free-form text search across workload fields
        [Parameter(Position = 0, ParameterSetName = 'Query')]
        [string[]] $Search,

        # Filter workloads by name
        [Parameter(ParameterSetName = 'Query')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [Alias('workload_name')]
        [string[]] $Name,

        # Filter workloads by application name
        [Parameter(ParameterSetName = 'Query')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [Alias('app_name')]
        [string[]] $AppName,

        # Filter workloads by application version
        [Parameter(ParameterSetName = 'Query')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [Alias('app_version')]
        [string[]] $AppVersion,

        # Filter workloads by cluster name
        [Parameter(ParameterSetName = 'Query', ValueFromPipelineByPropertyName = $true)]
        [Alias('cluster_name')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [string[]] $ClusterName,

        # Filter workloads by node name
        [Parameter(ParameterSetName = 'Query')]
        [Alias('node_name')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [string[]] $NodeName,

        # Filter workloads by enabled status
        [Parameter(ParameterSetName = 'Query')]
        [bool] $Enabled,

        # Filter workloads by associated asset name
        [Parameter(ParameterSetName = 'Query')]
        [ValidatePattern('^[a-z0-9][-_.a-z0-9]*[a-z0-9]$')]
        [Alias('asset_name')]
        [string] $AssetName,

        # Filter workloads by staged status
        [Parameter(ParameterSetName = 'Query')]
        [bool] $Staged,

        # Filter workloads by download status: pending, scheduled, processing, ready, or failed
        [Parameter(ParameterSetName = 'Query')]
        [ValidateSet('pending', 'scheduled', 'processing', 'ready', 'failed')]
        [Alias('download_status')]
        [string[]] $DownloadStatus,

        # Return detailed workload information by making an additional API call for each workload
        [Parameter()]
        [switch] $Detailed
    )

    Process {
        $params = _GetParams $PSBoundParameters @{
            search          = $Search
            app_name        = $AppName
            app_version     = $AppVersion
            cluster_name    = $ClusterName
            node_name       = $NodeName
            enabled         = $Enabled
            asset_name      = $AssetName
            staged          = $Staged
            download_status = $DownloadStatus
        }

        # Call the Kelvin API to list workloads
        Invoke-KelvinApi 'workloads/list' -Method Get -TypeName 'Kelvin.Workload' -Parameters $params `
        | ForEach-Object {
            if ($Detailed.IsPresent) {
                $ret = (Invoke-KelvinApi "workloads/$($_.name)/get" -Method Get -TypeName 'Kelvin.Workload')
            }
            else {
                $ret = $_
            }
            Write-Output $ret | Add-Member -Name 'state' -Value $_.status.state -MemberType NoteProperty -PassThru
        }
    }
}
#EndRegion '.\public\Get-KelvinWorkload.ps1' 98
#Region '.\public\Invoke-KelvinApi.ps1' -1

function Invoke-KelvinApi {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$Path,

        [Parameter(Position = 1)]
        [hashtable]$Parameters,

        [Parameter()]
        [ValidateSet("GET", "POST", "PUT", "DELETE", "PATCH")]
        [string]$Method = 'GET',

        [Parameter()]
        [object]$Body,

        [Parameter()]
        [string]$ContentType = "application/json",

        [Parameter()]
        [string] $TypeName
    )

    if (-not $script:KelvinURL -or -not $script:KelvinToken) {
        throw "Connect-KelvinAccount must be called before invoking any API commands."
    }

    $url = "$script:KelvinURL/$Path"
    Write-verbose "Invoking API at $url"

    if ($Body -is [string] -or $Body -is [int] -or $Body -is [double]) {
        $requestBody = $Body
        Write-Verbose "Using string body: $requestBody"
    }
    elseif ($null -ne $Body) {
        $requestBody = $Body | ConvertTo-Json -Depth 10
        Write-Verbose "Converting body to JSON: $requestBody"
    }

    if ($Parameters.Count -gt 0) {
        $urlBuilder = [UriBuilder] $url
        $urlBuilder.Query = _GetQuery $Parameters 
        $url = $urlBuilder.Uri
        Write-Verbose "URL after adding query parameters: $url"
    }

    return _GetPaginatedData -Uri $url `
        -Method $Method `
        -Body $requestBody `
        -ContentType $ContentType `
        -Headers @{ Authorization = "Bearer $script:KelvinToken" } `
        -TypeName $TypeName
}

Function _GetQuery($parameters) {
    $query = @()
    foreach ($key in $parameters.Keys) {
        if ($parameters[$key] -is [array]) {
            foreach ($value in $parameters[$key]) {
                $query += "$key=$value"
            }
        }
        else {
            $query += "$key=$($parameters[$key])"
        }
    }
    return [string]::Join('&', $query)
}
#EndRegion '.\public\Invoke-KelvinApi.ps1' 69