PublicAPI.ps1

class LogetoException : Exception {
    [string] $Code
    [System.Management.Automation.ErrorRecord] $InnerErrorRecord

    LogetoException($Code, $Message, $InnerErrorRecord) : base($Message) {
        $this.Code = $Code
        $this.InnerErrorRecord = $InnerErrorRecord
    }
}

function Get-LogetoEntity {
[CmdletBinding()]
Param (
    [Parameter(Mandatory=$true)] [string]$AccountName,
    [Parameter(Mandatory=$true)] [string]$AccessKey,
    [Parameter(Mandatory=$true)] [string]$Entity,
    [string]$EntityKey,    
    $Params,
    [int]$RetryCount = 1,
    [int]$ApiVersion = 2,
    [switch]$SingleSegment,
    [string]$CustomEndpoint
)
    if (!$Params)
    {
        $Params = @{}
    }

    $headers = @{
        AccessKey = $AccessKey
    }

    $accountName = $AccountName.ToLower()

    if ($CustomEndpoint) { $headers.AccountName = $accountName }

    $endpoint = if ($CustomEndpoint) { $CustomEndpoint } else { "https://$($accountName).logeto.com" }

    $key = if ($EntityKey) { "/$EntityKey" } else { $null }

    $uriBuilder = [System.UriBuilder]::new("$($endpoint)/api/v$($ApiVersion)/$($Entity)$key")

    $items = $null

    $progressId = 1

    $waitTime = 2
    $maxWaitTime = 300

    do
    {
        Write-Progress -PercentComplete -1 -Status "Records downloaded $($items.Count)" -Activity "Retrieving records" -Id $progressId

        try
        {
            [System.Collections.ArrayList] $query = @()
            foreach ($param in $Params.GetEnumerator()) {
                $value =  $param.Value
                if ($value -is [DateTime])
                {
                    $value = $value.ToString("yyyy-MM-dd")
                }
                $key = [System.Net.WebUtility]::UrlEncode($param.Name)
                $value = [System.Net.WebUtility]::UrlEncode($value)
                $query.Add("${key}=${value}") | Out-Null
            }

            $uriBuilder.Query = ($query -join '&')
            $uri = $uriBuilder.ToString()

            while ($RetryCount -gt 0)
            {
                try
                {
                    $result = Invoke-RestMethod -Method Get -Uri $uri -ErrorVariable $errorResult -Headers $headers
                    break
                }
                catch
                {
                    if (($_.Exception.GetType().Name -eq "WebException") -or ($_.Exception.GetType().Name -eq "HttpResponseException"))
                    {
                        if ((--$RetryCount) -eq 0)
                        {
                            throw
                        }
                        Start-Sleep -Seconds $waitTime
                        if ($waitTime -lt $maxWaitTime)
                        {
                            $waitTime = $waitTime * 2
                        }
                    }
                    else
                    {
                        throw
                    }
                }
            }
        }
        catch
        {
            Write-Progress -Completed -Activity "Retrieving records failed" -Id $progressId

            $exception = New-LogetoException
            if ($exception) { throw $exception }
            throw
        }

        if ($SingleSegment -or $EntityKey)
        {
            Write-Progress -Completed -Activity "Retrieving records" -Id $progressId
            return $result
        }

        if ($result.Items)
        {
            $items = $items + $result.Items
            Write-Progress -PercentComplete -1 -Status "Records downloaded $($items.Count)" -Activity "Retrieving records" -Id $progressId
        }

        if ($result.ContinuationToken)
        {
            $params["ContinuationToken"] = $result.ContinuationToken
        }
    } 
    while ($result -and $result.ContinuationToken)

    Write-Progress -Completed -Activity "Retrieving records" -Id $progressId

    return $items
}

function Set-LogetoEntity {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)] [string]$AccountName,
        [Parameter(Mandatory=$true)] [string]$AccessKey,
        [Parameter(Mandatory=$true)] [string]$Entity,
        [string]$EntityKey,
        $Params,
        $Data,
        [int]$RetryCount = 1,
        [int]$ApiVersion = 2,
        [string]$CustomEndpoint
    )

    if (!$Params)
    {
        $Params = @{}
    }

    $headers = @{
        AccessKey = $AccessKey
        'Content-Type' = "application/json"
    }

    $accountName = $AccountName.ToLower()

    if ($CustomEndpoint) { $headers.AccountName = $accountName }

    $endpoint = if ($CustomEndpoint) { $CustomEndpoint } else { "https://$($accountName).logeto.com" }

    $key = if ($EntityKey) { "/$EntityKey" } else { $null }
    
    $uriBuilder = [System.UriBuilder]::new("$($endpoint)/api/v$($ApiVersion)/$($Entity)$key")

    $waitTime = 2
    $maxWaitTime = 300

    try
    {
        [System.Collections.ArrayList] $query = @()
        foreach ($param in $Params.GetEnumerator()) {
            $value =  $param.Value
            if ($value -is [DateTime])
            {
                $value = $value.ToString("yyyy-MM-dd")
            }
            $key = [System.Net.WebUtility]::UrlEncode($param.Name)
            $value = [System.Net.WebUtility]::UrlEncode($value)
            $query.Add("${key}=${value}") | Out-Null
        }

        $uriBuilder.Query = ($query -join '&')
        $uri = $uriBuilder.ToString()

        $jsonBody = ($Data | ConvertTo-Json)

        while ($RetryCount -gt 0)
        {
            try
            {
                $method = if ($EntityKey) { [Microsoft.PowerShell.Commands.WebRequestMethod]::Put } else { [Microsoft.PowerShell.Commands.WebRequestMethod]::Post }

                $result = Invoke-RestMethod -Method $method -Uri $uri -ErrorVariable $errorResult -ContentType "application/json; charset=utf-8" -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes($jsonBody))
                break
            }
            catch
            {
                if (($_.Exception.GetType().Name -eq "WebException") -or ($_.Exception.GetType().Name -eq "HttpResponseException"))
                {
                    if ((--$RetryCount) -eq 0)
                    {
                        throw
                    }
                    Start-Sleep -Seconds $waitTime
                    if ($waitTime -lt $maxWaitTime)
                    {
                        $waitTime = $waitTime * 2
                    }
                }
                else
                {
                    throw
                }
            }
        }
    }
    catch
    {
        $exception = New-LogetoException
        if ($exception) { throw $exception }
        throw
    }

    return $result
}

function Edit-LogetoEntity {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)] [string]$AccountName,
        [Parameter(Mandatory=$true)] [string]$AccessKey,
        [Parameter(Mandatory=$true)] [string]$Entity,
        [Parameter(Mandatory=$true)] [string]$EntityKey,
        $Params,
        $Data,
        [int]$RetryCount = 1,
        [int]$ApiVersion = 2,
        [string]$CustomEndpoint
    )

    if (!$Params)
    {
        $Params = @{}
    }

    $headers = @{
        AccessKey = $AccessKey
        'Content-Type' = "application/json"
    }

    $accountName = $AccountName.ToLower()

    if ($CustomEndpoint) { $headers.AccountName = $accountName }
    
    $endpoint = if ($CustomEndpoint) { $CustomEndpoint } else { "https://$($accountName).logeto.com" }
    
    $uriBuilder = [System.UriBuilder]::new("$($endpoint)/api/v$($ApiVersion)/$($Entity)/$($EntityKey)")

    $waitTime = 2
    $maxWaitTime = 300

    try
    {
        [System.Collections.ArrayList] $query = @()
        foreach ($param in $Params.GetEnumerator()) {
            $value =  $param.Value
            if ($value -is [DateTime])
            {
                $value = $value.ToString("yyyy-MM-dd")
            }
            $key = [System.Net.WebUtility]::UrlEncode($param.Name)
            $value = [System.Net.WebUtility]::UrlEncode($value)
            $query.Add("${key}=${value}") | Out-Null
        }

        $uriBuilder.Query = ($query -join '&')
        $uri = $uriBuilder.ToString()

        $jsonBody = ($Data | ConvertTo-Json)

        while ($RetryCount -gt 0)
        {
            try
            {
                $result = Invoke-RestMethod -Method Patch -Uri $uri -ErrorVariable $errorResult -ContentType "application/json; charset=utf-8" -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes($jsonBody))
                break
            }
            catch
            {
                if (($_.Exception.GetType().Name -eq "WebException") -or ($_.Exception.GetType().Name -eq "HttpResponseException"))
                {
                    if ((--$RetryCount) -eq 0)
                    {
                        throw
                    }
                    Start-Sleep -Seconds $waitTime
                    if ($waitTime -lt $maxWaitTime)
                    {
                        $waitTime = $waitTime * 2
                    }
                }
                else
                {
                    throw
                }
            }
        }
    }
    catch
    {
        $exception = New-LogetoException
        if ($exception) { throw $exception }
        throw
    }
    
    return $result
}

function Remove-LogetoEntity {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)] [string]$AccountName,
        [Parameter(Mandatory=$true)] [string]$AccessKey,
        [Parameter(Mandatory=$true)] [string]$Entity,
        [Parameter(Mandatory=$true)] [string]$EntityKey,
        [int]$RetryCount = 1,
        [int]$ApiVersion = 2,
        [string]$CustomEndpoint
    )

    $headers = @{
        AccessKey = $AccessKey
    }

    $accountName = $AccountName.ToLower()

    if ($CustomEndpoint) { $headers.AccountName = $accountName }
    
    $endpoint = if ($CustomEndpoint) { $CustomEndpoint } else { "https://$($accountName).logeto.com" }
    
    $uriBuilder = [System.UriBuilder]::new("$($endpoint)/api/v$($ApiVersion)/$($Entity)/$($EntityKey)")

    $waitTime = 2
    $maxWaitTime = 300

    try
    {
        $uri = $uriBuilder.ToString()
        while ($RetryCount -gt 0)
        {
            try
            {
                $result = Invoke-RestMethod -Method Delete -Uri $uri -ErrorVariable $errorResult -Headers $headers
                break
            }
            catch
            {
                if (($_.Exception.GetType().Name -eq "WebException") -or ($_.Exception.GetType().Name -eq "HttpResponseException"))
                {
                    if ((--$RetryCount) -eq 0)
                    {
                        throw
                    }
                    Start-Sleep -Seconds $waitTime
                    if ($waitTime -lt $maxWaitTime)
                    {
                        $waitTime = $waitTime * 2
                    }
                }
                else
                {
                    throw
                }
            }
        }
    }
    catch
    {
        $exception = New-LogetoException
        if ($exception) { throw $exception }
        throw
    }
    
    return $result
}

function ConvertTo-LogetoIndexedTable
{
[CmdletBinding()]
Param (
    [Parameter(Mandatory=$true, Position=0)][AllowNull()] $Items,
    [Parameter(Mandatory=$true)] $LookupProperty
)
    if(!$Items)
    {
        return $null
    }

    $result = @{}
    foreach ($item in $Items)
    {
        $result[$item.$LookupProperty] = $item    
    }
    return $result
}

function Get-LogetoFormatedError
{
[CmdletBinding()]
Param (
    [Parameter(Mandatory=$true)] $ErrorRecord
)
    $body = ""

    if ($ErrorRecord.Exception.Code)
    {
        $body += $ErrorRecord.Exception.Code + ": "
    }
    $body += $ErrorRecord.Exception.Message + "`n"

    if (!$ErrorRecord.Exception.InnerErrorRecord)
    {
        $body += $ErrorRecord.ScriptStackTrace + "`n"
    }
    else
    {
        $body += $ErrorRecord.Exception.InnerErrorRecord.ScriptStackTrace + "`n"
    }

    return $body
}

function New-LogetoException
{
    $response = $null

    if ($PSVersionTable.PSVersion.Major -lt 6)
    {
        $streamReader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream())
        $streamReader.BaseStream.Position = 0
        $response = $streamReader.ReadToEnd()
        $streamReader.Close()
    }
    else
    {
        $response = $_.ErrorDetails.Message
    }

    if ($response)
    {
        try
        {
            $parsedError = $response | ConvertFrom-Json
            return [LogetoException]::new($parsedError.Error.Code, $parsedError.Error.Message, $_)
        }
        catch
        {
            Write-Warning "Unable to parse response $response"
            return $null
        }
    }
    else
    {
        return $null
    }
}