StatusCake-Helpers.psm1


<#
.SYNOPSIS
   Removes the StatusCake Authentication Username and API Key
.DESCRIPTION
   Removes the StatusCake Authentication Username and API Key credential file used by the module. If session switch is used this will remove the credentials added for the session.
.PARAMETER Session
   Switch to remove credentials configured for the session
.EXAMPLE
   C:\PS> Remove-StatusCakeHelperAPIAuth
   Remove the StatusCake Authentication credential file
.EXAMPLE
   C:\PS> Remove-StatusCakeHelperAPIAuth -Session
   Remove the StatusCake Authentication credential configured for the session
.OUTPUTS
   Returns a Boolean value on whether authentication removal operation was successful
.LINK
   https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/Authentication/Remove-StatusCakeHelperAPIAuth.md
#>

function Remove-StatusCakeHelperAPIAuth
{
   [CmdletBinding(SupportsShouldProcess=$true)]
   [OutputType([System.Boolean])]
   Param(
      [Switch]$Session
   )

   If($Session)
   {
      if($PSDefaultParameterValues.ContainsKey("Get-StatusCakeHelperAPIAuth:Credential"))
      {
         $PSDefaultParameterValues.Remove("Get-StatusCakeHelperAPIAuth:Credential")
      }
   }
   else
   {
      Try
      {
         $moduleName = (Get-Command $MyInvocation.MyCommand.Name).Source
         Remove-Item "$env:userprofile\.$moduleName\$moduleName-Credentials.xml" -Force
      }
      Catch
      {
         Write-Error $_
         Return $false
      }
   }


    Return $true
}
<#
.SYNOPSIS
    Sets the StatusCake API Username and API Key
.DESCRIPTION
    Sets the StatusCake API Username and API Key used by the module. Credential file will be stored in the user's profile folder under .StatusCake-Helpers folder.
    If the credential file already exists then the existing credential file will be overwritten otherwise a credential file will be created.
    To avoid persisting credentials to disk the session switch can be used.
.PARAMETER APIKey
    Statuscake API Key
.PARAMETER Credential
    Credential object containing the username and API Key
.PARAMETER Session
    Switch to configure the credential for the session only and avoid writing them to disk
.EXAMPLE
    C:\PS> Set-StatusCakeHelperAPIAuth -APIKey (Read-Host -AsSecureString -Prompt "Please enter the API key")
    Set the StatusCake Authentication credential file
.EXAMPLE
    C:\PS> Set-StatusCakeHelperAPIAuth -APIKey (Read-Host -AsSecureString -Prompt "Please enter the API key") -Session
    Set the StatusCake Authentication credential for the session
.INPUTS
    Credential - Credential object containing the username and API Key
.OUTPUTS
    Returns a Boolean value on whether the authentication credential was successfully set
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/Authentication/Set-StatusCakeHelperAPIAuth.md
#>

function Set-StatusCakeHelperAPIAuth
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    [OutputType([System.Boolean])]
    Param(
        [Parameter(Mandatory=$true,ParameterSetName = "APIKey")]
        [ValidateNotNullOrEmpty()]
        [securestring] $APIKey,

        [Parameter(Mandatory=$true,ParameterSetName = "Credential")]
        [ValidateNotNullOrEmpty()]
        [Alias('Credentials')]
        [System.Management.Automation.PSCredential] $Credential,

        [Switch]$Session
    )

    # If API key provided create a credential object from this
    If($APIKey)
    {
        $scCredentials = New-Object System.Management.Automation.PSCredential ("StatusCake API Key", $APIKey)
    }
    else
    {
        $scCredentials = $Credential
    }

    if($Session)
    {
        $PSDefaultParameterValues["Get-StatusCakeHelperAPIAuth:Credential"] = $scCredentials
    }
    else
    {
        Try
        {
            $moduleName = (Get-Command $MyInvocation.MyCommand.Name).Source
            If(! (Test-Path "$env:userprofile\.$moduleName\"))
            {
                New-Item "$env:userprofile\.$moduleName" -ItemType Directory | Out-Null
            }
            $scCredentials | Export-CliXml -Path "$env:userprofile\.$moduleName\$moduleName-Credentials.xml"
        }
        Catch
        {
            Write-Error $_
            Return $false
        }
    }

    Return $true
}


<#
.SYNOPSIS
   Tests whether StatusCake API Credentials have been configured
.DESCRIPTION
   Returns a boolean value depending on whether StatusCake API Credentials file for the module exists
.EXAMPLE
   C:\PS> Test-StatusCakeHelperAPIAuthSet
   Test whether the credential file exists
.OUTPUTS
   Returns a boolean value
.LINK
   https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/Authentication/Test-StatusCakeHelperAPIAuthSet.md
#>

function Test-StatusCakeHelperAPIAuthSet
{
   $moduleName = (Get-Command $MyInvocation.MyCommand.Name).Source
   Return (Test-Path "$env:userprofile\.$moduleName\$moduleName-Credentials.xml" -PathType Leaf)
}

<#
.SYNOPSIS
    Gets a StatusCake Contact Group, Maintenance Window, PageSpeed, SSL or Uptime item
.DESCRIPTION
    Retrieves a StatusCake item by type. If no name or id is supplied all items of that type are returned.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the item to retrieve
.PARAMETER Type
    Type of item to retrieve
.PARAMETER Parameter
    Hashtable of parameters for the item. The keys and values in the hashtable should match the parameters expected by the StatusCake API.
.PARAMETER Limit
    Number of items to retrieve per page
.EXAMPLE
    C:\PS>Get-StatusCakeHelperItem -Type "PageSpeed"
    Retrieve all pagespeed tests
.EXAMPLE
    C:\PS>Get-StatusCakeHelperItem -Type "SSL" -ID 1234
    Retrieve ssl test information for a test with ID 1234
#>

function Get-StatusCakeHelperItem
{
    [CmdletBinding(PositionalBinding=$false,DefaultParameterSetName='All')]
    [OutputType([System.Collections.Generic.List[PSObject]])]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID,

        [Parameter(Mandatory=$true)]
        [ValidateSet("PageSpeed","SSL","Uptime","Contact-Groups","Maintenance-Windows")]
        [string]$Type,

        [ValidateNotNullOrEmpty()]
        [hashtable]$Parameter,

        [ValidateRange(1,100)]
        [int]$Limit=25
    )

    $requestURL = "https://api.statuscake.com/v1/$($Type.ToLower())"
    $requestParams = @{
        Headers = @{"Authorization"="Bearer $($APICredential.GetNetworkCredential().password)"}
        Method = "Get"
        ContentType = "application/x-www-form-urlencoded"
        UseBasicParsing = $true
    }

    $itemData = [System.Collections.Generic.List[PSObject]]::new()
    do
    {
        try{
            if($ID)
            {
                $requestParams["uri"] = "$requestURL/$ID"
                $response = Invoke-RestMethod @requestParams
                $itemData = $response.data
            }
            else
            {
                $page =1
                if($Parameter)
                {
                    $Parameter["limit"] = $Limit
                }
                else
                {
                    $Parameter = @{"limit" = $Limit}
                }

                do{
                    $Parameter["page"] = $page
                    $queryParameters = $Parameter | ConvertTo-StatusCakeHelperXW3FormUrlEncoded
                    $requestParams["uri"] = "$requestURL`?$queryParameters"
                    $response = Invoke-RestMethod @requestParams
                    $response.data | ForEach-Object{$itemData.Add($_)}
                    $page++

                }
                while($page -le $response.metadata.page_count)
            }
            $finished = $true
        }
        catch [System.Net.WebException] {
            if($_.Exception.Response.StatusCode -eq 429)
            {
                $retryDelay = [int] ($_.Exception.Response.Headers["x-ratelimit-reset"])
                Write-Verbose "Rate limit exceeded [$($_.Exception.Response.Headers["x-ratelimit-limit"])], retrying in [$retryDelay] seconds"
                Start-Sleep -Seconds $retryDelay
            }
            else
            {
                $errorResponse = $_.Exception.Response.GetResponseStream() | Get-StatusCakeHelperAPIErrorResponse
                Write-Error "A web exception was caught: [$($_.Exception.Message)] / [$($errorResponse.Message)] / [$($errorResponse.Errors -join ",")]"
                throw $_
            }

        }
    }while ($finished -ne $true)

    $requestParams = @{}
    Return $itemData
}

<#
.SYNOPSIS
    Gets the metadata for a StatusCake Pagespeed or Uptime test
.DESCRIPTION
    Retrieves the metadata for a StatusCake item by type.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the item
.PARAMETER Type
    Type of item to retrieve
.PARAMETER Property
    Type of metadata to retrieve
.PARAMETER Parameter
    Hashtable of parameters to be used for the request
.PARAMETER PageLimit
    The number of results to return per page
.PARAMETER ResultLimit
    The maximum number of results to return
.EXAMPLE
    C:\PS>Get-StatusCakeHelperItemMetadata -Type "PageSpeed" -Property History -Name "Pagespeed Test"
    Retrieve pagespeed test history for test with name PageSpeed Test
.EXAMPLE
    C:\PS>Get-StatusCakeHelperItemMetadata -Type "Uptime" -Property Alerts -Name "Example Uptime Speed Test"
    Retrieve details of alerts sent for a Uptime test called "Example Page Speed Test"
#>

function Get-StatusCakeHelperItemMetadata
{
    [CmdletBinding(PositionalBinding=$false,DefaultParameterSetName='All')]
    [OutputType([System.Collections.Generic.List[PSObject]])]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID,

        [Parameter(Mandatory=$true)]
        [ValidateSet("PageSpeed","Uptime")]
        [string]$Type,

        [Parameter(Mandatory=$true)]
        [ValidateSet("History","Periods","Alerts")]
        [string]$Property,

        [ValidateNotNullOrEmpty()]
        [hashtable]$Parameter,

        [ValidateRange(1,100)]
        [int]$PageLimit=25,

        [ValidateNotNullOrEmpty()]
        [int]$ResultLimit
    )

    $requestParams = @{
        uri = "https://api.statuscake.com/v1/$($Type.ToLower())/$ID/$($Property.ToLower())"
        Headers = @{"Authorization"="Bearer $($APICredential.GetNetworkCredential().password)"}
        Method = "Get"
        UseBasicParsing = $true
    }

    #If the maximum of results requested is less than the API limit change the page limit to match the maximum number of results requested
    if($ResultLimit -gt 0 -and $ResultLimit -le 100)
    {
        $PageLimit = $ResultLimit
    }

    If($Parameter)
    {
        $Parameter.Add("limit",$PageLimit)
    }
    else
    {
        $Parameter = @{"limit" = $PageLimit}
    }
    $requestParams["Body"] = $Parameter

    $results = 0
    $itemData = [System.Collections.Generic.List[PSObject]]::new()
    do
    {
        try{
            do{
                $response = Invoke-RestMethod @requestParams
                foreach($item in $response.data)
                {
                    $itemData.Add($item)
                }
                $requestParams.Remove("Body")
                $requestParams["uri"] = $response.links.next
                $results += ($response.data).Count

                #If the number of results exceeds the requested amount exit out of the loop
                if($ResultLimit)
                {
                    if($results -ge $ResultLimit)
                    {
                        break
                    }
                    elseif(($ResultLimit - $results) -le 100)
                    {   #If the number of remaining results to retrieve is less than the API limit adjust the limit accordingly to retrieve the requested number
                        $requestParams["Body"] = @{"limit" = ($ResultLimit - $results) }
                    }

                }
            }while($response.links.next)
            $finished = $true
        }
        catch [System.Net.WebException] {
            if($_.Exception.Response.StatusCode -eq 429)
            {
                $retryDelay = [int] ($_.Exception.Response.Headers["x-ratelimit-reset"])
                Write-Verbose "Rate limit exceeded [$($_.Exception.Response.Headers["x-ratelimit-limit"])], retrying in [$retryDelay] seconds"
                Start-Sleep -Seconds $retryDelay
            }
            else
            {
                $errorResponse = $_.Exception.Response.GetResponseStream() | Get-StatusCakeHelperAPIErrorResponse
                Write-Error "A web exception was caught: [$($_.Exception.Message)] / [$($errorResponse.Message)] / [$($errorResponse.Errors -join ",")]"
                throw $_
            }

        }
    }while ($finished -ne $true)

    Return $itemData
}


<#
.SYNOPSIS
    Creates a StatusCake Contact Group, Maintenance Window, PageSpeed, SSL or Uptime item
.DESCRIPTION
    Creates a StatusCake Contact Group, Maintenance Window, PageSpeed, SSL or Uptime item and returns the ID. Use the passthru switch to return the details of the test created.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER Type
    Type of item to create
.PARAMETER Parameter
    Hashtable of parameters to create the item. The keys and values in the hashtable should match the parameters expected by the StatusCake API
.PARAMETER PassThru
    Return the details of the object created. By default only the ID is returned.
.EXAMPLE
    C:\PS>New-StatusCakeHelperItem -Type "pagespeed" -Parameter @{"name"="Example Page Speed Test"; "website_url"="https://www.example.com"; "check_rate"=86400; "region"="US"}
    Create a pagespeed test called "Example Page Speed Test" to check https://www.example.com every day from the US region
.EXAMPLE
    C:\PS>New-StatusCakeHelperItem -Type "ssl" -Parameter @{"website_url"="https://www.example.com"; "check_rate"=86400}
    Create a ssl test to check https://www.example.com every day
.EXAMPLE
    C:\PS>New-StatusCakeHelperItem -Type "uptime" -Parameter @{"name"="Example Uptime Speed Test";"test_type"="HTTP" "website_url"="https://www.example.com"; "check_rate"=86400}
    Create a uptime test called "Example Uptime Speed Test" to perform a HTTP check to test https://www.example.com every day
.EXAMPLE
    C:\PS>New-StatusCakeHelperItem -Type "contact-groups" -Parameter @{"name"="Example Contact Group"}
    Create a contact group called "Example Contact Group"
.EXAMPLE
    C:\PS>New-StatusCakeHelperItem -Type "maintenance-window" -Parameter @{"name"="Example Contact Group";"start_at"="2022-01-01T00:30:00Z";"end_at"="2022-01-01T01:30:00Z";"timezone"="UTC"}
    Create a maintenance window called "Example Maintenance Window" starting at 1st January 2022 00:30 UTC until 1st January 2022 01:30 UTC
#>

function New-StatusCakeHelperItem
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(Mandatory=$true)]
        [ValidateSet("PageSpeed","SSL","Uptime","Contact-Groups","Maintenance-Windows")]
        [string]$Type,

        [hashtable]$Parameter,

        [switch]$PassThru
    )

    $requestParams = @{
        uri = "https://api.statuscake.com/v1/$($Type.ToLower())"
        Headers = @{"Authorization"="Bearer $($APICredential.GetNetworkCredential().password)"}
        UseBasicParsing = $true
        Method = "Post"
        ContentType = "application/x-www-form-urlencoded"
        Body = $Parameter | ConvertTo-StatusCakeHelperXW3FormUrlEncoded
    }

    if( $pscmdlet.ShouldProcess("$($requestParams["uri"])", "$($requestParams["Method"]) request to") )
    {
        do{
            try{
                $id = (Invoke-RestMethod @requestParams).data.new_id
                $finished = $true
            }
            catch [System.Net.WebException] {
                if($_.Exception.Response.StatusCode -eq 429)
                {
                    $retryDelay = [int] ($_.Exception.Response.Headers["x-ratelimit-reset"])
                    Write-Verbose "Rate limit exceeded [$($_.Exception.Response.Headers["x-ratelimit-limit"])], retrying in [$retryDelay] seconds"
                    Start-Sleep -Seconds $retryDelay
                }
                else
                {
                    $errorResponse = $_.Exception.Response.GetResponseStream() | Get-StatusCakeHelperAPIErrorResponse
                    Write-Error "A web exception was caught: [$($_.Exception.Message)] / [$($errorResponse.Message)] / [$($errorResponse.Errors -join ",")]"
                    throw $_
                }
            }
        }while ($finished -ne $true)
        $requestParams = @{}
        if($PassThru)
        {
            Return Get-StatusCakeHelperItem -ID $id -Type $Type -APICredential $APICredential
        }
        else
        {
            Return $id
        }
    }
}


<#
.SYNOPSIS
    Remove a StatusCake Contact Group, Maintenance Window, PageSpeed, SSL or Uptime item
.DESCRIPTION
    Remove a StatusCake Contact Group, Maintenance Window, PageSpeed, SSL or Uptime item
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the test
.PARAMETER Type
    Type of test to remove
.EXAMPLE
    C:\PS>Remove-StatusCakeHelperItem -Type "PageSpeed" -Name "Example Page Speed Test"
    Remove a pagespeed test with name "Example Page Speed Test"
.EXAMPLE
    C:\PS>Remove-StatusCakeHelperItem -Type "SSL" -ID 1234
    Remove a ssl speed test with ID 1234
#>

function Remove-StatusCakeHelperItem
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID,

        [Parameter(Mandatory=$true)]
        [ValidateSet("PageSpeed","SSL","Uptime","Contact-Groups","Maintenance-Windows")]
        [string]$Type
    )

    $requestParams = @{
        uri = "https://api.statuscake.com/v1/$($Type.ToLower())/$ID"
        Headers = @{"Authorization"="Bearer $($APICredential.GetNetworkCredential().password)"}
        UseBasicParsing = $true
        method = "Delete"
    }

    if( $pscmdlet.ShouldProcess("$($requestParams["uri"])", "$($requestParams["Method"]) request to") )
    {
        do{
            try{
                Invoke-RestMethod @requestParams
                $finished = $true
            }
            catch [System.Net.WebException] {
                if($_.Exception.Response.StatusCode -eq 429)
                {
                    $retryDelay = [int] ($_.Exception.Response.Headers["x-ratelimit-reset"])
                    Write-Verbose "Rate limit exceeded [$($_.Exception.Response.Headers["x-ratelimit-limit"])], retrying in [$retryDelay] seconds"
                    Start-Sleep -Seconds $retryDelay
                }
                else
                {
                    $errorResponse = $_.Exception.Response.GetResponseStream() | Get-StatusCakeHelperAPIErrorResponse
                    Write-Error "A web exception was caught: [$($_.Exception.Message)] / [$($errorResponse.Message)] / [$($errorResponse.Errors -join ",")]"
                    throw $_
                }

            }
        }while ($finished -ne $true)
        $requestParams = @{}
    }
}


<#
.SYNOPSIS
    Update a StatusCake item
.DESCRIPTION
    Update a StatusCake Contact Group, Maintenance Window, PageSpeed, SSL or Uptime item
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the test
.PARAMETER Type
    Type of test to remove
.PARAMETER Parameter
    Hashtable of parameters to use to update the StatusCake item
.EXAMPLE
    C:\PS>Update-StatusCakeHelperItem -Type "PageSpeed" -ID 12345 -Parameter @{"check_rate"="3600"}
    Update a pagespeed test called "Example Page Speed Test" to check every hour
.EXAMPLE
    C:\PS>Update-StatusCakeHelperItem -Type "SSL" -ID 12345 -Parameter @{"check_rate"=86400}
    Update a ssl test to check the SSL certificate of https://www.example.com every day
.EXAMPLE
    C:\PS>Update-StatusCakeHelperItem -Type "Uptime" ID 12345 -Parameter @{"name"="New Example Uptime Speed Test"}
    Update a uptime test called "Example Uptime Speed Test" and change it's name to "New Example Uptime Speed Test"
.EXAMPLE
    C:\PS>Update-StatusCakeHelperItem -Type "Contact-Groups" ID 12345 -Parameter @{"email_addresses"="postmaster@example.com"}
    Update a contact group called "Example Contact Group" to use the email address postmaster@example.com
.EXAMPLE
    C:\PS>Update-StatusCakeHelperItem -Type "Maintenance-Window" -ID 12345 -Parameter @{"repeat_interval"=2w}
    Update a maintenance window with ID 12345 to have a repeat interval of every 2 weeks
#>

function Update-StatusCakeHelperItem
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID,

        [Parameter(Mandatory=$true)]
        [ValidateSet("PageSpeed","SSL","Uptime","Contact-Groups","Maintenance-Windows")]
        [string]$Type,

        [hashtable]$Parameter
    )

    $requestParams = @{
        uri = "https://api.statuscake.com/v1/$($Type.ToLower())/$ID"
        Headers = @{"Authorization"="Bearer $($APICredential.GetNetworkCredential().password)"}
        UseBasicParsing = $true
        Method = "Put"
        ContentType = "application/x-www-form-urlencoded"
        Body = $Parameter | ConvertTo-StatusCakeHelperXW3FormUrlEncoded
    }

    if( $pscmdlet.ShouldProcess("$($requestParams["uri"])", "$($requestParams["Method"]) request to") )
    {
        do{
            try{
                Invoke-RestMethod @requestParams
                $finished = $true
            }
            catch [System.Net.WebException] {
                if($_.Exception.Response.StatusCode -eq 429)
                {
                    $retryDelay = [int] ($_.Exception.Response.Headers["x-ratelimit-reset"])
                    Write-Verbose "Rate limit exceeded [$($_.Exception.Response.Headers["x-ratelimit-limit"])], retrying in [$retryDelay] seconds"
                    Start-Sleep -Seconds $retryDelay
                }
                else
                {
                    $errorResponse = $_.Exception.Response.GetResponseStream() | Get-StatusCakeHelperAPIErrorResponse
                    Write-Error "A web exception was caught: [$($_.Exception.Message)] / [$($errorResponse.Message)] / [$($errorResponse.Errors -join ",")]"
                    throw $_
                }

            }
        }while ($finished -ne $true)
        $requestParams = @{}
    }
}


<#
.SYNOPSIS
    Clears a property of a StatusCake contact group
.DESCRIPTION
    Clears the property of a StatusCake contact group using the supplied parameters.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the contact group to clear the property on
.PARAMETER Name
    Name of the Contact Group on which to clear the property
.PARAMETER Email
    Flag to clear all email addresses from contact group
.PARAMETER PingURL
    Flag to clear ping URL from contact group
.PARAMETER Mobile
    Flag to clear all mobile numbers from contact group
.PARAMETER Integration
    Flag to clear all integration IDs from contact group
.EXAMPLE
    C:\PS>Clear-StatusCakeHelperContactGroup -Name "Example" -email
    Set the contact group name "Example" with email address "test@example.com"
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/ContactGroups/Clear-StatusCakeHelperContactGroup.md
#>

function Clear-StatusCakeHelperContactGroup
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName='ByID')]
        [ValidateNotNullOrEmpty()]
        [int]$ID,

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

        [Alias("email_addresses")]
        [switch]$Email,

        [switch]$PingURL,

        [Alias("mobile_numbers")]
        [switch]$Mobile,

        [Alias("integrations")]
        [switch]$Integration
    )

    if($ByName -and $Name)
    {
        if( $pscmdlet.ShouldProcess("StatusCake API", "Retrieve StatusCake ContactGroups"))
        {
            $contactGroupCheck = Get-StatusCakeHelperContactGroup -APICredential $APICredential -Name $Name
            if(!$contactGroupCheck)
            {
                Write-Error "Unable to find Contact Group with specified name [$Name]"
                Return $null
            }
            elseif($contactGroupCheck.GetType().Name -eq 'Object[]')
            {
                Write-Error "Multiple Contact Groups with the same name [$Name]"
                Return $null
            }
            $ContactID = $contactGroupCheck.ID
        }
    }

    $exclude=@("ByName")
    $clear = @()

    foreach($parameter in $PSBoundParameters.GetEnumerator())
    {
        $variableValue = Get-Variable -Name $parameter.Key -ValueOnly
        if($variableValue.GetType().Name-eq "SwitchParameter" -and $variableValue -eq $true)
        {
            $clear += $parameter.Key
        }
    }
    $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
    $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude $exclude -Clear $clear
    $statusCakeAPIParams = $statusCakeAPIParams | ConvertTo-StatusCakeHelperAPIParameter

    $requestParams = @{
        uri = "https://api.statuscake.com/v1/contact-groups/$ContactID"
        Headers = @{"Authorization"="Bearer $($APICredential.GetNetworkCredential().password)"}
        UseBasicParsing = $true
        method = "Put"
        ContentType = "application/x-www-form-urlencoded"
        body = $statusCakeAPIParams
    }

    if( $pscmdlet.ShouldProcess("StatusCake API", "Update StatusCake ContactGroup") )
    {
        try{
            Invoke-RestMethod @requestParams
        }
        catch [System.Net.WebException] {
            Write-Verbose "A web exception was caught: [$($_.Exception.Message)] / [$($_.Exception.Response.StatusCode)] / [$($_.Exception.Response.StatusDescription)]"
        }

    }

}

<#
.SYNOPSIS
    Copies the settings of a StatusCake ContactGroup
.DESCRIPTION
    Creates a copy of a contact group which can be specified by name or id. The new name of the contact group must not already exist for the command to be successful.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER Name
    Name of the Contact Group to be copied
.PARAMETER ID
    ID of the Contact Group to be copied
.PARAMETER NewName
    Name of the copy of the Contact Group
.EXAMPLE
    C:\PS> Copy-StatusCakeHelperContactGroup -Name "Example" -NewName "Example - Copy"
    Create a copy of a contact group called "Example" with name "Example - Copy"
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/ContactGroups/Copy-StatusCakeHelperContactGroup.md
#>

function Copy-StatusCakeHelperContactGroup
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [Parameter(ParameterSetName='CopyByName')]
        [Parameter(ParameterSetName='CopyById')]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName='CopyById',Mandatory=$true)]
        [int]$ID,

        [Parameter(ParameterSetName='CopyByName',Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(ParameterSetName='CopyByName',Mandatory=$true)]
        [Parameter(ParameterSetName='CopyById',Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$NewName
    )

    if($Name)
    {   #If copying by name check if resource with that name exists
        if( $pscmdlet.ShouldProcess("StatusCake API", "Retrieve StatusCake Contact Groups"))
        {
            $statusCakeItem = Get-StatusCakeHelperContactGroup -APICredential $APICredential -Name $Name
            if(!$statusCakeItem)
            {
                Write-Error "No Contact with Specified Name Exists [$Name]"
                Return $null
            }
            elseif($statusCakeItem.GetType().Name -eq 'Object[]')
            {
                Write-Error "Multiple Contacts with the same name [$Name] [$($statusCakeItem.ID)]"
                Return $null
            }
        }
    }
    elseif($ID)
    {   #If copying by ID verify that a resource with the Id already exists
        if( $pscmdlet.ShouldProcess("StatusCake API", "Retrieve StatusCake Contacts"))
        {
            $statusCakeItem = Get-StatusCakeHelperContactGroup -APICredential $APICredential -ID $ID
            if(!$statusCakeItem)
            {
                Write-Error "No Contact with Specified ID Exists [$ID]"
                Return $null
            }
        }
    }

    $psParams = @{}
    $psParams = $statusCakeItem | Get-StatusCakeHelperCopyParameter -FunctionName "New-StatusCakeHelperContactGroup"

    Write-Verbose "$($psParams.Keys -join ",")"

    $psParams.Name = $NewName

    if( $pscmdlet.ShouldProcess("StatusCake API", "Create StatusCake Contact Group"))
    {
        $result = New-StatusCakeHelperContactGroup -APICredential $APICredential @psParams
    }
    Return $result
}
<#
.SYNOPSIS
    Retrieves a StatusCake Contact Group with a specific name or Test ID
.DESCRIPTION
    Retrieves a StatusCake contact group via the name or ID. If no name or id supplied all contact groups will be returned.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER Name
    Name of the Contact Group
.PARAMETER ID
    ID of the Contact Group to be retrieved
.EXAMPLE
    C:\PS>Get-StatusCakeHelperContactGroup
    Retrieve all contact groups
.EXAMPLE
    C:\PS>Get-StatusCakeHelperContactGroup -ID 123456
    Retrieve contact group with ID 123456
.OUTPUTS
    Returns the contact group(s)
        id : 123456
        name : Test Contact Group
        email_addresses : {test@example.com}
        mobile_numbers : {}
        integrations : {}
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/ContactGroups/Get-StatusCakeHelperContactGroup.md
.LINK
    https://www.statuscake.com/api/v1/#tag/contact-groups/operation/list-contact-groups
.LINK
    https://www.statuscake.com/api/v1/#tag/contact-groups/operation/get-contact-group
#>

function Get-StatusCakeHelperContactGroup
{
    [CmdletBinding(PositionalBinding=$false,DefaultParameterSetName='all')]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "Group Name")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(ParameterSetName = "Contact ID")]
        [Alias("GroupID","ContactID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID
    )

    if($Name)
    {
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "Contact-Groups" | Where-Object{$_.name -eq $Name}
    }
    elseif($ID)
    {
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "Contact-Groups" -ID $ID
    }
    else
    {
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "Contact-Groups"
    }
    Return $statusCakeItem
}

<#
.SYNOPSIS
    Create a StatusCake ContactGroup
.DESCRIPTION
    Creates a new StatusCake ContactGroup using the supplied parameters. The name of the contact group must be unique for the contact group to be created.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER Name
    Name of the Contact Group to be created
.PARAMETER Email
    Array of email addresses to sent alerts to.
.PARAMETER IntegrationID
    List of integration IDs to link with this contact group
.PARAMETER PingURL
    URL or IP address of an endpoint to push uptime events. Currently this only supports HTTP GET endpoints
.PARAMETER Mobile
    Array of mobile numbers in International Format E.164 notation
.PARAMETER Force
    Force creation of the contact group even if a window with the same name already exists
.PARAMETER Passthru
    Return the contact group details instead of the contact id
.EXAMPLE
    C:\PS>New-StatusCakeHelperContactGroup -Name "Example" -email @(test@example.com)
    Create contact group called "Example" using email address "test@example.com"
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/ContactGroups/New-StatusCakeHelperContactGroup.md
.LINK
    https://www.statuscake.com/api/v1/#tag/contact-groups/operation/create-contact-group
#>

function New-StatusCakeHelperContactGroup
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [ValidateScript({
            if(!($_ | Test-StatusCakeHelperEmailAddress)){
                Throw "Invalid email address format supplied [$_]"
            }
            else{$true}
        })]
        [Alias("email_addresses")]
        [string[]]$Email,

        [Alias("integrations")]
        [int[]]$IntegrationID,

        [ValidatePattern('^((http|https):\/\/)([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)')]
        [Alias("ping_url")]
        [string]$PingURL,

        [ValidateScript({
            if(!($_ | Test-StatusCakeHelperMobileNumber)){
                Throw "Mobile number is not in E.164 format [$_]"
            }
            else{$true}
        })]
        [Alias("mobile_numbers")]
        [string[]]$Mobile,

        [switch]$Force,

        [switch]$PassThru
    )

    #If force flag not set then check if an existing test with the same name already exists
    if(!$Force)
    {
       $statusCakeItem = Get-StatusCakeHelperContactGroup -APICredential $APICredential -Name $Name
       if($statusCakeItem)
       {
            Write-Error "Existing contact group(s) found with name [$Name]. Please use a different name or use the -Force argument"
            Return $null
       }
    }

    $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
    $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude @("Force","PassThru") | ConvertTo-StatusCakeHelperAPIValue

    if( $pscmdlet.ShouldProcess("StatusCake API", "Create StatusCake contact group") )
    {
        Return (New-StatusCakeHelperItem -APICredential $APICredential -Type Contact-Groups -Parameter $statusCakeAPIParams -PassThru:$PassThru)
    }

}


<#
.SYNOPSIS
    Removes the specified StatusCake contact group
.DESCRIPTION
    Removes the StatusCake contact group via its ID or Name.
.PARAMETER APICredential
    Username and APIKey Credentials to access StatusCake API
.PARAMETER ID
    ID of the contact group to be removed
.PARAMETER Name
    Name of the Contact Group to be removed
.EXAMPLE
    C:\PS>Remove-StatusCakeHelperContactGroup -ID 123456
    Remove contact group with ID 123456
.OUTPUTS
    Returns the result of the contact group removal as an object
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/ContactGroups/Remove-StatusCakeHelperContactGroup.md
.LINK
    https://www.statuscake.com/api/v1/#tag/contact-groups/operation/delete-contact-group
#>

function Remove-StatusCakeHelperContactGroup
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ContactID")]
        [ValidateNotNullOrEmpty()]
        [Alias("group_id","GroupID","ContactID")]
        [int]$ID,

        [Parameter(ParameterSetName = "GroupName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )

    if($Name)
    {
       $statusCakeItem = Get-StatusCakeHelperContactGroup -APICredential $APICredential -Name $Name
       if(!$statusCakeItem)
       {
            Write-Error "No contact group(s) found with name [$Name]"
            Return $null
       }
       elseif($statusCakeItem.GetType().Name -eq 'Object[]')
       {
           Write-Error "Multiple contact group found with name [$Name]. Please delete the contact group by ID"
           Return $null
       }
       $ID = $statusCakeItem.id
    }

    if( $pscmdlet.ShouldProcess("$ID", "Delete StatusCake contact group") )
    {
        Return (Remove-StatusCakeHelperItem -APICredential $APICredential -Type Contact-Groups -ID $ID)
    }
}

<#
.SYNOPSIS
    Updates the configuration of a StatusCake ContactGroup
.DESCRIPTION
    Updates a StatusCake ContactGroup using the supplied parameters. Values supplied overwrite existing values.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER Name
    Name of the Contact Group to be created
.PARAMETER ID
    ID of the contact group to set
.PARAMETER Email
    Array of email addresses to sent alerts to.
.PARAMETER PingURL
    URL To Send a POST alert
.PARAMETER Mobile
    Array of mobile number in International Format E.164 notation
.PARAMETER IntegrationID
    List of integration IDs to link with this contact group
.EXAMPLE
    C:\PS>Update-StatusCakeHelperContactGroup -Name "Example" -email @(test@example.com)
    Set the contact group name "Example" with email address "test@example.com"
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/ContactGroups/Update-StatusCakeHelperContactGroup.md
.LINK
    https://www.statuscake.com/api/v1/#tag/contact-groups/operation/update-contact-group
#>

function Update-StatusCakeHelperContactGroup
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [Alias("group_id","GroupID","ContactID")]
        [int]$ID,

        [Parameter(ParameterSetName = "Name")]
        [ValidateNotNullOrEmpty()]
        [Alias("GroupName")]
        [string]$Name,

        [ValidateScript({
            if(!($_ | Test-StatusCakeHelperEmailAddress)){
                Throw "Invalid email address format supplied [$_]"
            }
            else{$true}
        })]
        [Alias("email_addresses")]
        [string[]]$Email,

        [Alias("integrations")]
        [int[]]$IntegrationID,

        [ValidatePattern('^((http|https):\/\/)([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)')]
        [Alias("ping_url")]
        [string]$PingURL,

        [ValidateScript({
            if(!($_ | Test-StatusCakeHelperMobileNumber)){
                Throw "Mobile number is not in E.164 format [$_]"
            }
            else{$true}
        })]
        [Alias("mobile_numbers")]
        [string[]]$Mobile
    )

    if($Name)
    {
       $statusCakeItem = Get-StatusCakeHelperContactGroup -APICredential $APICredential -Name $Name
       if(!$statusCakeItem)
       {
            Write-Error "No contact group(s) found with name [$Name]"
            Return $null
       }
       elseif($statusCakeItem.GetType().Name -eq 'Object[]')
       {
           Write-Error "Multiple contact groups found with name [$Name]. Please update the contact group by ID"
           Return $null
       }
       $ID = $statusCakeItem.id
    }

    $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
    $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude @("Force","Name")  | ConvertTo-StatusCakeHelperAPIValue

    if( $pscmdlet.ShouldProcess("$ID", "Update StatusCake contact group") )
    {
        Return (Update-StatusCakeHelperItem -APICredential $APICredential -Type "Contact-Groups" -ID $ID -Parameter $statusCakeAPIParams)
    }

}
<#
.SYNOPSIS
   Retrieve the details of the StatusCake locations from StatusCake
.DESCRIPTION
   Retrieves details of StatusCake locations from StatusCake
.PARAMETER APICredential
   Credentials to access StatusCake API
.PARAMETER RegionCode
   The StatusCake region code to return locations for. All locations returned if no region code supplied.
.PARAMETER Type
   The type of test to retrieve locations for. Options are Uptime or Pagespeed. Default is Uptime
.PARAMETER Best
   Return only locations with the least number of tests. Default is false.
.EXAMPLE
   C:\PS>Get-StatusCakeHelperLocation -Type Uptime-Locations
   Retrieve all StatusCake uptime locations
.EXAMPLE
   C:\PS>Get-StatusCakeHelperLocation -Type Uptime-Locations -Location GBR -Best
   Retrieve all StatusCake uptime locations in Great Britian with the least number of checks
.EXAMPLE
   C:\PS>Get-StatusCakeHelperLocation -Type PageSpeed-Locations
   Retrieve all StatusCake pagespeed locations
.OUTPUTS
   StatusCakeLocations - Object containing details of StatusCake Locations
      description : Australia, Sydney - 1
      region : Australia / Sydney
      region_code : sydney
      status : up
      ipv4 : 45.76.123.211
.LINK
   https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/Locations/Get-StatusCakeHelperLocation.md
.LINK
   https://www.statuscake.com/api/v1/#tag/locations
#>

function Get-StatusCakeHelperLocation
{
   [CmdletBinding(PositionalBinding=$false,DefaultParameterSetName='All')]
   Param(
      [ValidateNotNullOrEmpty()]
      [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

      [ValidateNotNullOrEmpty()]
      [Alias("region_code")]
      [string]$RegionCode,

      [ValidateSet("Uptime-Locations","PageSpeed-Locations")]
      [string]$Type="Uptime-Locations",

      [ValidateNotNullOrEmpty()]
      [bool]$Best
   )

      $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
      $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude @("Type")
      $statusCakeAPIParams = $statusCakeAPIParams | ConvertTo-StatusCakeHelperAPIValue

      $requestParams = @{
         uri = "https://api.statuscake.com/v1/$($Type.ToLower())"
         Headers = @{"Authorization"="Bearer $($APICredential.GetNetworkCredential().password)"}
         UseBasicParsing = $true
         Method = "Get"
         Body = $statusCakeAPIParams
      }
      do{
         try {
            $locationList = Invoke-RestMethod @requestParams
            $finished = $true
         }
         catch [System.Net.WebException] {
            if($_.Exception.Response.StatusCode -eq 429)
            {
               $retryDelay = [int] ($_.Exception.Response.Headers["x-ratelimit-reset"])
               Write-Verbose "Rate limit exceeded [$($_.Exception.Response.Headers["x-ratelimit-limit"])], retrying in [$retryDelay] seconds"
               Start-Sleep -Seconds $retryDelay
            }
            else
            {
               $errorResponse = $_.Exception.Response.GetResponseStream() | Get-StatusCakeHelperAPIErrorResponse
               Write-Error "A web exception was caught: [$($_.Exception.Message)] / [$($errorResponse.Message)] / [$($errorResponse.Errors -join ",")]"
               throw $_
            }
         }
      }while ($finished -ne $true)
   Return $locationList.data
}


<#
.SYNOPSIS
    Clears the tests associated with a StatusCake Maintenance Window
.DESCRIPTION
    Clears the tests and/or tags associated with a pending StatusCake Maintenance Window. You can only clear the test IDs/tags for a window which is in a pending state.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER Name
    Name of the maintenance window to clear the tests from
.PARAMETER ID
    The maintenance window ID
.PARAMETER UptimeID
    Flag to clear all tests included in a maintenance window
.PARAMETER UptimeTag
    Flag to clear all tags of the tests to be included in a maintenance window
.EXAMPLE
    C:\PS>Clear-StatusCakeHelperMaintenanceWindow -ID 123456 -TestIDs
    Clear all test IDs associated with maintenance window 123456
.EXAMPLE
    C:\PS>Clear-StatusCakeHelperMaintenanceWindow -ID 123456 -TestTags
    Clear all test tags associated with maintenance window 123456
#>

function Clear-StatusCakeHelperMaintenanceWindow
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName='ByID',Mandatory=$true)]
        [Parameter(ParameterSetName='TestIDs',Mandatory=$true)]
        [Parameter(ParameterSetName='TestTags',Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$ID,

        [Parameter(ParameterSetName='ByName',Mandatory=$true)]
        [Parameter(ParameterSetName='TestIDs',Mandatory=$true)]
        [Parameter(ParameterSetName='TestTags',Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(ParameterSetName='TestIDs',Mandatory=$true)]
        [Parameter(ParameterSetName='ByID')]
        [Parameter(ParameterSetName='ByName')]
        [Alias('tests','raw_tests','TestIDs')]
        [switch]$UptimeID,

        [Parameter(ParameterSetName='TestTags',Mandatory=$true)]
        [Parameter(ParameterSetName='ByID')]
        [Parameter(ParameterSetName='ByName')]
        [Alias('tags','raw_tags','TestTags')]
        [switch]$UptimeTag
    )

    if($Name)
    {
       $statusCakeItem = Get-StatusCakeHelperMaintenanceWindow -APICredential $APICredential -Name $Name
       if(!$statusCakeItem)
       {
            Write-Error "No maintenance window(s) found with name [$Name]"
            Return $null
       }
       elseif($statusCakeItem.GetType().Name -eq 'Object[]')
       {
           Write-Error "Multiple maintenance windows found with name [$Name]. Please update the maintenance window by ID"
           Return $null
       }
       $ID = $statusCakeItem.id
    }

    if($UptimeID)
    {
        $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
        $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude @("Name","ID") -Clear "UptimeID"
    }
    elseif($UptimeTag)
    {
        $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
        $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude @("Name","ID") -Clear "UptimeTag"
    }
    Write-Verbose "[$($allParameterValues.keys)] [$($allParameterValues.values)]"
    Write-Verbose "[$($statusCakeAPIParams.keys)] [$($statusCakeAPIParams.values)]"
    if( $pscmdlet.ShouldProcess("StatusCake API", "Clear Value From StatusCake Test"))
    {
        Return (Update-StatusCakeHelperItem -APICredential $APICredential -Type Maintenance-Windows -ID $ID -Parameter $statusCakeAPIParams)
    }
}

<#
.SYNOPSIS
    Gets a StatusCake Maintenance Window
.DESCRIPTION
    Retrieves StatusCake Maintenance Windows. If no id or name is supplied all maintenance windows are returned. Results can be filtered by maintenance window state.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER Name
    Name of the maintenance window to retrieve
.PARAMETER ID
    ID of the maintenance window to retrieve
.PARAMETER State
    Filter results based on state. Valid options are pending, active, and paused
.EXAMPLE
    C:\PS>Get-StatusCakeHelperMaintenanceWindow
    Get all maintenance windows
.EXAMPLE
    C:\PS>Get-StatusCakeHelperMaintenanceWindow -State pending
    Get all maintenance windows in a pending state
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/MaintenanceWindows/Get-StatusCakeHelperMaintenanceWindow.md
.LINK
    https://www.statuscake.com/api/v1/#operation/list-maintenance-windows
.LINK
    https://www.statuscake.com/api/v1/#operation/get-maintenance-window
.OUTPUTS
    Returns StatusCake Maintenance Windows as an object
#>

function Get-StatusCakeHelperMaintenanceWindow
{
    [CmdletBinding(PositionalBinding=$false,DefaultParameterSetName='all')]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "all")]
        [ValidateSet("active","paused","pending")]
        [string]$State,

        [Parameter(ParameterSetName = "Name")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID
    )

    if($State)
    {
        $parameter = $State | ConvertTo-StatusCakeHelperAPIValue
        $itemParameters["Parameter"] = $parameter
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "Maintenance-Windows" -Parameter $parameter
    }
    elseif($Name)
    {
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "Maintenance-Windows" | Where-Object{$_.name -eq $Name}
    }
    elseif($ID)
    {
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "Maintenance-Windows" -ID $ID
    }
    else
    {
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "Maintenance-Windows"
    }

    Return $statusCakeItem

}

<#
.SYNOPSIS
    Create a StatusCake Maintenance Window
.DESCRIPTION
    Creates a new StatusCake Maintenance Window using the supplied parameters. Tests can be included in the maintenance window by either supplying the uptime test IDs or uptime test tags.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER Name
    A descriptive name for the maintenance window
.PARAMETER StartDate
    Start date of the maintenance window.
.PARAMETER EndDate
    End time of your window. Must be in the future
.PARAMETER Timezone
    Must be a valid timezone, or UTC
.PARAMETER UptimeID
    Array of uptime test IDs that should be included
.PARAMETER UptimeTag
    Array of uptime test tags with these tags will be included
.PARAMETER RepeatInterval
    How often this window should occur
.PARAMETER Force
    Force creation of the maintenance window even if a window with the same name already exists
.PARAMETER Passthru
    Return the maintenance window details instead of the maintenance window id
.EXAMPLE
    C:\PS>New-StatusCakeHelperMaintenanceWindow -Name "Example Maintenance Window" -StartDate $(Get-Date) -EndDate $((Get-Date).AddHours(1)) -Timezone "Europe/London" -TestIDs @("123456")
    Create a maintenance window called "Example Maintenance Window" starting today and ending in one hour in the Europe/London timezone for test ID 123456
.EXAMPLE
    C:\PS>New-StatusCakeHelperMaintenanceWindow -Name "Example Maintenance Window" -StartDate $(Get-Date) -EndDate $((Get-Date).AddHours(1)) -Timezone "Europe/London" -TestTags @("Tag1","Tag2")
    Create a maintenance window called "Example Maintenance Window" starting today and ending in one hour in the Europe/London timezone including tests which have tags "Tag1" and "Tag2"
.EXAMPLE
    C:\PS>New-StatusCakeHelperMaintenanceWindow -Name "Example Maintenance Window" -StartDate $(Get-Date) -EndDate $((Get-Date).AddHours(1)) -Timezone "Europe/London" -TestIDs @("123456") -RecurEvery 7
    Create a maintenance window called "Example Maintenance Window" starting today and ending in one hour in the Europe/London timezone for test ID 123456 recurring every 7 day
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/MaintenanceWindows/New-StatusCakeHelperMaintenanceWindow.md
.LINK
    https://www.statuscake.com/api/v1/#tag/maintenance-windows/operation/create-maintenance-window
#>

function New-StatusCakeHelperMaintenanceWindow
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(Mandatory=$true)]
        [Alias('start_at','start_date','start_unix')]
        [datetime]$StartDate,

        [Parameter(Mandatory=$true)]
        [Alias('end_at','end_date','end_unix')]
        [datetime]$EndDate,

        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Timezone,

        [ValidateNotNullOrEmpty()]
        [Alias('tests','raw_tests','TestIDs')]
        [int[]]$UptimeID,

        [ValidateNotNullOrEmpty()]
        [Alias('tags','raw_tags','TestTags')]
        [string[]]$UptimeTag,

        [ValidateSet("never","1d","1w","2w","1m")]
        [Alias('repeat_interval')]
        [string]$RepeatInterval,

        [switch]$Force,

        [switch]$PassThru
    )

    #If force flag not set then check if an existing item with the same name already exists
    if(!$Force)
    {
       $statusCakeItem = Get-StatusCakeHelperMaintenanceWindow -APICredential $APICredential -Name $Name
       if($statusCakeItem)
       {
            Write-Error "Existing maintenance window(s) found with name [$Name]. Please use a different name for the check or use the -Force argument"
            Return $null
       }
    }

    $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
    $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude @("Force","PassThru") | ConvertTo-StatusCakeHelperAPIValue

    if( $pscmdlet.ShouldProcess("StatusCake API", "Create StatusCake Maintenance Window") )
    {
        Return (New-StatusCakeHelperItem -APICredential $APICredential -Type Maintenance-Windows -Parameter $statusCakeAPIParams -PassThru:$PassThru)
    }

}

<#
.SYNOPSIS
   Remove a StatusCake Maintenance Window
.DESCRIPTION
    Deletes a StatusCake Maintenance Window using the supplied ID or name.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    The maintenance window ID
.PARAMETER Name
    Name of the maintenance window to remove
.EXAMPLE
    C:\PS>Remove-StatusCakeHelperMaintenanceWindow -ID 123456
    Remove the maintenance window with id 123456
.EXAMPLE
    C:\PS>Remove-StatusCakeHelperMaintenanceWindow -ID 123456 -Name "Example Maintenance Window"
    Remove the maintenance window with name "Example Maintenance Window"
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/MaintenanceWindows/Remove-StatusCakeHelperMaintenanceWindow.md
.LINK
    https://www.statuscake.com/api/v1/#tag/maintenance-windows/operation/delete-maintenance-window
#>

function Remove-StatusCakeHelperMaintenanceWindow
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [int]$ID,

        [Parameter(ParameterSetName = "name")]
        [string]$Name
    )

    if($Name)
    {
       $statusCakeItem = Get-StatusCakeHelperMaintenanceWindow -APICredential $APICredential -Name $Name
       if(!$statusCakeItem)
       {
            Write-Error "No maintenance window(s) found with name [$Name]"
            Return $null
       }
       elseif($statusCakeItem.GetType().Name -eq 'Object[]')
       {
           Write-Error "Multiple maintenance windows found with name [$Name]. Please delete the maintenance window by ID"
           Return $null
       }
       $ID = $statusCakeItem.id
    }

    if( $pscmdlet.ShouldProcess("$ID", "Remove StatusCake Maintenance Window") )
    {
        Return (Remove-StatusCakeHelperItem -APICredential $APICredential -Type Maintenance-Windows -ID $ID)
    }
}

<#
.SYNOPSIS
    Updates a StatusCake Maintenance Window
.DESCRIPTION
    Updates the configuration of StatusCake Maintenance Window using the supplied parameters. You can only update a window which is in a pending state.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER Name
    A descriptive name for the maintenance window
.PARAMETER ID
    The maintenance window ID
.PARAMETER StartDate
    Start date of your window. Can be slightly in the past
.PARAMETER EndDate
    End time of your window. Must be in the future
.PARAMETER Timezone
    Must be a valid timezone, or UTC
.PARAMETER UptimeID
    Array of uptime test IDs that should be included
.PARAMETER UptimeTag
    Array of uptime test tags with these tags will be included
.PARAMETER RepeatInterval
    How often in days this window should occur
.EXAMPLE
    C:PS>Update-StatusCakeHelperMaintenanceWindow -ID 123456 -RepeatInterval 1m
    Modify the maintenance window with ID 123456 to recur every month
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/MaintenanceWindows/Update-StatusCakeHelperMaintenanceWindow.md
.LINK
    https://www.statuscake.com/api/v1/#tag/maintenance-windows/operation/update-maintenance-window
#>

function Update-StatusCakeHelperMaintenanceWindow
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName='ID')]
        [ValidateNotNullOrEmpty()]
        [string]$ID,

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

        [Alias('start_at','start_date','start_unix')]
        [datetime]$StartDate,

        [Alias('end_at','end_date','end_unix')]
        [datetime]$EndDate,

        [ValidateScript({$_ | Test-StatusCakeHelperTimeZone})]
        [string]$Timezone,

        [Alias('tests','raw_tests','TestIDs')]
        [int[]]$UptimeID,

        [Alias('tags','raw_tags','TestTags')]
        [string[]]$UptimeTag,

        [ValidateSet("never","1d","1w","2w","1m")]
        [Alias('repeat_interval')]
        [string]$RepeatInterval

    )

    if($Name)
    {
       $statusCakeItem = Get-StatusCakeHelperMaintenanceWindow -APICredential $APICredential -Name $Name
       if(!$statusCakeItem)
       {
            Write-Error "No maintenance window(s) found with name [$Name]"
            Return $null
       }
       elseif($statusCakeItem.GetType().Name -eq 'Object[]')
       {
           Write-Error "Multiple maintenance windows found with name [$Name]. Please update the maintenance window by ID"
           Return $null
       }
       $ID = $statusCakeItem.id
    }

    $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
    $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude @("Force","Name","ID")  | ConvertTo-StatusCakeHelperAPIValue

    if( $pscmdlet.ShouldProcess("$ID", "Update StatusCake Maintenance Window") )
    {
        Return (Update-StatusCakeHelperItem -APICredential $APICredential -Type Maintenance-Windows -ID $ID -Parameter $statusCakeAPIParams)
    }

}

<#
.SYNOPSIS
    Copies the settings of a StatusCake Page Speed Test
.DESCRIPTION
    Creates a copy of a Page Speed Test. Supply a value for the WebsiteURL parameter to override the source URL. A region is required as StatusCake API does not return the region the tests are carried out from.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the Page Speed Test to be copied
.PARAMETER Name
    Name of the Page Speed Test to be copied
.PARAMETER NewName
    Name of the Page Speed Test copy
.PARAMETER WebsiteURL
    Name of the URL to be used in the copy of the test
.PARAMETER Region
    StatusCake region the test should carried out from.
.EXAMPLE
    C:\PS>Copy-StatusCakeHelperPageSpeedTest -Name "Example" -NewName "Example - Copy" -Region UK
    Creates a copy of a page speed test called "Example" with name "Example - Copy"
.EXAMPLE
    C:\PS>Copy-StatusCakeHelperPageSpeedTest -Name "Example" -NewName "Example - Copy" -WebsiteURL "https://www.example.org" -Region UK
    Creates a copy of a page speed test called "Example" with name "Example - Copy" using the URL "https://www.example.org"
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/PageSpeed/Copy-StatusCakeHelperPageSpeedTest.md
#>

function Copy-StatusCakeHelperPageSpeedTest
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [Parameter(ParameterSetName='CopyByName')]
        [Parameter(ParameterSetName='CopyById')]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName='CopyById',Mandatory=$true)]
        [int]$ID,

        [Parameter(ParameterSetName='CopyByName',Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(ParameterSetName='CopyByName',Mandatory=$true)]
        [Parameter(ParameterSetName='CopyById',Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$NewName,

        [Parameter(ParameterSetName='CopyByName')]
        [Parameter(ParameterSetName='CopyById')]
        [ValidatePattern('^((http|https):\/\/)?([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))*$')]
        [Alias('website_url')]
        [string]$WebsiteURL,

        [Parameter(Mandatory=$true)]
        [ValidateSet("AU","CA","DE","FR","IN","JP","NL","SG","UK","US","USW")]
        [string]$Region
    )

    if($Name)
    {   #If copying by name check if resource with that name exists
        if( $pscmdlet.ShouldProcess("$Name", "Retrieve StatusCake Page Speed Test Details"))
        {
            $statusCakeItem = Get-StatusCakeHelperPageSpeedTest -APICredential $APICredential -Name $Name
            if(!$statusCakeItem)
            {
                Write-Error "No Page Speed Test with Specified Name Exists [$Name]"
                Return $null
            }
            elseif($statusCakeItem.GetType().Name -eq 'Object[]')
            {
                Write-Error "Multiple Page Speed Tests with the same name [$Name] [$($statusCakeItem.ID)]"
                Return $null
            }
        }
    }
    elseif($ID)
    {   #If copying by ID verify that a resource with the Id already exists
        if( $pscmdlet.ShouldProcess("$ID", "Retrieve StatusCake Page Speed Test Details"))
        {
            $statusCakeItem = Get-StatusCakeHelperPageSpeedTest -APICredential $APICredential -id $ID
            if(!$statusCakeItem)
            {
                Write-Error "No Page Speed Test with Specified ID Exists [$ID]"
                Return $null
            }
        }
    }

    $psParams = @{}
    $psParams = $statusCakeItem | Get-StatusCakeHelperCopyParameter -FunctionName "New-StatusCakeHelperPageSpeedTest"

    Write-Verbose "$($psParams.Keys -join ",")"

    $psParams.Name = $NewName
    if($WebsiteURL)
    {
        $psParams.WebsiteURL = $WebsiteURL
    }

    if( $pscmdlet.ShouldProcess("StatusCake API", "Create StatusCake Page Speed Test"))
    {
        $result = New-StatusCakeHelperPageSpeedTest -APICredential $APICredential @psParams -Region $Region
    }
    Return $result
}

<#
.SYNOPSIS
    Gets a StatusCake PageSpeed Test
.DESCRIPTION
    Retrieves a StatusCake PageSpeed Test. If no name or id is supplied all tests are returned.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the PageSpeed Test
.PARAMETER Name
    Name of the PageSpeed test
.EXAMPLE
    C:\PS>Get-StatusCakeHelperPageSpeedTest
    Retrieve all page speed tests
.EXAMPLE
    C:\PS>Get-StatusCakeHelperPageSpeedTest -Name "Example Page Speed Test"
    Retrieve page speed test information for a test called "Example Page Speed Test"
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/PageSpeed/Get-StatusCakeHelperPageSpeedTest.md
.LINK
    https://www.statuscake.com/api/v1/#tag/pagespeed/operation/get-pagespeed-test
.LINK
    https://www.statuscake.com/api/v1/#operation/get-maintenance-window
#>

function Get-StatusCakeHelperPageSpeedTest
{
    [CmdletBinding(PositionalBinding=$false,DefaultParameterSetName='All')]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID,

        [Parameter(ParameterSetName = "Name")]
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )

    if($Name)
    {
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "PageSpeed" | Where-Object{$_.name -eq $Name}
    }
    elseif($ID)
    {
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "PageSpeed" -ID $ID
    }
    else
    {
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "PageSpeed"
    }
    Return $statusCakeItem
}


<#
.SYNOPSIS
    Gets the history of a StatusCake PageSpeed Test
.DESCRIPTION
    Retrieves the history for a StatusCake PageSpeed Test. Use the Days parameter to specify the number of days which should be retrieved.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the PageSpeed Test
.PARAMETER Name
    Name of the PageSpeed test
.PARAMETER Before
    Return only results from before this date
.PARAMETER Limit
    The maximum of number of results to return
.EXAMPLE
    C:\PS>Get-StatusCakeHelperPageSpeedTestHistory -ID 123456
    Retrieve the page speed test history for page speed test with id 123456
.EXAMPLE
    C:\PS>Get-StatusCakeHelperPageSpeedTestHistory -ID 123456 -Before 2022-01-01
    Retrieve all page speed test history before the 1st January 2022
.EXAMPLE
    C:\PS>Get-StatusCakeHelperPageSpeedTestHistory -ID 123456 -Limit 1
    Retrieve the history of the most recent page speed test
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/PageSpeed/Get-StatusCakeHelperPageSpeedTestHistory.md
.LINK
    https://www.statuscake.com/api/v1/#tag/pagespeed/operation/list-pagespeed-test-history
.OUTPUTS
    Returns a StatusCake PageSpeed Tests History as an object
#>

function Get-StatusCakeHelperPageSpeedTestHistory
{
    [CmdletBinding(PositionalBinding=$false)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID,

        [Parameter(ParameterSetName = "name")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [ValidateNotNullOrEmpty()]
        [datetime]$Before,

        [ValidateNotNullOrEmpty()]
        [int]$Limit
    )

    # If name supplied find the ID of the test
    if($Name)
    {
        $statusCakeItem = Get-StatusCakeHelperPageSpeedTest -APICredential $APICredential -Name $Name
        $ID = $statusCakeItem.id
    }

    $metaDataParameters = @{
        APICredential = $APICredential
        Type = "PageSpeed"
        Property = "History"
        ID = $ID
    }

    if($Before)
    {
        $parameter = $Before | ConvertTo-StatusCakeHelperAPIValue -DateUnix @("Before")
        $metaDataParameters["Parameter"] = $parameter
    }

    if($Limit)
    {
        $metaDataParameters["ResultLimit"] = $Limit
    }

    Return (Get-StatusCakeHelperItemMetadata @metaDataParameters)

}


<#
.SYNOPSIS
    Create a StatusCake PageSpeed Test
.DESCRIPTION
    Creates a new StatusCake PageSpeed Test using the supplied parameters.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER Name
    Name for PageSpeed test
.PARAMETER WebsiteURL
    URL that should be checked
.PARAMETER Checkrate
    Number of seconds between checks
.PARAMETER AlertBigger
    An alert will be sent if the size of the page is larger than this value (kb). A value of 0 prevents alerts being sent.
.PARAMETER AlertSlower
    Time in ms, will alert to Contact Groups if actual time is slower
.PARAMETER AlertSmaller
    Size in kb, will alert to Contact Groups if actual size is smaller
.PARAMETER ContactID
    IDs of selected Contact Groups to alert
.PARAMETER Region
    2-letter code of the region from which to run the checks. Valid values: AU, CA, DE, FR, IN, JP, NL, SG, UK, US, USW
.PARAMETER Paused
    Whether the test should be run.
.PARAMETER Force
    Force creation of the test even if a test with the same name already exists
.PARAMETER Passthru
    Return the page speed test details instead of the page speed test id
.EXAMPLE
    C:\PS>New-StatusCakeHelperPageSpeedTest -WebsiteURL "https://www.example.com" -Checkrate 60 Region UK -AlertSlower 10000
    Create a page speed test to check site "https://www.example.com" every 60 minutes from a UK test server and alert when page speed load time is slower than 10000ms
.EXAMPLE
    C:\PS>New-StatusCakeHelperPageSpeedTest -WebsiteURL "https://www.example.com" -Checkrate 60 -Region UK -AlertSmaller 500
    Create a page speed test to check site "https://www.example.com" every 60 minutes from a UK test server and alert when page load is less than 500kb
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/PageSpeed/New-StatusCakeHelperPageSpeedTest.md
.LINK
    https://www.statuscake.com/api/v1/#tag/pagespeed/operation/create-pagespeed-test
#>

function New-StatusCakeHelperPageSpeedTest
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(Mandatory=$true)]
        [ValidatePattern('^((http|https):\/\/)([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)')]
        [Alias('website_url')]
        [string]$WebsiteURL,

        [Parameter(Mandatory=$true)]
        [ValidateSet("60","300","600","900","1800","3600","86400")]
        [Alias('check_rate')]
        [int]$Checkrate,

        [Alias('alert_bigger')]
        [int]$AlertBigger=0,

        [Alias('alert_slower')]
        [int]$AlertSlower=0,

        [Alias('alert_smaller')]
        [int]$AlertSmaller=0,

        [Alias('contact_groups')]
        [int[]]$ContactID,

        [Parameter(Mandatory=$true)]
        [ValidateSet("AU","CA","DE","FR","IN","JP","NL","SG","UK","US","USW")]
        [string]$Region,

        [boolean]$Paused,

        [switch]$Force,

        [switch]$PassThru
    )

    #If force flag not set then check if an existing test with the same name already exists
    if(!$Force)
    {
       $statusCakeItem = Get-StatusCakeHelperPageSpeedTest -APICredential $APICredential -Name $Name
       if($statusCakeItem)
       {
            Write-Error "Existing pagespeed test(s) found with name [$Name]. Please use a different name for the check or use the -Force argument"
            Return $null
       }
    }

    $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
    $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude @("Force","PassThru") | ConvertTo-StatusCakeHelperAPIValue

    if( $pscmdlet.ShouldProcess("StatusCake API", "Create StatusCake Pagespeed Check") )
    {
        Return (New-StatusCakeHelperItem -APICredential $APICredential -Type PageSpeed -Parameter $statusCakeAPIParams -PassThru:$PassThru)
    }
}

<#
.SYNOPSIS
    Remove a StatusCake PageSpeed Test
.DESCRIPTION
    Deletes a StatusCake PageSpeed Test using the supplied ID or name.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the PageSpeed Test to remove
.PARAMETER Name
    Name for PageSpeed test
.EXAMPLE
    C:\PS>Remove-StatusCakeHelperPageSpeedTest -ID 123456
    Remove page speed test with id 123456
.EXAMPLE
    C:\PS>Remove-StatusCakeHelperPageSpeedTest -Name "Example PageSpeed Test"
    Remove page speed test with name "Example PageSpeed Test"
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/PageSpeed/Remove-StatusCakeHelperPageSpeedTest.md
.LINK
    https://www.statuscake.com/api/v1/#tag/pagespeed/operation/delete-pagespeed-test
#>

function Remove-StatusCakeHelperPageSpeedTest
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [int]$ID,

        [Parameter(ParameterSetName = "Name")]
        [string]$Name
    )

    if($Name)
    {
       $statusCakeItem = Get-StatusCakeHelperPageSpeedTest -APICredential $APICredential -Name $Name
       if(!$statusCakeItem)
       {
            Write-Error "No PageSpeed test(s) found with name [$Name]"
            Return $null
       }
       elseif($statusCakeItem.GetType().Name -eq 'Object[]')
       {
           Write-Error "Multiple PageSpeed Tests found with name [$Name]. Please delete the PageSpeed test by ID"
           Return $null
       }
       $ID = $statusCakeItem.id
    }

    if( $pscmdlet.ShouldProcess("$ID", "Delete StatusCake Pagespeed Test") )
    {
        Return (Remove-StatusCakeHelperItem -APICredential $APICredential -Type PageSpeed -ID $ID)
    }
}

<#
.SYNOPSIS
    Updates a StatusCake PageSpeed Test
.DESCRIPTION
    Updates a new StatusCake PageSpeed Test by either its name or ID.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of PageSpeed test to update
.PARAMETER Name
    Name of PageSpeed test to update
.PARAMETER WebsiteURL
    URL that should be checked
.PARAMETER Checkrate
    Number of seconds between checks
.PARAMETER AlertBigger
    An alert will be sent if the size of the page is larger than this value (kb). A value of 0 prevents alerts being sent.
.PARAMETER AlertSlower
    Time in ms, will alert to Contact Groups if actual time is slower. A value of 0 prevents alerts being sent.
.PARAMETER AlertSmaller
    Size in kb, will alert to Contact Groups if actual size is smaller. A value of 0 prevents alerts being sent.
.PARAMETER ContactID
    IDs of selected Contact Groups to alert
.PARAMETER Region
    Statuscake region from which to run the checks. Valid values: AU, CA, DE, FR, IN, JP, NL, SG, UK, US, USW
.PARAMETER Paused
    Whether the test should be run.
.EXAMPLE
    C:\PS>Update-StatusCakeHelperPageSpeedTest -WebsiteURL "https://www.example.com" -Checkrate 60 -Region UK -AlertSlower 10000
    Create a page speed test to check site "https://www.example.com" every 60 minutes from a UK test server and alert when page speed load time is slower than 10000ms
.EXAMPLE
    C:\PS>Update-StatusCakeHelperPageSpeedTest -WebsiteURL "https://www.example.com" -Checkrate 60 -Region UK -AlertSmaller 500
    Create a page speed test to check site "https://www.example.com" every 60 minutes from a UK test server and alert when page load is less than 500kb
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/PageSpeed/Update-StatusCakeHelperPageSpeedTest.md
.LINK
    https://www.statuscake.com/api/v1/#tag/pagespeed/operation/update-pagespeed-test
#>

function Update-StatusCakeHelperPageSpeedTest
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [int]$ID,

        [Parameter(ParameterSetName = "Name")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [ValidatePattern('^((http|https):\/\/)([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)')]
        [Alias('website_url')]
        [string]$WebsiteURL,

        [ValidateSet("60","300","600","900","1800","3600","86400")]
        [Alias('check_rate')]
        [int]$Checkrate,

        [Alias('alert_bigger')]
        [ValidateNotNullOrEmpty()]
        [int]$AlertBigger,

        [Alias('alert_slower')]
        [ValidateNotNullOrEmpty()]
        [int]$AlertSlower,

        [Alias('alert_smaller')]
        [ValidateNotNullOrEmpty()]
        [int]$AlertSmaller,

        [Alias('contact_groups')]
        [ValidateNotNullOrEmpty()]
        [int[]]$ContactID,

        [ValidateSet("AU","CA","DE","FR","IN","JP","NL","SG","UK","US","USW")]
        [string]$Region,

        [boolean]$Paused
    )

    if($Name)
    {
       $statusCakeItem = Get-StatusCakeHelperPageSpeedTest -APICredential $APICredential -Name $Name
       if(!$statusCakeItem)
       {
            Write-Error "No PageSpeed test(s) found with name [$Name]"
            Return $null
       }
       elseif($statusCakeItem.GetType().Name -eq 'Object[]')
       {
           Write-Error "Multiple PageSpeed Tests found with name [$Name]. Please update the PageSpeed test by ID"
           Return $null
       }
       $ID = $statusCakeItem.id
    }

    $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
    $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude @("Force","Name")  | ConvertTo-StatusCakeHelperAPIValue

    if( $pscmdlet.ShouldProcess("$ID", "Update StatusCake Pagespeed Test") )
    {
        Return (Update-StatusCakeHelperItem -APICredential $APICredential -Type PageSpeed -ID $ID -Parameter $statusCakeAPIParams)
    }
}

<#
.SYNOPSIS
    Copies the settings of a StatusCake SSL Test
.DESCRIPTION
    Creates a copy of a SSL Test.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER Id
    ID of the SSL Test to be copied
.PARAMETER WebsiteURL
    Website URL of the SSL Test to be copied
.PARAMETER NewWebsiteURL
    Website URL of the new SSL Test
.EXAMPLE
    C:\PS>Copy-StatusCakeHelperSSLTest -WebsiteURL "https://www.example.com" -NewWebsiteURL "https://www.example.org"
    Create a copy of the SSL test with URL "https://www.example.com" for URL "https://www.example.org"
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/SSL/Copy-StatusCakeHelperSSLTest.md
#>

function Copy-StatusCakeHelperSSLTest
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [Parameter(ParameterSetName='CopyByName')]
        [Parameter(ParameterSetName='CopyById')]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName='CopyById',Mandatory=$true)]
        [int]$ID,

        [Parameter(ParameterSetName='CopyByName',Mandatory=$true)]
        [ValidatePattern('^((http|https):\/\/)?([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))*$')]
        [string]$WebsiteURL,

        [Parameter(ParameterSetName='CopyByName',Mandatory=$true)]
        [Parameter(ParameterSetName='CopyById',Mandatory=$true)]
        [ValidatePattern('^((http|https):\/\/)?([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))*$')]
        [string]$NewWebsiteURL
    )

    if($WebsiteURL)
    {   #If copying by URL check if resource with that URL exists
        if( $pscmdlet.ShouldProcess("StatusCake API", "Retrieve StatusCake SSL Tests"))
        {
            $statusCakeItem = Get-StatusCakeHelperSSLTest -APICredential $APICredential -WebsiteURL $WebsiteURL
            if(!$statusCakeItem)
            {
                Write-Error "No SSL Test with Specified Name Exists [$Name]"
                Return $null
            }
            elseif($statusCakeItem.GetType().Name -eq 'Object[]')
            {
                Write-Error "Multiple SSL Tests with the same name [$Name] [$($statusCakeItem.ID)]"
                Return $null
            }
        }
    }
    elseif($ID)
    {   #If copying by ID verify that a resource with the Id already exists
        if( $pscmdlet.ShouldProcess("StatusCake API", "Retrieve StatusCake SSL Tests"))
        {
            $statusCakeItem = Get-StatusCakeHelperSSLTest -APICredential $APICredential -id $ID
            if(!$statusCakeItem)
            {
                Write-Error "No SSL Test with Specified ID Exists [$ID]"
                Return $null
            }
        }
    }

    $psParams = @{}
    $psParams = $statusCakeItem | Get-StatusCakeHelperCopyParameter -FunctionName "New-StatusCakeHelperSSLTest"

    $psParams["website_url"] = $NewWebsiteURL

    if( $pscmdlet.ShouldProcess("StatusCake API", "Create StatusCake SSL Test"))
    {
        $result = New-StatusCakeHelperSSLTest -APICredential $APICredential @psParams
    }
    Return $result
}

<#
.SYNOPSIS
    Gets a StatusCake SSL Test
.DESCRIPTION
    Retrieves a StatusCake SSL Test. If no domain or id is supplied all tests are returned.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER WebsiteURL
    Name of the test to retrieve
.PARAMETER ID
    Test ID to retrieve
.EXAMPLE
    C:\PS>Get-StatusCakeHelperSSLTest
    Retrieve all SSL tests
.EXAMPLE
    C:\PS>Get-StatusCakeHelperSSLTest -ID 123456
    Retrieve SSL test with ID 123456
.OUTPUTS
    Returns a StatusCake SSL Tests as an object
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/SSL/Get-StatusCakeHelperSSLTest.md
.LINK
    https://www.statuscake.com/api/v1/#tag/ssl/operation/list-ssl-tests
.LINK
    https://www.statuscake.com/api/v1/#tag/ssl/operation/get-ssl-test
#>

function Get-StatusCakeHelperSSLTest
{
    [CmdletBinding(PositionalBinding=$false,DefaultParameterSetName='All')]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "Domain")]
        [ValidatePattern('^((https):\/\/)([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)')]
        [Alias('website_url','Domain')]
        [string]$WebsiteURL,

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID
    )

    if($WebsiteURL)
    {
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "SSL" | Where-Object{$_.website_url -eq $WebsiteURL}
    }
    elseif($ID)
    {
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "SSL" -ID $ID
    }
    else
    {
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "SSL"
    }
    Return $statusCakeItem
}


<#
.SYNOPSIS
    Create a StatusCake SSL Test
.DESCRIPTION
    Creates a new StatusCake SSL Test using the supplied parameters. Default settings for a SSL test will check a URL every day with alerts sent at 7, 14 and 30 days.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER WebsiteURL
    URL to check
.PARAMETER Checkrate
    Checkrate in seconds. Default is 86400 seconds (1 day). Options are 300 (5 minutes), 600 (10 minutes), 1800 (30 minutes), 3600 (1 hour), 86400 (1 day), 2073600 (24 days)
.PARAMETER AlertAt
    Number of days before expiration when reminders will be sent. Defaults to reminders at 30, 14 and 7 days. Must be 3 numeric values.
.PARAMETER AlertBroken
    Set to true to enable broken alerts. False to disable
.PARAMETER AlertExpiry
    Set to true to enable expiration alerts. False to disable
.PARAMETER AlertMixed
    Set to true to enable mixed content alerts. False to disable
.PARAMETER AlertReminder
    Set to true to enable reminder alerts. False to disable
.PARAMETER ContactID
    Array containing contact IDs to alert.
.PARAMETER FollowRedirects
    Whether to follow redirects when testing.
.PARAMETER Hostname
    Hostname of the server under test
.PARAMETER Paused
    Whether the test should be run.
.PARAMETER UserAgent
    Custom user agent string set when testing
.PARAMETER Force
    Create an SSL test even if one with the same website URL already exists
.PARAMETER Passthru
    Return the SSL test details instead of the SSL test id
.EXAMPLE
    C:\PS>New-StatusCakeHelperSSLTest -WebsiteURL "https://www.example.com"
    Create a new SSL Test to check https://www.example.com every day
.EXAMPLE
    C:\PS>New-StatusCakeHelperSSLTest -WebsiteURL "https://www.example.com" -AlertAt ("14","30","60")
    Create a new SSL Test to check https://www.example.com every day with alerts sent at 14, 30 and 60 days.
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/SSL/New-StatusCakeHelperSSLTest.md
.LINK
    https://www.statuscake.com/api/v1/#tag/ssl/operation/create-ssl-test
#>

function New-StatusCakeHelperSSLTest
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(Mandatory=$true)]
        [ValidatePattern('^((https):\/\/)([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)')]
        [Alias('website_url','Domain')]
        [string]$WebsiteURL,

        [ValidateSet("300","600","1800","3600","86400","2073600")]
        [Alias('check_rate')]
        [int]$Checkrate=86400,

        [Alias('alert_at')]
        [ValidateCount(3,3)]
        [int[]]$AlertAt=@("7","14","30"),

        [Alias('alert_broken')]
        [boolean]$AlertBroken,

        [Alias('alert_expiry')]
        [boolean]$AlertExpiry=$true,

        [Alias('alert_mixed')]
        [boolean]$AlertMixed,

        [Alias('alert_reminder')]
        [boolean]$AlertReminder=$true,

        [Alias('contact_groups')]
        [int[]]$ContactID,

        [Alias('follow_redirects')]
        [boolean]$FollowRedirects,

        [string]$Hostname,

        [boolean]$Paused,

        [Alias('user_agent')]
        [string]$UserAgent,

        [switch]$Force,

        [switch]$PassThru

    )

    #If force flag not set then check if an existing test with the same website url already exists
    if(!$Force)
    {
       $statusCakeItem = Get-StatusCakeHelperSSLTest -APICredential $APICredential -WebsiteURL $WebsiteURL
       if($statusCakeItem)
       {
            Write-Error "Existing SSL test(s) found with name [$Name]. Please use a different name for the check or use the -Force argument"
            Return $null
       }
    }

    $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
    $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude @("Force","PassThru") | ConvertTo-StatusCakeHelperAPIValue

    if( $pscmdlet.ShouldProcess("StatusCake API", "Create StatusCake SSL Check") )
    {
        Return (New-StatusCakeHelperItem -APICredential $APICredential -Type SSL -Parameter $statusCakeAPIParams -PassThru:$PassThru)
    }

}

<#
.SYNOPSIS
    Remove a StatusCake SSL Test
.DESCRIPTION
    Deletes a StatusCake SSL Test using the supplied ID.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    Test ID to delete
.PARAMETER WebsiteURL
    WebsiteURL SSL test to remove
.EXAMPLE
    C:\PS>Remove-StatusCakeHelperSSLTest -ID 123456
    Remove the SSL Test with ID 123456
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/SSL/Remove-StatusCakeHelperSSLTest.md
.LINK
    https://www.statuscake.com/api/v1/#tag/ssl/operation/delete-ssl-test
#>

function Remove-StatusCakeHelperSSLTest
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [int]$ID,

        [Parameter(ParameterSetName = "Domain")]
        [ValidatePattern('^((https):\/\/)([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)')]
        [Alias('website_url','Domain')]
        [string]$WebsiteURL
    )

    if($WebsiteURL)
    {
        $statusCakeItem = Get-StatusCakeHelperSSLTest -APICredential $APICredential -WebsiteURL $WebsiteURL
        if(!$statusCakeItem)
        {
                Write-Error "No SSL test(s) found with name [$Name]"
                Return $null
        }
        elseif($statusCakeItem.GetType().Name -eq 'Object[]')
        {
            Write-Error "Multiple SSL Tests found with name [$Name]. Please delete the SSL test by ID"
            Return $null
        }
        $ID = $statusCakeItem.id
    }

    if( $pscmdlet.ShouldProcess("$ID", "Remove StatusCake SSL Test") )
    {
        Return (Remove-StatusCakeHelperItem -APICredential $APICredential -Type SSL -ID $ID)
    }
}

<#
.SYNOPSIS
    Update a StatusCake SSL Test
.DESCRIPTION
    Updates a new StatusCake SSL Test using the supplied parameters. Default settings for a SSL test will check a URL every day with alerts sent at 7, 14 and 30 days.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    Test ID to update
.PARAMETER WebsiteURL
    URL to check
.PARAMETER Checkrate
    Checkrate in seconds. Default is 86400 seconds (1 day). Options are 300 (5 minutes), 600 (10 minutes), 1800 (30 minutes), 3600 (1 hour), 86400 (1 day), 2073600 (24 days)
.PARAMETER AlertAt
    Number of days before expiration when reminders will be sent. Defaults to reminders at 30, 14 and 7 days. Must be 3 numeric values.
.PARAMETER AlertBroken
    Set to true to enable broken alerts. False to disable
.PARAMETER AlertExpiry
    Set to true to enable expiration alerts. False to disable
.PARAMETER AlertMixed
    Set to true to enable mixed content alerts. False to disable
.PARAMETER AlertReminder
    Set to true to enable reminder alerts. False to disable
.PARAMETER ContactID
    Array containing contact IDs to alert.
.PARAMETER FollowRedirect
    Whether to follow redirects when testing.
.PARAMETER Hostname
    Hostname of the server under test
.PARAMETER Paused
    Whether the test should be run.
.PARAMETER UserAgent
    Custom user agent string set when testing
.PARAMETER Force
    Update an SSL test even if one with the same website URL already exists
.EXAMPLE
    C:\PS>Update-StatusCakeHelperSSLTest -WebsiteURL "https://www.example.com"
    Update a new SSL Test to check https://www.example.com every day
.EXAMPLE
    C:\PS>Update-StatusCakeHelperSSLTest -WebsiteURL "https://www.example.com" -AlertAt ("14","30","60")
    Update a new SSL Test to check https://www.example.com every day with alerts sent at 14, 30 and 60 days.
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/SSL/Update-StatusCakeHelperSSLTest.md
.LINK
    https://www.statuscake.com/api/v1/#tag/ssl/operation/update-ssl-test
#>

function Update-StatusCakeHelperSSLTest
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID,

        [Parameter(ParameterSetName = "WebsiteURL")]
        [ValidatePattern('^((https):\/\/)([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)')]
        [Alias('website_url','Domain')]
        [string]$WebsiteURL,

        [ValidateSet("300","600","1800","3600","86400","2073600")]
        [Alias('check_rate')]
        [int]$Checkrate=86400,

        [Alias('alert_at')]
        [ValidateCount(3,3)]
        [int[]]$AlertAt=@("7","14","30"),

        [Alias('alert_broken')]
        [boolean]$AlertBroken,

        [Alias('alert_expiry')]
        [boolean]$AlertExpiry=$true,

        [Alias('alert_mixed')]
        [boolean]$AlertMixed,

        [Alias('alert_reminder')]
        [boolean]$AlertReminder=$true,

        [Alias('contact_groups')]
        [int[]]$ContactID,

        [Alias('follow_redirects')]
        [boolean]$FollowRedirect,

        [string]$Hostname,

        [boolean]$Paused,

        [Alias('user_agent')]
        [string]$UserAgent,

        [switch]$Force

    )

    if($WebsiteURL)
    {
       $statusCakeItem = Get-StatusCakeHelperSSLTest -APICredential $APICredential -WebsiteURL $WebsiteURL
       if(!$statusCakeItem)
       {
            Write-Error "No SSL test(s) found with name [$Name]"
            Return $null
       }
       elseif($statusCakeItem.GetType().Name -eq 'Object[]')
       {
           Write-Error "Multiple SSL Tests found with name [$Name]. Please update the SSL test by ID"
           Return $null
       }
       $ID = $statusCakeItem.id
    }

    $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
    $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude @("Force","Name")  | ConvertTo-StatusCakeHelperAPIValue

    if( $pscmdlet.ShouldProcess("$ID", "Update StatusCake SSL Test") )
    {
        Return (Update-StatusCakeHelperItem -APICredential $APICredential -Type SSL -ID $ID -Parameter $statusCakeAPIParams)
    }

}

<#
.SYNOPSIS
    Returns a list of uptime check alerts for a test
.DESCRIPTION
    Returns all alerts that have been sent for a test by its name or id. The return order is newest alerts are shown first.
    Alerts to be returned can be filtered by date using the Before parameter. The number of alerts returned can be controlled
    using the Limit argument.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the Test to retrieve the sent alerts for
.PARAMETER Name
    Name of the Test to retrieve the sent alerts for
.PARAMETER Before
    Return only results from before this date
.PARAMETER Limit
    The maximum of number of results to return
.OUTPUTS
    Returns an object with the details on the Alerts Sent
.EXAMPLE
    C:\PS> Get-StatusCakeHelperUptimeAlert -ID 123456 -Before "2017-08-19 13:29:49"
    Return all the alerts sent for test ID 123456 since the 19th August 2017 13:29:49
.EXAMPLE
    C:\PS> Get-StatusCakeHelperUptimeAlert -ID 123456 -Limit 100
    Return the last 100 alerts sent for test ID 123456
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/Uptime/Base/Get-StatusCakeHelperUptimeAlert.md
.LINK
    https://www.statuscake.com/api/v1/#tag/uptime/operation/list-uptime-test-alerts
#>

function Get-StatusCakeHelperUptimeAlert
{
    [CmdletBinding(PositionalBinding=$false)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "name")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID,

        [ValidateNotNullOrEmpty()]
        [datetime]$Before,

        [ValidateNotNullOrEmpty()]
        [int]$Limit
    )

    # If name supplied find the ID of the test
    if($Name)
    {
        $statusCakeItem = Get-StatusCakeHelperUptimeTest -APICredential $APICredential -Name $Name
        $ID = $statusCakeItem.id
    }

    $metaDataParameters = @{
        APICredential = $APICredential
        Type = "Uptime"
        Property = "Alerts"
        ID = $ID
    }

    if($Before)
    {
        $parameter = $Before | ConvertTo-StatusCakeHelperAPIValue -DateUnix @("Before")
        $metaDataParameters["Parameter"] = $parameter
    }

    if($Limit)
    {
        $metaDataParameters["ResultLimit"] = $Limit
    }

    Return (Get-StatusCakeHelperItemMetadata @metaDataParameters)

}


<#
.SYNOPSIS
    Returns uptime check history results for tests
.DESCRIPTION
    Returns uptime check history results for tests detailing the runs performed on the StatusCake testing infrastructure. The return order is newest alerts are shown first.
    Alerts to be returned can be filtered by date using the Before parameter.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the Test to retrieve the sent alerts for
.PARAMETER Name
    Name of the Test to retrieve the sent alerts for
.PARAMETER Before
    Return only results from before this date
.PARAMETER Limit
    The maximum of number of results to return
.OUTPUTS
    Returns an object with the details on the Alerts Sent
.EXAMPLE
    C:\PS> Get-StatusCakeHelperUptimeHistory -ID 123456 -Before "2017-08-19 13:29:49"
    Return all the uptime history for test ID 123456 since the 19th August 2017 13:29:49
.EXAMPLE
    C:\PS> Get-StatusCakeHelperUptimeHistory -ID 123456 -Limit 100
    Return the last 100 historical results sent for test ID 123456
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/Uptime/Property/Get-StatusCakeHelperUptimeHistory.md
.LINK
    https://www.statuscake.com/api/v1/#tag/uptime/operation/list-uptime-test-history
#>

function Get-StatusCakeHelperUptimeHistory
{
    [CmdletBinding(PositionalBinding=$false)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "name")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID,

        [ValidateNotNullOrEmpty()]
        [datetime]$Before,

        [int]$Limit
    )

    # If name supplied find the ID of the test
    if($Name)
    {
        $statusCakeItem = Get-StatusCakeHelperUptimeTest -APICredential $APICredential -Name $Name
        $ID = $statusCakeItem.id
    }

    $metaDataParameters = @{
        APICredential = $APICredential
        Type = "Uptime"
        Property = "History"
        ID = $ID
    }

    if($Before)
    {
        $parameter = $Before | ConvertTo-StatusCakeHelperAPIValue -DateUnix @("Before")
        $metaDataParameters["Parameter"] = $parameter
    }

    if($Limit)
    {
        $metaDataParameters["ResultLimit"] = $Limit
    }

    Return (Get-StatusCakeHelperItemMetadata @metaDataParameters)

}


<#
.SYNOPSIS
    Returns a list of uptime check periods for a uptime test
.DESCRIPTION
    Returns a list of uptime check periods for a given id or name, detailing the creation time of the period, when it ended and the duration.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the Test to retrieve the sent alerts for
.PARAMETER Name
    Name of the Test to retrieve the sent alerts for
.PARAMETER Before
    Return only results from before this date
.PARAMETER Limit
    The maximum of number of results to return
.OUTPUTS
    Returns an object with the details on the Alerts Sent
.EXAMPLE
    C:\PS> Get-StatusCakeHelperUptimePeriod -TestID 123456 -Before "2017-08-19 13:29:49"
    Return all the alerts sent for test ID 123456 since the 19th August 2017 13:29:49
.EXAMPLE
    C:\PS> Get-StatusCakeHelperUptimePeriod -ID 123456 -Limit 100
    Return the last 100 uptime check periods sent for test ID 123456
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/Uptime/Property/Get-StatusCakeHelperUptimePeriod.md
.LINK
    https://www.statuscake.com/api/v1/#tag/uptime/operation/list-uptime-test-periods
#>

function Get-StatusCakeHelperUptimePeriod
{
    [CmdletBinding(PositionalBinding=$false)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID,

        [Parameter(ParameterSetName = "name")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [ValidateNotNullOrEmpty()]
        [datetime]$Before,

        [int]$Limit
    )

    # If name supplied find the ID of the test
    if($Name)
    {
        $statusCakeItem = Get-StatusCakeHelperUptimeTest -APICredential $APICredential -Name $Name
        $ID = $statusCakeItem.id
    }

    $metaDataParameters = @{
        APICredential = $APICredential
        Type = "Uptime"
        Property = "Periods"
        ID = $ID
    }

    if($Before)
    {
        $parameter = $Before | ConvertTo-StatusCakeHelperAPIValue -DateUnix @("Before")
        $metaDataParameters["Parameter"] = $parameter
    }

    if($Limit)
    {
        $metaDataParameters["ResultLimit"] = $Limit
    }

    Return (Get-StatusCakeHelperItemMetadata @metaDataParameters)

}


<#
.SYNOPSIS
    Retrieves all the StatusCake Tests that have been paused more than the specified time unit
.DESCRIPTION
    Retrieves all the tests from StatusCake that are paused and have been tested longer than
    the supplied parameters. Defaults to returning tests that have been paused more than 24 hours.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER Days
    Specify the number of day(s) (24h period) that the test(s) has been paused
.PARAMETER Hours
    Specify the number of hour(s) that the test(s) has been paused
.PARAMETER Minutes
    Specify the number of minute(s) that the test(s) has been paused
.PARAMETER IncludeNotTested
    If set tests that have never been tested will be included
.PARAMETER ExcludeTested
    If set tests that have been tested will be excluded
.EXAMPLE
    C:\PS>Get-StatusCakeHelperUptimePausedTest
    Get all tests paused longer than a day
.OUTPUTS
    Returns an object with the StatusCake Detailed Test data
 
#>

function Get-StatusCakeHelperUptimePausedTest
{
    [CmdletBinding(PositionalBinding=$false)]
    [OutputType([System.Collections.Generic.List[PSObject]])]
    Param(
        [Parameter(ParameterSetName='Days')]
        [Parameter(ParameterSetName='Hours')]
        [Parameter(ParameterSetName='Minutes')]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName='Days')]
        [int]$Days=1,

        [Parameter(ParameterSetName='Hours')]
        [int]$Hours,

        [Parameter(ParameterSetName='Minutes')]
        [int]$Minutes,

        [Parameter(ParameterSetName='Days')]
        [Parameter(ParameterSetName='Hours')]
        [Parameter(ParameterSetName='Minutes')]
        [switch]$IncludeNotTested,

        [Parameter(ParameterSetName='Days')]
        [Parameter(ParameterSetName='Hours')]
        [Parameter(ParameterSetName='Minutes')]
        [switch]$ExcludeTested
    )
    $statusCakePausedTests = Get-StatusCakeHelperUptimeTest -APICredential $APICredential
    $statusCakePausedTests = $StatusCakePausedTests | Where-Object{$_.Paused -eq "True"}

    $matchingTests=[System.Collections.Generic.List[PSObject]]::new()
    Foreach($sctest in $statusCakePausedTests)
    {
        $detailedTestData = Get-StatusCakeHelperUptimeTest -APICredential $APICredential -ID $sctest.id
        if($detailedTestData.status -eq "up" -and $detailedTestData.uptime -eq 0)
        {
            Write-Verbose "Uptime test [$($sctest.id) / $($sctest.name)] has never been tested"
            if($IncludeNotTested)
            {
                $matchingTests.Add($sctest)
            }
            continue
        }

        if($ExcludeTested)
        {
            Write-Verbose "Skipping uptime test [$($sctest.id) / $($sctest.name)] as ExcludeTested flag set"
            continue
        }

        $testLastTestTimeSpan = New-TimeSpan -Start $detailedTestData.last_tested_at

        If($testLastTestTimeSpan.TotalDays -ge $Days -and $hours -eq 0)
        {
            Write-Verbose "Uptime test [$($sctest.id) / $($sctest.name)] paused for [$([math]::Round($testLastTestTimeSpan.totaldays,2))] days"
            $matchingTests.Add($detailedTestData)
            continue
        }
        elseif($testLastTestTimeSpan.Totalhours -ge $Hours -and $minutes -eq 0)
        {
            Write-Verbose "Uptime test [$($sctest.id) / $($sctest.name)] paused for [$([int]$($testLastTestTimeSpan.totalhours))] hours"
            $matchingTests.Add($detailedTestData)
        }
        elseif($testLastTestTimeSpan.Totalminutes -ge $Minutes)
        {
            Write-Verbose "Uptime test [$($sctest.id) / $($sctest.name)] paused for [$([int]$($testLastTestTimeSpan.totalminutes))] minutes"
            $matchingTests.Add($detailedTestData)
        }
    }
    Return $matchingTests
}

<#
.SYNOPSIS
    Resumes a StatusCake test check
.DESCRIPTION
    Resumes a test
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the Test to be removed from StatusCake
.PARAMETER Name
    Name of the Test to be removed from StatusCake
.EXAMPLE
    C:\PS>Resume-StatusCakeHelperUptimeTest -Name "Example"
    Unpause the test called "Example"
#>

function Resume-StatusCakeHelperUptimeTest
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName='ResumeById',Mandatory=$true)]
        [int]$ID,

        [Parameter(ParameterSetName='ResumeByName',Mandatory=$true)]
        [string]$Name
    )

    if($Name)
    {   #If resuming by name check if resource with that name exists
        if( $pscmdlet.ShouldProcess("StatusCake API", "Retrieve StatusCake Tests"))
        {
            $uptimeTest = Get-StatusCakeHelperUptimeTest -APICredential $APICredential -Name $Name
            if(!$uptimeTest)
            {
                Write-Error "No Test with Specified Name Exists [$Name]"
                Return $null
            }
            elseif($uptimeTest.GetType().Name -eq 'Object[]')
            {
                Write-Error "Multiple Tests with the same name [$Name] [$($uptimeTest.ID)]"
                Return $null
            }
            $ID = $uptimeTest.ID
        }
    }
    elseif($ID)
    {   #If resuming by ID verify that a resource with the Id already exists
        if( $pscmdlet.ShouldProcess("StatusCake API", "Retrieve StatusCake Tests"))
        {
            $uptimeTest = Get-StatusCakeHelperUptimeTest -APICredential $APICredential -ID $ID
            if(!$uptimeTest)
            {
                Write-Error "No Test with Specified ID Exists [$ID]"
                Return $null
            }
            $ID = $uptimeTest.ID
        }
    }

    if( $pscmdlet.ShouldProcess("StatusCake API", "Resume StatusCake Test"))
    {
        $result = Update-StatusCakeHelperUptimeTest -APICredential $APICredential -ID $ID -Paused $false
    }
    Return $result
}

<#
.SYNOPSIS
    Pauses a StatusCake test check
.DESCRIPTION
    Pauses a test.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the Test to be removed from StatusCake
.PARAMETER Name
    Name of the Test to be removed from StatusCake
.EXAMPLE
    C:\PS>Suspend-StatusCakeHelperUptimeTest -Name "Example"
    Pauses the test called "Example"
#>

function Suspend-StatusCakeHelperUptimeTest
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName='PauseById',Mandatory=$true)]
        [int]$ID,

        [Parameter(ParameterSetName='PauseByName',Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )

    if($Name)
    {   #If pausing by name check if resource with that name exists
        if( $pscmdlet.ShouldProcess("StatusCake API", "Retrieve StatusCake Tests"))
        {
            $testCheck = Get-StatusCakeHelperUptimeTest -APICredential $APICredential -Name $Name
            if(!$testCheck)
            {
                Write-Error "No Test with Specified Name Exists [$Name]"
                Return $null
            }
            elseif($testCheck.GetType().Name -eq 'Object[]')
            {
                Write-Error "Multiple Tests with the same name [$Name] [$($testCheck.ID)]"
                Return $null
            }
            $ID = $testCheck.ID
        }
    }
    elseif($ID)
    {   #If pausing by ID verify that a resource with the Id already exists
        if( $pscmdlet.ShouldProcess("StatusCake API", "Retrieve StatusCake Tests"))
        {
            $testCheck = Get-StatusCakeHelperUptimeTest -APICredential $APICredential -ID $ID
            if(!$testCheck)
            {
                Write-Error "No Test with Specified ID Exists [$ID]"
                Return $null
            }
            $ID = $testCheck.ID
        }
    }

    if( $pscmdlet.ShouldProcess("StatusCake API", "Suspend StatusCake Test"))
    {
        $result = Update-StatusCakeHelperUptimeTest -APICredential $APICredential -ID $ID -Paused $true
    }
    Return $result
}

<#
.SYNOPSIS
    Copies the settings of a StatusCake test check
.DESCRIPTION
    Creates a copy of a test. Supply the TestURL or Paused parameter to override the original values in the source test.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    The uptime test ID to modify the details for
.PARAMETER Name
    Name of the uptime Test
.PARAMETER WebsiteURL
    Website URL to be used in the copied test
.PARAMETER NewName
    Name of the new copied uptime test
.PARAMETER Paused
    If supplied sets the state of the test should be after it is copied.
.EXAMPLE
    C:\PS>Copy-StatusCakeHelperUptimeTest -Name "Example" -NewName "Example - Copy"
    Create a copy of test "Example" with name "Example - Copy"
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/Uptime/Copy-StatusCakeHelperUptimeTest.md
#>

function Copy-StatusCakeHelperUptimeTest
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [Parameter(ParameterSetName='CopyByName')]
        [Parameter(ParameterSetName='CopyById')]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName='CopyById',Mandatory=$true)]
        [int]$ID,

        [Parameter(ParameterSetName='CopyByName',Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [String]$Name,

        [Parameter(ParameterSetName='CopyByName')]
        [Parameter(ParameterSetName='CopyById')]
        [ValidatePattern('^((http|https):\/\/)?([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))*$')]
        [String]$WebsiteURL,

        [Parameter(ParameterSetName='CopyByName',Mandatory=$true)]
        [Parameter(ParameterSetName='CopyById',Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [String]$NewName,

        [Parameter(ParameterSetName='CopyByName')]
        [Parameter(ParameterSetName='CopyById')]
        [Boolean]$Paused
    )

    if($Name)
    {   #If copying by name check if resource with that name exists
        if( $pscmdlet.ShouldProcess("StatusCake API", "Retrieve StatusCake Tests"))
        {
            $statusCakeItem = Get-StatusCakeHelperUptimeTest -APICredential $APICredential -Name $Name
            if(!$statusCakeItem)
            {
                Write-Error "No Uptime test with Specified Name Exists [$Name]"
                Return $null
            }
            elseif($statusCakeItem.GetType().Name -eq 'Object[]')
            {
                Write-Error "Multiple Uptime tests with the same name [$Name] [$($statusCakeItem.ID)]"
                Return $null
            }
            $ID = $statusCakeItem.ID
        }
    }
    elseif($ID)
    {   #If copying by ID verify that a resource with the Id already exists
        if( $pscmdlet.ShouldProcess("StatusCake API", "Retrieve StatusCake Tests"))
        {
            $statusCakeItem = Get-StatusCakeHelperUptimeTest -APICredential $APICredential -ID $ID
            if(!$statusCakeItem)
            {
                Write-Error "No Test with Specified ID Exists [$ID]"
                Return $null
            }
            $ID = $statusCakeItem.ID
        }
    }

    $psParams = @{}
    $psParams = $statusCakeItem | Get-StatusCakeHelperCopyParameter -FunctionName "New-StatusCakeHelperUptimeTest"

    Write-Verbose "$($psParams.Keys -join ",")"

    $psParams.Name = $NewName
    if($WebsiteURL)
    {
        $psParams.WebsiteURL = $WebsiteURL
    }
    if($Paused)
    {
        $psParams.Paused = $Paused
    }

    if( $pscmdlet.ShouldProcess("StatusCake API", "Create StatusCake Uptime Test"))
    {
        $result = New-StatusCakeHelperUptimeTest -APICredential $APICredential @psParams
    }
    Return $result

}

<#
.SYNOPSIS
    Retrieves a StatusCake Test with a specific name or Test ID
.DESCRIPTION
    Retrieves StatusCake Test via the test name of the test or Test ID
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    Test ID to retrieve
.PARAMETER Name
    Name of the test to retrieve
.PARAMETER Status
    Filter tests to a specific status
.PARAMETER Tag
    Match tests with tags
.PARAMETER MatchAny
    Match tests which have any of the supplied tags (true) or all of the supplied tags (false). Default is false.
.PARAMETER NoUptime
    Do not calculate uptime percentages for results. Default is false.
.PARAMETER Status
    The status of the uptime tests to be returned
.EXAMPLE
    C:\PS>Get-StatusCakeHelperUptimeTest
    Retrieve all tests
.EXAMPLE
    C:\PS>Get-StatusCakeHelperUptimeTest -TestID 123456
    Retrieve the test with ID 123456
.OUTPUTS
    Returns the test which exists returning $null if no matching test
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/Uptime/Get-StatusCakeHelperUptimeTest.md
.LINK
    https://www.statuscake.com/api/v1/#tag/uptime/operation/list-uptime-tests
.LINK
    https://www.statuscake.com/api/v1/#tag/uptime/operation/get-uptime-test
#>

function Get-StatusCakeHelperUptimeTest
{
    [CmdletBinding(PositionalBinding=$false,DefaultParameterSetName='MatchTag')]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ByTestID")]
        [Parameter(ParameterSetName='MatchTag')]
        [Parameter(ParameterSetName='MatchAnyTag')]
        [ValidateNotNullOrEmpty()]
        [int]$ID,

        [Parameter(ParameterSetName = "ByTestName")]
        [Parameter(ParameterSetName='MatchTag')]
        [Parameter(ParameterSetName='MatchAnyTag')]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(Mandatory=$false,ParameterSetName='MatchTag')]
        [Parameter(Mandatory=$true,ParameterSetName='MatchAnyTag')]
        [Alias('tags')]
        [string[]]$Tag,

        [Parameter(Mandatory=$false,ParameterSetName='MatchTag')]
        [Parameter(Mandatory=$false,ParameterSetName='MatchAnyTag')]
        [switch]$NoUptime,

        [Parameter(Mandatory=$false,ParameterSetName='MatchAnyTag')]
        [switch]$MatchAny,

        [Parameter(Mandatory=$false,ParameterSetName='MatchTag')]
        [Parameter(Mandatory=$false,ParameterSetName='MatchAnyTag')]
        [ValidateSet("down","up")]
        [string]$Status
    )

    if($Name)
    {
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "Uptime" | Where-Object{$_.name -eq $Name}
        if($statusCakeItem)
        {
            #Ensure output when retrieving the name is the same as the ID
            $statusCakeItem = $statusCakeItem.id | ForEach-Object{Get-StatusCakeHelperItem -APICredential $APICredential -Type "Uptime" -ID $_}
        }
    }
    elseif($ID)
    {
        $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "Uptime" -ID $ID
    }
    else
    {
        $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
        $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude @("Force","Limit")
        if($statusCakeAPIParams.Count -ge 1)
        {
            $statusCakeAPIParams = $statusCakeAPIParams | ConvertTo-StatusCakeHelperAPIValue -CsvString "tags"
            $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "Uptime" -Parameter $statusCakeAPIParams
        }
        else
        {
            $statusCakeItem = Get-StatusCakeHelperItem -APICredential $APICredential -Type "Uptime"
        }
    }
    Return $statusCakeItem
}

<#
.SYNOPSIS
    Create a StatusCake uptime tes
.DESCRIPTION
    Creates a new StatusCake Uptime Test using the supplied parameters. Only parameters which have been supplied values are set
    and the defaults for a particular test type are used otherwise.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER Name
    Name of the Test to be displayed in StatusCake
.PARAMETER Type
    The type of test to create. Valid options are "DNS", "HEAD", "HTTP", "PING", "SMTP", "SSH", "TCP"
.PARAMETER WebsiteURL
    Test location, either an IP (for TCP and Ping) or a fully qualified URL for other TestTypes
.PARAMETER CheckRate
    The interval in seconds between checks. Default is 300 seconds.
.PARAMETER BasicUsername
    A Basic Auth Username to use to login for a HTTP check
.PARAMETER BasicPassword
    If BasicUser is set then this should be the password for the BasicUser for a HTTP check
.PARAMETER Confirmation
    Number of confirmation servers to use must be between 0 and 3
.PARAMETER ContactID
    An array of contact group IDs to be assigned to the check
.PARAMETER CustomHeader
    Custom HTTP header for the test, must be supplied as as hashtable
.PARAMETER DNSIP
    DNS Tests only. Array of IP addresses to compare against returned DNS records.
.PARAMETER DNSServer
    DNS Tests only. FQDN or IP address of the nameserver to query.
.PARAMETER DoNotFind
    If the value for the FindString parameter should be found to trigger a alert. 1 = will trigger if FindString found
.PARAMETER EnableSSLAlert
    HTTP Tests only. If enabled, tests will send warnings if the SSL certificate is about to expire.
.PARAMETER FinalEndpoint
    Use to specify the expected Final URL in the testing process
.PARAMETER FindString
    A string that should either be found or not found.
.PARAMETER FollowRedirect
    HTTP Tests only. Whether to follow redirects when testing.
.PARAMETER HostingProvider
    Name of the hosting provider
.PARAMETER IncludeHeader
    Include header content in string match search
.PARAMETER Region
    Array of region codes which should be used for the test.
.PARAMETER Paused
    The state of the test should be after it is created. 0 for unpaused, 1 for paused
.PARAMETER PingURL
    A URL to ping if a site goes down
.PARAMETER Port
    The port to use on a TCP/SMTP/SSH test
.PARAMETER PostBody
    Use to populate the POST data field on the test
.PARAMETER PostRaw
    Use to populate the RAW POST data field on the test
.PARAMETER Public
    Set 1 to enable public reporting, 0 to disable
.PARAMETER StatusCode
    Array of StatusCodes that trigger an alert
.PARAMETER Tag
    Array of tags to assign to a test
.PARAMETER Timeout
    Time in seconds before a test times out. Valid values are between 5 and 100 seconds
.PARAMETER TriggerRate
    How many minutes to wait before sending an alert. Valid values are between 0 and 60 minutes
.PARAMETER UseJar
    Set to 1 to enable the Cookie Jar. Required for some redirects.
.PARAMETER UserAgent
    Use to populate the test with a custom user agent
.PARAMETER Force
    Create an uptime test even if one with the same website URL already exists
.PARAMETER Passthru
    Return the uptime test details instead of the uptime test id
.EXAMPLE
    C:\PS>New-StatusCakeHelperUptimeTest -tName "Example" -WebsiteURL "http://www.example.com" -TestType HTTP -CheckRate 300
    Create a HTTP test called "Example" with URL http://www.example.com checking every 300 seconds
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/Uptime/New-StatusCakeHelperUptimeTest.md
.LINK
    https://www.statuscake.com/api/v1/#tag/uptime/operation/create-uptime-test
#>

function New-StatusCakeHelperUptimeTest
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(Mandatory=$true)]
        [ValidateSet("DNS","HEAD","HTTP","PING","SMTP","SSH","TCP")]
        [Alias('test_type',"TestType")]
        [String]$Type,

        [Parameter(Mandatory=$true)]
        [ValidatePattern('^((http|https):\/\/)?([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))*$')]
        [Alias('website_url','Endpoint','Domain','IP','HostName')]
        [string]$WebsiteURL,

        [Parameter(Mandatory=$true)]
        [ValidateSet("0","30","60","300","900","1800","3600","86400")]
        [Alias('check_rate')]
        [int]$CheckRate,

        #Optional parameters

        [Alias('basic_username')]
        [string]$BasicUsername,

        [Alias('basic_password')]
        [securestring]$BasicPassword,

        [ValidateRange(0,3)]
        [int]$Confirmation,

        [Alias('contact_groups','ContactGroup')]
        [int[]]$ContactID,

        [Alias('custom_header')]
        [hashtable]$CustomHeader,

        [Alias('do_not_find')]
        [boolean]$DoNotFind,

        [Alias("enable_ssl_alert","EnableSSLWarning")]
        [boolean]$EnableSSLAlert,

        [ValidatePattern('^((http|https):\/\/)([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)')]
        [Alias('final_endpoint')]
        [string]$FinalEndpoint,

        [Alias('find_string')]
        [string]$FindString,

        [Alias('follow_redirects')]
        [boolean]$FollowRedirect,

        [Alias('host')]
        [string]$HostingProvider,

        [Alias('include_header')]
        [boolean]$IncludeHeader,

        [boolean]$Paused,

        [Alias('post_body')]
        [object]$PostBody,

        [Alias('post_raw')]
        [string]$PostRaw,

        [ValidateScript({
            if(!($_ | Test-StatusCakeHelperRegionCode)){
                Throw "Supplied region code is invalid [$_]"
            }
            else{$true}
        })]
        [Alias('regions')]
        [String[]]$Region,

        [ValidateScript({
            if(!($_ | Test-StatusCakeHelperHTTPStatusCode)){
                Throw "HTTP Status Code invalid [$_]"
            }
            else{$true}
        })]
        [Alias('status_codes_csv')]
        [int[]]$StatusCode,

        [Alias('tags')]
        [string[]]$Tag,

        [ValidateRange(5,75)]
        [int]$Timeout,

        [ValidateRange(0,60)]
        [Alias('trigger_rate')]
        [int]$TriggerRate,

        [Alias('use_jar')]
        [boolean]$UseJar,

        [Alias('user_agent')]
        [string]$UserAgent,

        [switch]$Force,

        [switch]$PassThru
    )

    DynamicParam
    {
        $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()

        if($Type -eq "TCP" -or $Type -eq "SMTP" -or $Type -eq "SSH")
        {
            $portParameter = New-StatusCakeHelperDynamicParameter -Name "Port" -Type "int" -Mandatory $true -ValidateRange @(1,65535)
            $paramDictionary.Add("Port",$portParameter)
        }
        elseif($Type -eq "DNS")
        {
            $dnsIPsParameter = New-StatusCakeHelperDynamicParameter -Name "DNSIPs" -Type "string[]" -Mandatory $true -Alias @("dns_ips") `
                                                -ValidateScript {
                                                    if(!($_ | Test-StatusCakeHelperIPAddress)){
                                                        Throw "Supplied IP Address is invalid [$_]"
                                                    }
                                                    else{$true}
                                                }
            $paramDictionary.Add("DNSIPs",$dnsIPsParameter)

            $dnsServerParameter = New-StatusCakeHelperDynamicParameter -Name "DNSServer" -Type "string" -Mandatory $true -Alias ("dns_server") `
                                                -ValidatePattern '([a-zA-Z0-9\-]{2,}\.[a-zA-Z\-]{2,})(\.[a-zA-Z\-]{2,})?(\.[a-zA-Z\-]{2,})?|^(?!^.*,$)((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))*$'
            $paramDictionary.Add("DNSServer",$dnsServerParameter)
        }
        return $paramDictionary
    }

    Process
    {
        #If force flag not set then check if an existing test with the same name already exists
        if(!$Force)
        {
        $statusCakeItem = Get-StatusCakeHelperUptimeTest -APICredential $APICredential -Name $Name
            if($statusCakeItem)
            {
                    Write-Error "Existing uptime test(s) found with name [$Name]. Please use a different name for the check or use the -Force argument"
                    Return $null
            }
        }

        $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
        $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude "Force" | ConvertTo-StatusCakeHelperAPIValue -CsvString @("status_codes_csv")

        if( $pscmdlet.ShouldProcess("StatusCake API", "Create StatusCake Uptime Check") )
        {
            Return (New-StatusCakeHelperItem -APICredential $APICredential -Type Uptime -Parameter $statusCakeAPIParams -PassThru:$PassThru)
        }
    }
}


<#
.SYNOPSIS
    Removes the specified StatusCake Test
.DESCRIPTION
    Removes the StatusCake test via it's Test ID or name
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the Test to be removed from StatusCake
.PARAMETER Name
    Name of the Test to be removed from StatusCake
.EXAMPLE
    C:\PS>Remove-StatusCakeHelperUptimeTest -TestID 123456
    Delete the StatusCake test with ID 123456
.OUTPUTS
    Returns the result of the test removal as an object
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/Uptime/Remove-StatusCakeHelperUptimeTest.md
.LINK
    https://www.statuscake.com/api/v1/#tag/uptime/operation/delete-uptime-test
#>

function Remove-StatusCakeHelperUptimeTest
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID,

        [Parameter(ParameterSetName = "Name")]
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )

    if($Name)
    {
       $statusCakeItem = Get-StatusCakeHelperUptimeTest -APICredential $APICredential -Name $Name
       if(!$statusCakeItem)
       {
            Write-Error "No uptime test(s) found with name [$Name]"
            Return $null
       }
       elseif($statusCakeItem.GetType().Name -eq 'Object[]')
       {
           Write-Error "Multiple uptime tests found with name [$Name]. Please delete the Uptime test by ID"
           Return $null
       }
       $ID = $statusCakeItem.id
    }

    if( $pscmdlet.ShouldProcess("$ID", "Delete StatusCake Uptime Test") )
    {
        Return (Remove-StatusCakeHelperItem -APICredential $APICredential -Type Uptime -ID $ID)
    }
}

<#
.SYNOPSIS
    Create a StatusCake uptime tes
.DESCRIPTION
    Creates a new StatusCake Uptime Test using the supplied parameters. Only parameters which have been supplied values are set
    and the defaults for a particular test type are used otherwise.
.PARAMETER APICredential
    Credentials to access StatusCake API
.PARAMETER ID
    ID of the Test to be updated in StatusCake
.PARAMETER Name
    Name of the Test to be updated in StatusCake
.PARAMETER Type
    The type of test to create. Valid options are "DNS", "HEAD", "HTTP", "PING", "SMTP", "SSH", "TCP"
.PARAMETER WebsiteURL
    Test location, either an IP (for TCP and Ping) or a fully qualified URL for other TestTypes
.PARAMETER CheckRate
    The interval in seconds between checks. Default is 300 seconds.
.PARAMETER BasicUsername
    A Basic Auth Username to use to login for a HTTP check
.PARAMETER BasicPassword
    If BasicUser is set then this should be the password for the BasicUser for a HTTP check
.PARAMETER Confirmation
    Number of confirmation servers to use must be between 0 and 3
.PARAMETER ContactID
    An array of contact group IDs to be assigned to the check
.PARAMETER CustomHeader
    Custom HTTP header for the test, must be supplied as as hashtable
.PARAMETER DNSIP
    DNS Tests only. IP to compare against WebsiteURL value.
.PARAMETER DNSServer
    DNS Tests only. Hostname or IP of DNS server to use.
.PARAMETER DoNotFind
    If the value for the FindString parameter should be found to trigger a alert. 1 = will trigger if FindString found
.PARAMETER EnableSSLAlert
    HTTP Tests only. If enabled, tests will send warnings if the SSL certificate is about to expire.
.PARAMETER FinalEndpoint
    Use to specify the expected Final URL in the testing process
.PARAMETER FindString
    A string that should either be found or not found.
.PARAMETER FollowRedirect
    HTTP Tests only. Whether to follow redirects when testing.
.PARAMETER HostingProvider
    Name of the hosting provider
.PARAMETER IncludeHeader
    Include header content in string match search
.PARAMETER Regions
    List of region codes which should be used for the test.
.PARAMETER Paused
    The state of the test should be after it is created. 0 for unpaused, 1 for paused
.PARAMETER Port
    The port to use on a TCP/SMTP/SSH test
.PARAMETER PostBody
    Use to populate the POST data field on the test
.PARAMETER PostRaw
    Use to populate the RAW POST data field on the test
.PARAMETER Public
    Set 1 to enable public reporting, 0 to disable
.PARAMETER StatusCodes
    Array of StatusCodes that trigger an alert
.PARAMETER Tags
    Array of tags to assign to a test
.PARAMETER Timeout
    Time in seconds before a test times out. Valid values are between 5 and 100 seconds
.PARAMETER TriggerRate
    How many minutes to wait before sending an alert. Valid values are between 0 and 60 minutes
.PARAMETER UseJar
    Set to 1 to enable the Cookie Jar. Required for some redirects.
.PARAMETER UserAgent
    Use to populate the test with a custom user agent
.PARAMETER Force
    Create an uptime test even if one with the same website URL already exists
.EXAMPLE
    C:\PS>Update-StatusCakeHelperUptimeTest -tName "Example" -WebsiteURL "http://www.example.com" -TestType HTTP -CheckRate 300
    Create a HTTP test called "Example" with URL http://www.example.com checking every 300 seconds
.LINK
    https://github.com/Oliver-Lii/statuscake-helpers/blob/master/Documentation/Uptime/Update-StatusCakeHelperUptimeTest.md
.LINK
    https://www.statuscake.com/api/v1/#tag/uptime/operation/create-uptime-test
#>

function Update-StatusCakeHelperUptimeTest
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(ParameterSetName = "ID")]
        [ValidateNotNullOrEmpty()]
        [int]$ID,

        [Parameter(ParameterSetName = "Name")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [ValidateSet("DNS","HEAD","HTTP","PING","SMTP","SSH","TCP")]
        [Alias('test_type',"TestType")]
        [String]$Type,

        [ValidatePattern('^((http|https):\/\/)?([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))*$')]
        [Alias('website_url','Endpoint','Domain','IP','HostName')]
        [string]$WebsiteURL,

        [ValidateSet("0","30","60","300","900","1800","3600","86400")]
        [Alias('check_rate')]
        [int]$CheckRate,

        #Optional parameters

        [Alias('basic_username')]
        [string]$BasicUsername,

        [Alias('basic_password')]
        [securestring]$BasicPassword,

        [ValidateRange(0,3)]
        [int]$Confirmation,

        [Alias('contact_groups','ContactGroup')]
        [int[]]$ContactID,

        [Alias('custom_header')]
        [hashtable]$CustomHeader,

        [ValidateScript({
            if(!($_ | Test-StatusCakeHelperIPAddress)){
                Throw "Supplied IP Address is invalid [$_]"
            }
            else{$true}
        })]
        [Alias('dns_ips')]
        [string]$DNSIP,

        [ValidatePattern('^([a-zA-Z0-9]{2,}\.[a-zA-Z]{2,})(\.[a-zA-Z]{2,})?|^(?!^.*,$)((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))*$')]
        [Alias('dns_server')]
        [string]$DNSServer,

        [Alias('do_not_find')]
        [boolean]$DoNotFind,

        [Alias("enable_ssl_alert","EnableSSLWarning")]
        [boolean]$EnableSSLAlert,

        [ValidatePattern('^((http|https):\/\/)([a-zA-Z0-9\-]+(\.[a-zA-Z]+)+.*)$|^(?!^.*,$)')]
        [Alias('final_endpoint')]
        [string]$FinalEndpoint,

        [Alias('find_string')]
        [string]$FindString,

        [Alias('follow_redirects')]
        [boolean]$FollowRedirect,

        [Alias('host')]
        [string]$HostingProvider,

        [Alias('include_header')]
        [boolean]$IncludeHeader,

        [boolean]$Paused,

        [ValidateRange(1,65535)]
        [int]$Port,

        [Alias('post_body')]
        [object]$PostBody,

        [Alias('post_raw')]
        [string]$PostRaw,

        [ValidateScript({
            if(!($_ | Test-StatusCakeHelperRegionCode)){
                Throw "Supplied region code is invalid [$_]"
            }
            else{$true}
        })]
        [String[]]$Regions,

        [ValidateScript({
            if(!($_ | Test-StatusCakeHelperHTTPStatusCode)){
                Throw "HTTP Status Code invalid [$_]"
            }
            else{$true}
        })]
        [Alias('status_codes_csv')]
        [int[]]$StatusCodes,

        [string[]]$Tags,

        [ValidateRange(5,75)]
        [int]$Timeout,

        [ValidateRange(0,60)]
        [Alias('trigger_rate')]
        [int]$TriggerRate,

        [Alias('use_jar')]
        [boolean]$UseJar,

        [Alias('user_agent')]
        [string]$UserAgent
    )

    if($Name)
    {
       $statusCakeItem = Get-StatusCakeHelperUptimeTest -APICredential $APICredential -Name $Name
       if(!$statusCakeItem)
       {
            Write-Error "No Uptime test(s) found with name [$Name]"
            Return $null
       }
       elseif($statusCakeItem.GetType().Name -eq 'Object[]')
       {
           Write-Error "Multiple Uptime Tests found with name [$Name]. Please update the Uptime test by ID"
           Return $null
       }
       $ID = $statusCakeItem.id
    }

    $allParameterValues = $MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
    $statusCakeAPIParams = $allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation -Exclude "Force" | ConvertTo-StatusCakeHelperAPIValue -CsvString @("status_codes_csv")

    if( $pscmdlet.ShouldProcess("$ID", "Update StatusCake Uptime Test") )
    {
        Return (Update-StatusCakeHelperItem -APICredential $APICredential -Type Uptime -ID $ID -Parameter $statusCakeAPIParams)
    }
}

<#
.SYNOPSIS
    Tests to confirm that a supplied string is a valid email address
.DESCRIPTION
    Tests to confirm that a supplied string is a valid email address
.PARAMETER EmailAddress
    String containing the email address to be tested
.OUTPUTS
    Returns true if email address is valid
.EXAMPLE
    C:\PS>"test@example.com" | Test-StatusCakeHelperEmailAddress
    Test if the string "test@example.com" is a valid email address
#>

function Test-StatusCakeHelperEmailAddress
{
    [CmdletBinding(PositionalBinding=$false)]
    [OutputType([System.Boolean])]
    Param(
        [Parameter(Mandatory=$True,
        ValueFromPipeline=$True)]
        [string] $EmailAddress
    )
    Process
    {
        if($EmailAddress -match '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$')
        {
            Return $true
        }
        Return $false
    }
}
<#
.SYNOPSIS
    Tests to confirm that a supplied IP Address is valid
.DESCRIPTION
    Tests to confirm that a supplied IP Address is valid. The IP Address class is used to determine if the supplied IP address is valid.
.PARAMETER IPAddress
    IP Address to test is valid
.EXAMPLE
    C:\PS>"10.0.0.1" | Test-StatusCakeHelperIPAddress
    Test if the value 10.0.0.1 is a valid IP address
.OUTPUTS
    Returns true if the IP Address is valid
#>

function Test-StatusCakeHelperIPAddress
{
    [CmdletBinding(PositionalBinding=$false)]
    [OutputType([System.Boolean])]
    Param(
        [Parameter(Mandatory=$True,
        ValueFromPipeline=$True)]
        [string] $IPAddress
    )
    Process{
        Return $IPAddress -as [IPAddress] -as [Bool]
    }
}

<#
.SYNOPSIS
    Tests to confirm that a supplied phone number has a valid phone number format
.DESCRIPTION
    Tests to confirm that a supplied phone number is in the E.164 phone number format
.PARAMETER MobileNumber
    String containing phone number
.EXAMPLE
    C:\PS>"+1023456789" | Test-StatusCakeHelperMobileNumber
    Test to confirm that "+1023456789" is in E.164 phone number format
.OUTPUTS
    Returns true if mobile number is valid
#>

function Test-StatusCakeHelperMobileNumber
{
    [CmdletBinding(PositionalBinding=$false)]
    [OutputType([System.Boolean])]
    Param(
        [Parameter(Mandatory=$True,
        ValueFromPipeline=$True)]
        [string] $MobileNumber
    )
    Process
    {
        if($MobileNumber -match '^\+[1-9]{1}[0-9]{9,14}$')
        {
            Return $true
        }
        Return $false
    }
}

<#
.SYNOPSIS
    Tests to confirm that a supplied region code is valid
.DESCRIPTION
    Tests to confirm that a supplied region code is valid. Test is carried out by retrieving the list of all region codes and verifying the string is present in the list.
.PARAMETER RegionCode
    Region code to validate
.PARAMETER StatusCakeRegionCodes
    List of StatusCake Region Codes.
.PARAMETER Refresh
    Refresh the list of region codes the function has stored.
.EXAMPLE
    C:\PS>"london" | Test-StatusCakeHelperRegion
    Test if "london" is a valid StatusCake node location
.OUTPUTS
    Returns true if node location server code is valid
#>

function Test-StatusCakeHelperRegionCode
{
    [CmdletBinding(PositionalBinding=$false)]
    [OutputType([System.Boolean])]
    Param(
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential] $APICredential = (Get-StatusCakeHelperAPIAuth),

        [Parameter(Mandatory=$True,
        ValueFromPipeline=$True)]
        [string] $RegionCode,

        [string[]]$StatusCakeRegionCodes,

        [switch]$Refresh
    )

    Process{

        If($PSDefaultParameterValues.ContainsKey("Test-StatusCakeHelperRegion:StatusCakeRegionCodes"))
        {
            $StatusCakeRegionCodes = $PSDefaultParameterValues["Test-StatusCakeHelperRegion:StatusCakeRegionCodes"]
        }
        elseif($Refresh)
        {
            # Update the list of region code stored by the function
            $StatusCakeRegionCodes = (Get-StatusCakeHelperLocation -APICredential $APICredential).region_code | Sort-Object -Unique
            $PSDefaultParameterValues["Test-StatusCakeHelperRegion:StatusCakeRegionCodes"] = $StatusCakeRegionCodes
        }
        elseif(! $StatusCakeRegionCodes)
        {
            $StatusCakeRegionCodes = (Get-StatusCakeHelperLocation -APICredential $APICredential).region_code | Sort-Object -Unique
            $PSDefaultParameterValues["Test-StatusCakeHelperRegion:StatusCakeRegionCodes"] = $StatusCakeRegionCodes
        }

        if($StatusCakeRegionCodes -contains $RegionCode)
        {
            Return $true
        }

    Return $false
    }
}

<#
.SYNOPSIS
    Tests to confirm that a supplied HTTP Status Code is valid
.DESCRIPTION
    Tests to confirm that a supplied HTTP Status Code is valid
.PARAMETER StatusCode
    StatusCode to test is valid
.EXAMPLE
    C:\PS>"404" | Test-StatusCakeHelperHTTPStatusCode
    Test if the value 404 is a valid status code
.OUTPUTS
    Returns true if HTTP Status Code code is valid
#>

function Test-StatusCakeHelperHTTPStatusCode
{
    [CmdletBinding(PositionalBinding=$false)]
    [OutputType([System.Boolean])]
    Param(
        [Parameter(Mandatory=$True,
        ValueFromPipeline=$True)]
        [string] $StatusCode
    )
    Process{
        Return $StatusCode -as [System.Net.HttpStatusCode] -as [Bool]
    }
}

<#
.SYNOPSIS
    Tests to confirm that a supplied TimeZone is valid
.DESCRIPTION
    Tests to confirm that a supplied TimeZone is valid
.PARAMETER TimeZone
    TimeZone string to test is valid
.PARAMETER TimeZoneFile
    Path to JSON file containing valid timezones
.EXAMPLE
    C:\PS>"UTC" | Test-StatusCakeHelperTimeZone
    Test if UTC is a valid time zone
.OUTPUTS
    Returns true if Time Zone is valid
#>

function Test-StatusCakeHelperTimeZone
{
    [CmdletBinding(PositionalBinding=$false)]
    [OutputType([System.Boolean])]
    Param(
        [Parameter(Mandatory=$True,
        ValueFromPipeline=$True)]
        [string] $TimeZone,
        [string] $TimeZoneFile="$PSScriptRoot\Files\TimeZones.json"
    )
    Process
    {
        $timeZoneList = Get-Content $TimeZoneFile | ConvertFrom-Json

        if($timeZoneList -contains $TimeZone)
        {
            Return $true
        }
        Return $false
    }
}
<#
.SYNOPSIS
    Tests to confirm that a supplied HTTP Status Code is valid
.DESCRIPTION
    Tests to confirm that a supplied HTTP Status Code is valid. The URL is tested using the URI class with the uri well formed method and verifying scheme is http and/or https.
.PARAMETER URL
    URL to test is valid
.EXAMPLE
    C:\PS>"https://www.example.com" | Test-StatusCakeHelperURL
    Test if the value https://www.example.com is a valid URL
.OUTPUTS
    Returns true if the supplied string is a URL
#>

function Test-StatusCakeHelperURL
{
    [CmdletBinding(PositionalBinding=$false)]
    [OutputType([System.Boolean])]
    Param(
        [Parameter(Mandatory=$True,
        ValueFromPipeline=$True)]
        [string] $URL
    )
    Process{
        Return [uri]::IsWellFormedUriString($URL, 'Absolute') -and ([uri] $uri).Scheme -in 'http', 'https'
    }
}
<#
.SYNOPSIS
    Converts a hashtable of parameter values to the format expected by the StatusCake API
.DESCRIPTION
    Converts a hashtable of parameter values to the format expected by the StatusCake API
.PARAMETER InputHashTable
    Hashtable containing the parameters and values to be converted
.PARAMETER DateUnix
    Keys with values which should be converted to Unix timestamp instead of RFC3339 format
.PARAMETER CsvString
    Key with values which should be converted to a CSV string instead of as an array
.EXAMPLE
   C:\PS>$inputHashtable | ConvertTo-StatusCakeHelperAPIValue
   Convert values of a hashtable into values to the format expected by StatusCake API
#>

function ConvertTo-StatusCakeHelperAPIValue
{
    [CmdletBinding()]
    [OutputType([hashtable])]
    Param(
        [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
        [hashtable] $InputHashTable,

        [string[]]$DateUnix,

        [string[]]$CsvString
    )

    Process
    {
        $workingHashtable = $PSBoundParameters["InputHashTable"]
        $outputHashtable = @{}

        foreach ($var in $workingHashtable.GetEnumerator())
        {
            $name = $var.name

            switch($var.value.GetType().Name)
            {
                'Boolean'{ # Boolean should be converted to lower case "true/false"
                    $value = "false"
                    If($var.value -eq $true){
                        $value = "true"
                    }
                    $outputHashtable.Add($name,$value)
                    Break
                }
                'DateTime'{ #Dates needs to be in RFC3339 unless Unix timestamp has been specified for the parameter
                    if($name -in $DateUnix)
                    {
                        $value = [int64]($var.value).ToUniversalTime() | Get-Date -UFormat %s
                    }
                    else
                    {
                        $value = ($var.value).ToUniversalTime() | Get-Date -Format s
                        $value = "$value`Z"
                    }
                    $outputHashtable.Add($name,$value)
                    Break
                }
                'Hashtable'{ # Hash table should be converted to JSON (CustomHeader)
                    $value = $var.value  | ConvertTo-Json
                    $outputHashtable.Add($name,$value)
                    Break
                }
                'IPAddress[]'{ # IP Address string should be used
                    $ipAddresses = [System.Collections.Generic.List[PSObject]]::new()
                    $value = $var.value | ForEach-Object{$ipAddresses.Add($_.IPAddressToString)}
                    $outputHashtable.Add($name,$value)
                    Break
                }
                'Object'{ # Hash table should be converted to JSON (CustomHeader)
                    $value = $var.value  | ConvertTo-Json
                    $outputHashtable.Add($name,$value)
                    Break
                }
                'Object[]'{ #Convert arrays to CSV if specified
                    $value = $var.value
                    if($name -in $CsvString)
                    {
                        $value = $var.value -join ","
                    }
                    $outputHashtable.Add($name,$value)
                    Break
                }
                'SecureString'{ #SecureString needs to be converted to plaintext
                    $Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList "UserName", $var.value
                    $value = $Credentials.GetNetworkCredential().Password
                    Write-Verbose "[$($var.name)] [$($var.value.GetType().Name)] will be added with value [$("*".PadRight(8, "*"))]"
                    $outputHashtable.Add($name,$value)
                    Break
                }
                'String[]'{ #Convert arrays to CSV if specified
                    $value = $var.value
                    if($name -in $CsvString)
                    {
                        $value = $var.value -join ","
                    }
                    $outputHashtable.Add($name,$value)
                    Break
                }
                'Uri'{ # Original string of URI should be used
                    $value = $($var.value).OriginalString
                    $outputHashtable.Add($name,$value)
                    Break
                }
                default {
                    $value = $var.value
                    $outputHashtable.Add($name,$value)
                }
            }

            if($var.value.GetType().Name -ne 'SecureString')
            {
                Write-Verbose "[$($var.name)] [$($var.value.GetType().Name)] will be added with value [$value]"
            }
        }
        Return $outputHashtable
    }
}

<#
.SYNOPSIS
    Converts a PSCustomObject to a hashtable
.DESCRIPTION
    Converts a PSCustomObject to a hashtable.
.PARAMETER PSCustomObject
    PSCustomObject to be converted
.PARAMETER IncludeNull
    Flag to include properties with null values in returned hashtable
.EXAMPLE
    C:\PS>$PSCustomObject | ConvertTo-StatusCakeHelperHashtableFromPSCustomObject
    Convert a PSCustomObject into a hashtable
#>

function ConvertTo-StatusCakeHelperHashtableFromPSCustomObject
{
    [CmdletBinding()]
    [OutputType([hashtable])]
    Param(
        [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
        [Object] $PSCustomObject,

        [switch] $IncludeNull
    )

    Process
    {
        $hashtable = @{}
        $properties = $PSCustomObject | Get-Member -MemberType *Property

        foreach($property in $properties)
        {
            $value = $PSCustomObject | Select-Object -ExpandProperty ($property.name)
            if($null -ne $value -and !([string]::IsNullOrEmpty($value)) -or $IncludeNull)
            {
                $hashtable[$property.name] = $value
            }
        }
        return  $hashtable
    }
}
<#
.SYNOPSIS
   Converts a hashtable of parameters to the request body type for the StatusCake API
.DESCRIPTION
   Converts a hashtable of parameters to the request body type for the StatusCake API
.PARAMETER InputHashTable
   Hashtable containing the parameters to be converted
.EXAMPLE
   C:\PS>$inputHashtable | ConvertTo-StatusCakeHelperXW3FormUrlEncoded
   Convert a hashtable to application/x-www-form-urlencoded content body type
#>

function ConvertTo-StatusCakeHelperXW3FormUrlEncoded
{
    [CmdletBinding()]
    [OutputType([string])]
    Param(
        [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
        [hashtable] $InputHashTable
    )

    Process
    {
        $workingHashtable = $PSBoundParameters["InputHashTable"]
        $parameterArray = [System.Collections.Generic.List[PSObject]]::new()
        $outputString = ""

        foreach ($var in $workingHashtable.GetEnumerator())
        {
            $name = $var.name

            switch($var.value.GetType().BaseType.Name)
            {
                'Array'{ # Prefix key with "[]" and add copy of key for each value
                    $name = $([uri]::EscapeDataString("$name[]"))
                    $var.value | ForEach-Object{$parameterArray.Add("$name=$([uri]::EscapeDataString(($_)))")}
                    Break
                }
                default {
                    $name = [uri]::EscapeDataString($name)
                    $value = [uri]::EscapeDataString(($var.value))
                    $parameterArray.Add("$name=$value")
                }
            }
            $outputString = $parameterArray -join "&"
        }

        Return $outputString
    }
}

<#
.SYNOPSIS
   Gets the StatusCake API Username and API Key
.DESCRIPTION
   Returns a PSCredential object containing the StatusCake API Credential
.PARAMETER Credential
   Credential object should not be passed to function but set using Set-StatusCakeHelperAPIAuth
.EXAMPLE
   C:\PS>Get-StatusCakeHelperAPIAuth
   Retrieve the credential from the session if available or from the credential file if credentials have not been set for the session.
 
#>

function Get-StatusCakeHelperAPIAuth
{
   [CmdletBinding()]
   [OutputType([System.Management.Automation.PSCredential])]
   Param(
      [System.Management.Automation.PSCredential] $Credential
   )

   if($PSDefaultParameterValues.ContainsKey("Get-StatusCakeHelperAPIAuth:Credential"))
   {
      $Credential = $PSDefaultParameterValues["Get-StatusCakeHelperAPIAuth:Credential"]
   }
   elseif(Test-StatusCakeHelperAPIAuthSet)
   {
      $moduleName = (Get-Command $MyInvocation.MyCommand.Name).Source
      $Credential = Import-CliXml -Path "$env:userprofile\.$moduleName\$moduleName-Credentials.xml"
   }

   Return $Credential
}

<#
.SYNOPSIS
   Gets the StatusCake API error response
.DESCRIPTION
   Get the StatusCake API error response. In Powershell 5.1 the error response has to be retrieved if a web exception error is thrown by Invoke-RestMethod.
.PARAMETER ExceptionResponseStream
   Exception response stream containing the error details to be extracted
.EXAMPLE
   C:\PS>$_.Exception.Response.GetResponseStream() | Get-StatusCakeHelperAPIErrorResponse
   Retrieve the details of the error returned by the StatusCake API
#>

function Get-StatusCakeHelperAPIErrorResponse
{
   [CmdletBinding()]
   Param(
      [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
      $ExceptionResponseStream
   )

   Process
   {
      $streamReader = New-Object System.IO.StreamReader($ExceptionResponseStream)
      $streamReader.BaseStream.Position = 0
      $errorResponse = $streamReader.ReadToEnd() | ConvertFrom-Json

      #Get all the error response property names
      $errorProperties = $errorResponse.errors | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty "Name"

      $apiErrors = [System.Collections.Generic.List[PSObject]]::new()
      #Collate the error property details into a single array
      $errorProperties | ForEach-Object{
         $errorPropMsg = $errorResponse.errors | Select-Object -ExpandProperty $_
         $apiErrors.Add("[$_] $errorPropMsg")
      }

      $statusCakeAPIError = [PSCustomObject]@{
         Message = $errorResponse.message
         Errors = $apiErrors
      }

      Return $statusCakeAPIError
   }
}
<#
.SYNOPSIS
    Retrieves the StatusCake API parameters
.DESCRIPTION
    Retrieves the StatusCake API parameter names from the parameter aliases defined in the function and modifies them to meet the StatusCake API requirements.
.PARAMETER InputHashTable
    Hashtable containing the values to pass to the StatusCake API
.PARAMETER InvocationInfo
    InvocationInfo object from the calling function
.PARAMETER Clear
    Array of key names for which the values should be sent empty to StatusCake API
.PARAMETER Join
    Hashtable containing values which need to be joined by specific separator
.PARAMETER Exclude
    Array of parameter names which should be excluded from the output hashtable
.EXAMPLE
    C:\PS>$allParameterValues | Get-StatusCakeHelperAPIParameter -InvocationInfo $MyInvocation
    Retrieve the StatusCake API parameter names from the supplied invocation object
#>

Function Get-StatusCakeHelperAPIParameter
{
    [CmdletBinding()]
    [OutputType([hashtable])]
    Param(
        [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
        [hashtable] $InputHashTable,

        [System.Management.Automation.InvocationInfo]$InvocationInfo,

        [string[]] $Clear=@(),

        [hashtable] $Join=@{},

        [string[]] $Exclude=@()
    )

    Process
    {
        $parameterAction = $InputHashTable
        $parameterAction.Remove("InputHashTable")
        $parameterAction.Remove("InvocationInfo")

        $outputHashtable = @{}

        # Avoiding sending any common parameters
        $Exclude += [System.Management.Automation.PSCmdlet]::CommonParameters
        $Exclude += [System.Management.Automation.PSCmdlet]::OptionalCommonParameters
        # APICredential is sent as part of the request header
        $Exclude += "APICredential"

        #Remove excluded variables
        $workingHash = $InputHashTable.GetEnumerator() | Where-Object {$_.Key -notin $Exclude}

        #Ignore common parameters and find aliases of parameters which need to be renamed
        $FunctionInfo = (Get-Command -Name $InvocationInfo.InvocationName)
        $parameterMetadata = $FunctionInfo.Parameters.Values
        $aliases = $parameterMetadata | Select-Object -Property Name, Aliases | Where-Object {$_.Name -in $workingHash.Key -and $_.Aliases}
        $rename = @{}
        foreach($item in $aliases)
        {   # Rename parameter using first alias
            $rename[$item.name] = $item.aliases[0]
        }

        foreach($cItem in $Clear)
        {
            If($InputHashTable.ContainsKey($cItem))
            {
                $InputHashTable[$cItem] = "`"`""
            }
            else
            {
                $outputHashtable[$cItem] = "`"`""
            }
        }

        foreach($item in $workingHash)
        {
            #API parameter names should be lower cased
            $itemName = ($item.Name).ToLower()

            Switch($itemName)
            {
                {$rename.ContainsKey($_)}{
                    $outputHashtable[$rename[$_]] = $InputHashTable[$_]
                }
                {$Join.ContainsKey($_)}{
                    $outputHashtable[$_] = $InputHashTable[$_] -join $Join[$_]
                }
                Default {
                    $outputHashtable[$_] = $InputHashTable[$_]
                }
            }
        }

        Return $outputHashtable
    }
}

<#
.SYNOPSIS
    Returns the parameters which can be copied from an object
.DESCRIPTION
    Returns the properties and values of a StatusCake object which be set via the API. Not all parameters returned by StatusCake can by set via the API.
    The properties of the statuscake object are compared against the parameter names and aliases on the supplied function name.
.PARAMETER InputObject
    StatusCake Object to be copied
.PARAMETER FunctionName
    Name of the function for which the parameters need to be copied
.EXAMPLE
    C:\PS>$statusCakeObject | Get-StatusCakeHelperCopyParameter -FunctionName "New-StatusCakeHelperSSLTest"
    Return a hashtable of available parameters from the New-StatusCakeHelperSSLTest function
#>

function Get-StatusCakeHelperCopyParameter
{
    [CmdletBinding(PositionalBinding=$false)]
    [OutputType([hashtable])]
    Param(
        [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
        [ValidateNotNullOrEmpty()]
        $InputObject,

        [Parameter(Mandatory=$True)]
        [ValidateNotNullOrEmpty()]
        [string] $FunctionName
    )

    Process
    {
        $psParams = @{}
        $functionInfo = (Get-Command -Name $FunctionName)
        $parameterList = $functionInfo.Parameters
        $sourceItemHash = $InputObject | ConvertTo-StatusCakeHelperHashtableFromPSCustomObject

        $parameterMetadata = $FunctionInfo.Parameters.Values
        $copyProperties = $parameterMetadata | Select-Object -Property Name, Aliases | Where-Object {$_.Name -in $sourceItemHash.Keys -or ($_.Aliases | Select-Object -First 1) -in $sourceItemHash.Keys}
        $parameterList = [System.Collections.Generic.List[PSObject]]::new()
        foreach($item in $copyProperties)
        {   # Add renamed parameter using first alias
            If($item.aliases[0])
            {
                $parameterList.Add($item.aliases[0])
            }
            else
            {
                $parameterList.Add($item.Name)
            }

        }

        $paramsToUse = Compare-Object @($sourceItemHash.Keys) @($parameterList) -IncludeEqual -ExcludeDifferent
        $paramsToUse = $paramsToUse | Select-Object -ExpandProperty InputObject

        foreach($key in $paramsToUse)
        {
            $psParams[$key]=$sourceItemHash[$key]
        }

        Return $psParams
    }
}
<#
.SYNOPSIS
    Get the all the parameter which have values
.DESCRIPTION
    Combines the default parameter values with the parameters from bound parameters excluding any null parameters
.PARAMETER InvocationInfo
    InvocationInfo object supplied by the calling function
.PARAMETER BoundParameters
    The $PSBoundParameters hashtable supplied by the calling function
.EXAMPLE
    C:\PS>$MyInvocation | Get-StatusCakeHelperParameterValue -BoundParameters $PSBoundParameters
    Get all the parameters which values from $MyInvocation and $PSBoundParameter objects
.OUTPUTS
    Returns a hashtable with all parameters which are bound or have non-null defaults
#>

Function Get-StatusCakeHelperParameterValue
{
    [CmdletBinding()]
    [OutputType([hashtable])]
    param(
        [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
        [System.Management.Automation.InvocationInfo]$InvocationInfo,

        [Parameter(Mandatory=$True)]
        [hashtable]$BoundParameters
    )

    Process
    {
        $allParameterValues = @{}
        foreach($parameter in $InvocationInfo.MyCommand.Parameters.GetEnumerator())
        {
            $value = Get-Variable -Name $parameter.Key -ValueOnly -ErrorAction Ignore
            # Check if variable value is not null
            if($null -ne $value)
            {
                # Check if variable is not a null type
                if($value -ne ($null -as $parameter.Value.ParameterType))
                {
                    $allParameterValues[$parameter.Key] = $value
                }
            }
            if($BoundParameters.ContainsKey($parameter.Key))
            {
                $allParameterValues[$parameter.Key] = $BoundParameters[$parameter.Key]
            }
        }
        return $allParameterValues
    }
}

<#
.SYNOPSIS
    Returns the parameters which can be copied from an object
.DESCRIPTION
    Returns the properties and values of a StatusCake object which be set via the API. Not all parameters returned by StatusCake can by set via the API.
    The properties of the statuscake object are compared against the parameter names and aliases on the supplied function name.
.PARAMETER Name
    The name of the dynamic parameter to create.
.PARAMETER Type
    The variable type of the parameter to be created
.PARAMETER ParameterSetName
    The name of the parameter set that the variable is a part of
.PARAMETER Mandatory
    Specify if the parameter should be made mandatory
.PARAMETER Alias
    The aliases that the parameter should have
.PARAMETER ValidateCount
    The minimum and maximum number of arguments that a parameter can accept
.PARAMETER ValidateLength
    The minimum and maximum number of characters in the parameter argument
.PARAMETER ValidateNotNullOrEmpty
    Validate whether the parameter can contain a null or empty value
.PARAMETER ValidatePattern
    Regular expression that the parameter argument should meet
.PARAMETER ValidateRange
    The minimum and maximum values in the parameter argument
.PARAMETER ValidateScript
    Script to use for validation of the parameter argument
.PARAMETER ValidateSet
    The set of valid values for the parameter argument
.EXAMPLE
    C:\PS>New-StatusCakeHelperDynamicParameter -Name "Port" -Type $( "int" -as [type]) -Mandatory $true -ValidateRange @(1,65535)
    Create a new dynamic parameter called Port which is an integer that is mandatory and has a minimum value of 1 and maximum value of 65535
#>

function New-StatusCakeHelperDynamicParameter
{
    [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess=$true)]
    [OutputType([System.Management.Automation.RuntimeDefinedParameter])]
    Param(
        [Parameter(Mandatory=$True)]
        [ValidateNotNullOrEmpty()]
        [string] $Name,

        [Parameter(Mandatory=$True)]
        [ValidateNotNullOrEmpty()]
        [string] $Type,

        [string] $ParameterSetName,

        [boolean] $Mandatory=$false,

        [string[]] $Alias,

        [Parameter(ParameterSetName = "ValidateCount")]
        [ValidateCount(2,2)]
        [int[]]$ValidateCount,

        [Parameter(ParameterSetName = "ValidateLength")]
        [ValidateCount(2,2)]
        [int[]]$ValidateLength,

        [Parameter(ParameterSetName = "ValidateNotNullOrEmpty")]
        [boolean]$ValidateNotNullOrEmpty,

        [Parameter(ParameterSetName = "ValidatePattern")]
        [ValidateNotNullOrEmpty()]
        [string]$ValidatePattern,

        [Parameter(ParameterSetName = "ValidateRange")]
        [ValidateCount(2,2)]
        [int[]]$ValidateRange,

        [Parameter(ParameterSetName = "ValidateScript")]
        [ValidateNotNullOrEmpty()]
        [scriptblock]$ValidateScript,

        [Parameter(ParameterSetName = "ValidateSet")]
        [ValidateNotNullOrEmpty()]
        [string[]]$ValidateSet

    )

    if( $pscmdlet.ShouldProcess("Parameter Attribute", "Create") )
    {
        $parameterAttributes = New-Object -Type System.Management.Automation.ParameterAttribute
        if($ParameterSetName)
        {
            $parameterAttributes.ParameterSetName = $ParameterSetName
        }
        $parameterAttributes.Mandatory = $Mandatory

        $parameterAttributesCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
    }

    if($Alias)
    {
        $parameterAliasAttribute = New-Object -Type System.Management.Automation.AliasAttribute($Alias)
        $parameterAttributesCollection.Add($parameterAliasAttribute)
    }

    #Add validation objects to parameter attribute collection
    switch($PsCmdlet.ParameterSetName)
    {
        "ValidateCount"{$validationOption = New-Object -Type System.Management.Automation.ValidateCountAttribute($ValidateCount[0],$ValidateCount[1]) ;continue }
        "ValidateLength"{$validationOption = New-Object -Type System.Management.Automation.ValidateLengthAttribute($ValidateLength[0],$ValidateLength[1]) ;continue }
        "ValidatePattern"{$validationOption = New-Object -Type System.Management.Automation.ValidatePatternAttribute($ValidatePattern) ;continue }
        "ValidateRange"{$validationOption = New-Object -Type System.Management.Automation.ValidateRangeAttribute($ValidateRange[0],$ValidateRange[1]) ;continue }
        "ValidateScript"{$validationOption = New-Object -Type System.Management.Automation.ValidateScriptAttribute($ValidateScript) ;continue }
        "ValidateSet"{$validationOption = New-Object -Type System.Management.Automation.ValidateSetAttribute ($ValidateSet) ;continue }
    }
    If($ValidateNotNullOrEmpty){$validationOption = New-Object -Type System.Management.Automation.ValidateNotNullOrEmptyAttribute}
    If($validationOption){$parameterAttributesCollection.Add($validationOption)}

    $parameterAttributesCollection.Add($parameterAttributes)

    if( $pscmdlet.ShouldProcess("Runtime Defined Parameter", "Create") )
    {
        $dynamicParameter = New-Object -Type System.Management.Automation.RuntimeDefinedParameter( `
        $Name,
        ($Type -as [type]),
        $parameterAttributesCollection)
    }

    return $dynamicParameter
}