Private/APICalls.ps1

function ConvertTo-QueryString {
<#
    .SYNOPSIS
        Converts uri filter parameters
 
    .DESCRIPTION
        The Invoke-ApiRequest cmdlet converts & formats uri filter parameters
        from a function which are later used to make the full resource uri for
        an API call
 
        This is an internal helper function the ties in directly with the
        Invoke-ApiRequest & any public functions that define parameters
 
    .PARAMETER uri_Filter
        Hashtable of values to combine a functions parameters with
        the resource_Uri parameter.
 
        This allows for the full uri query to occur
 
    .PARAMETER resource_Uri
        Defines the short resource uri (url) to use when creating the API call
 
    .EXAMPLE
        ConvertTo-QueryString -uri_Filter $uri_Filter -resource_Uri '/account'
 
        Example: (From public function)
            $uri_Filter = @{}
 
            ForEach ( $Key in $PSBoundParameters.GetEnumerator() ){
                if( $excludedParameters -contains $Key.Key ){$null}
                else{ $uri_Filter += @{ $Key.Key = $Key.Value } }
            }
 
            1x key = https://api.datto.com/v1/account?accountId=12345
            2x key = https://api.datto.com/v1/account?accountId=12345&details=True
 
    .NOTES
        N\A
 
    .LINK
        https://celerium.github.io/Datto-PowerShellWrapper/site/Internal/ConvertTo-QueryString.html
 
#>


[CmdletBinding()]
param(
    [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
    [hashtable]$uri_Filter,

    [Parameter(Mandatory = $true)]
    [String]$resource_Uri
)

    begin{}

    process{

        if (-not $uri_Filter) {
            return ""
        }

        $excludedParameters =   'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable',
                                'OutBuffer', 'OutVariable', 'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable',
                                'allPages', 'page', 'perPage',
                                'endpoint_Agents', 'endpoint_Devices', 'endpoint_byDevice', 'endpoint_byDeviceAgent',
                                'endpoint_byDeviceAlert', 'endpoint_byDeviceAsset', 'endpoint_byDeviceShare', 'endpoint_byDeviceVolume',
                                'endpoint_Domains', 'endpoint_CustomerSeats', 'endpoint_CustomerApps', 'saasCustomerId'

        $query_Parameters = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)

        ForEach ( $Key in $uri_Filter.GetEnumerator() ){

            if( $excludedParameters -contains $Key.Key ){$null}
            elseif ( $Key.Value.GetType().IsArray ){
                Write-Verbose "[ $($Key.Key) ] is an array parameter"
                foreach ($Value in $Key.Value) {
                    #$ParameterName = $Key.Key
                    $query_Parameters.Add($Key.Key, $Value)
                }
            }
            else{
                $query_Parameters.Add($Key.Key, $Key.Value)
            }

        }

        # Build the request and load it with the query string.
        $uri_Request        = [System.UriBuilder]($Datto_Base_URI + $resource_Uri)
        $uri_Request.Query  = $query_Parameters.ToString()

        return $uri_Request

    }

    end{}

}



function Invoke-ApiRequest {
<#
    .SYNOPSIS
        Makes an API request
 
    .DESCRIPTION
        The Invoke-ApiRequest cmdlet invokes an API request to Datto API.
 
        This is an internal function that is used by all public functions
 
        As of 2023-08 the Datto v1 API only supports GET requests
 
    .PARAMETER method
        Defines the type of API method to use
 
        Allowed values:
        'GET'
 
    .PARAMETER resource_Uri
        Defines the resource uri (url) to use when creating the API call
 
    .PARAMETER uri_Filter
        Used with the internal function [ ConvertTo-QueryString ] to combine
        a functions parameters with the resource_Uri parameter.
 
        This allows for the full uri query to occur
 
        The full resource path is made with the following data
        $Datto_Base_URI + $resource_Uri + ConvertTo-QueryString
 
    .PARAMETER data
        Place holder parameter to use when other methods are supported
        by the Datto v1 API
 
    .PARAMETER allPages
        Returns all items from an endpoint
 
        When using this parameter there is no need to use either the page or perPage
        parameters
 
    .EXAMPLE
        Invoke-ApiRequest -method GET -resource_Uri '/account' -uri_Filter $uri_Filter
 
        Invoke a rest method against the defined resource using any of the provided parameters
 
        Example:
            Name Value
            ---- -----
            Method GET
            Uri https://api.datto.com/v1/account?accountId=12345&details=True
            Headers {Authorization = Bearer 123456789}
            Body
 
 
    .NOTES
        N\A
 
    .LINK
        https://celerium.github.io/Datto-PowerShellWrapper/site/Internal/Invoke-ApiRequest.html
 
#>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [ValidateSet('GET')]
        [String]$method = 'GET',

        [Parameter(Mandatory = $true)]
        [String]$resource_Uri,

        [Parameter(Mandatory = $false)]
        [Hashtable]$uri_Filter = $null,

        [Parameter(Mandatory = $false)]
        [Hashtable]$data = $null,

        [Parameter(Mandatory = $false)]
        [Switch]$allPages

    )

    begin{}

    process{

        $query_string = ConvertTo-QueryString -uri_Filter $uri_Filter -resource_Uri $resource_Uri

        Set-Variable -Name 'Datto_queryString' -Value $query_string -Scope Global -Force

        if ($null -eq $data) {
            $body = $null
        } else {
            $body = @{'data'= $data} | ConvertTo-Json -Depth $Datto_JSON_Conversion_Depth
        }

        try {
            $Api_Token = Get-DattoAPIKey -PlainText
            $Api_Token_base64 = [Convert]::ToBase64String( [Text.Encoding]::ASCII.GetBytes( ("{0}:{1}" -f ($Api_Token).PublicKey,($Api_Token).SecretKey) ) )

            $parameters = [ordered] @{
                "Method"    = $method
                "Uri"       = $query_string.Uri
                "Headers"   = @{ 'Authorization' = 'Basic {0}'-f $Api_Token_base64 }
                "Body"      = $body
            }
            Set-Variable -Name 'Datto_invokeParameters' -Value $parameters -Scope Global -Force

            if ($allPages){

                Write-Verbose "Gathering all items from [ $( $Datto_Base_URI + $resource_Uri ) ] "

                $page_number = 1
                $all_responseData = [System.Collections.Generic.List[object]]::new()

                do {

                    $parameters['Uri'] = $query_string.Uri -replace '_page=\d+',"_page=$page_number"

                    $current_page = Invoke-RestMethod @parameters -ErrorAction Stop

                    Write-Verbose "[ $page_number ] of [ $($current_page.pagination.totalPages) ] pages"

                        foreach ($item in $current_page.items){
                            $all_responseData.add($item)
                        }

                    $page_number++

                } while ($current_page.pagination.totalPages -ne $page_number - 1 -and $current_page.pagination.totalPages -ne 0)

            }
            else{
                $api_response = Invoke-RestMethod @parameters -ErrorAction Stop
            }

        }
        catch {

            $exceptionError = $_.Exception.Message
            Write-Warning 'The [ Datto_invokeParameters, Datto_queryString, & Datto_CmdletNameParameters ] variables can provide extra details'

            switch -Wildcard ($exceptionError) {
                '*404*' { Write-Error "Invoke-ApiRequest : [ $resource_Uri ] not found!" }
                '*429*' { Write-Error 'Invoke-ApiRequest : API rate limited' }
                '*504*' { Write-Error "Invoke-ApiRequest : Gateway Timeout" }
                default { Write-Error $_ }
            }

        }
        finally {
            [void] ( $Datto_Headers.Remove('Authorization') )
        }


        if($allPages){

            #Making output consistent
            if( [string]::IsNullOrEmpty($all_responseData.data) ){
                $api_response = $null
            }
            else{
                $api_response = [PSCustomObject]@{
                    data = $all_responseData
                }
            }

            return $api_response

        }
        else{ return $api_response }

    }

    end{}

}



function Get-InvokeMetaData {
<#
    .SYNOPSIS
        Gets various Api metadata values
 
    .DESCRIPTION
        The Get-InvokeMetaData cmdlet gets various Api metadata values from an
        Invoke-WebRequest to assist in various troubleshooting scenarios such
        as rate-limiting.
 
    .PARAMETER base_uri
        Define the base URI for the Datto API connection using Datto's URI or a custom URI.
 
        The default base URI is https://api.datto.com/v1
 
    .EXAMPLE
        Get-InvokeMetaData
 
        Gets various Api metadata values from an Invoke-WebRequest to assist
        in various troubleshooting scenarios such as rate-limiting.
 
        The default full base uri test path is:
            https://api.datto.com/v1
 
    .EXAMPLE
        Get-InvokeMetaData -base_uri http://myapi.gateway.example.com
 
        Gets various Api metadata values from an Invoke-WebRequest to assist
        in various troubleshooting scenarios such as rate-limiting.
 
        The full base uri test path in this example is:
            http://myapi.gateway.example.com/device
 
    .NOTES
        N\A
 
    .LINK
        https://celerium.github.io/Datto-PowerShellWrapper/site/Internal/Get-InvokeMetaData.html
#>


    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string]$base_uri = $Datto_Base_URI
    )

    begin { $resource_uri = "/bcdr/agent" }

    process {

        try {

            $Api_Token = Get-DattoAPIKey -plainText
            $Api_Token_base64 = [Convert]::ToBase64String( [Text.Encoding]::ASCII.GetBytes( ("{0}:{1}" -f ($Api_Token).PublicKey,($Api_Token).SecretKey) ) )

            $Datto_Headers.Add('Authorization', 'Basic {0}'-f $Api_Token_base64)

            $rest_output = Invoke-WebRequest -method Get -uri ($base_uri + $resource_uri) -headers $Datto_Headers -ErrorAction Stop
        }
        catch {

            [PSCustomObject]@{
                Method = $_.Exception.Response.Method
                StatusCode = $_.Exception.Response.StatusCode.value__
                StatusDescription = $_.Exception.Response.StatusDescription
                Message = $_.Exception.Message
                URI = $($Datto_Base_URI + $resource_uri)
            }

        }
        finally {
            [void] ( $Datto_Headers.Remove('Authorization') )
        }

        if ($rest_output){
            $data = @{}
            $data = $rest_output

            [PSCustomObject]@{
                ResponseUri             = $data.BaseResponse.ResponseUri.AbsoluteUri
                ResponsePort            = $data.BaseResponse.ResponseUri.Port
                StatusCode              = $data.StatusCode
                StatusDescription       = $data.StatusDescription
                'Content-Type'          = $data.headers.'Content-Type'
                'X-Request-Id'          = $data.headers.'X-Request-Id'
                'X-API-Limit-Remaining' = $data.headers.'X-API-Limit-Remaining'
                'X-API-Limit-Resets'    = $data.headers.'X-API-Limit-Resets'
                'X-API-Limit-Cost'      = $data.headers.'X-API-Limit-Cost'
                raw                     = $data
            }
        }

    }

    end {}
}