dev.core.odata.psm1

Import-Module "$PSScriptRoot\dev.core.utils.psm1" -DisableNameChecking;


#
# Public Methods
#

<#
.SYNOPSIS
  Invoke ODATA method with required parameters.
 
.PARAMETER $Method
  The HTTP method.
 
.PARAMETER $Uri
  The reuqest uri.
 
.PARAMETER $Body
  The body to post.
 
.PARAMETER $Headers
  The extra headers to sent along with the request.
 
.PARAMETER RawResponse
  Return the raw response instead of Odata values.
#>

function Invoke-ODataMethod {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, Position=0)][Uri]$Uri,
        [Microsoft.PowerShell.Commands.WebRequestMethod]$Method = [Microsoft.PowerShell.Commands.WebRequestMethod]::Get,
        [object]$Body,
        [hashtable]$Headers,
        [string]$UserAgent,
        [switch]$RawResponse);
    
    $RequestHeaders = @{};

    if ($Body)
    {
        $RequestHeaders["Content-Type"] = "application/json";
        
        # Convert the body to JSON string before sending the request.
        if (!($Body -is [string]))
        {
            $Body = ConvertTo-Json $Body -Compress;
        }

        Write-Verbose "Request Body:"
        Write-Verbose $Body;
        Write-Verbose "";
    }

    $RequestHeaders = Merge-HashTable -Table $RequestHeaders -With $Headers;
    if ($RequestHeaders)
    {
        Write-Verbose "Request Headers:"
        foreach ($Header in $RequestHeaders.Keys)
        {
            Write-Verbose "$Header : $($RequestHeaders[$Header])";
        }
        Write-Verbose "";
    }

    try
    {
        if ($UserAgent)
        {
           $Response = Invoke-RestMethod -Method $Method -Uri $Uri -Headers $RequestHeaders -Body $Body -UserAgent $UserAgent;
        }
        else
        {
           $Response = Invoke-RestMethod -Method $Method -Uri $Uri -Headers $RequestHeaders -Body $Body;
        }
    }
    catch [System.Net.WebException]
    {
        [System.Net.WebException]$exception = $_.Exception;

        $responseContent = Read-ResponseContent -Response $_.Exception.Response;
        $errorDetail = [PsCustomObject]@{
            Error = $_;
            Exception = $exception;
            Response = $exception.Response;
            ResponseBody = $responseContent;
        };

        Write-Output $errorDetail;
        
        return;
    }

    if ($RawResponse) 
    {
        Write-Output $Response;
    }
    else 
    {
        # Response could be $null if the server returns 204 NoContent status code.
        if ($Response)
        {
            if ($Response."@odata.context" -is [string])
            {
                Write-Verbose $Response."@odata.context";
            }

            if ($null -ne $Response.value)
            {
                # When odata returns multiple items, the items will be encapsulated in value property.
                foreach ($item in $Response.value) 
                {            
                    Write-Output $item;
                }
            }
            else 
            {
                # When odata returns single item, the response itself will the item.
                Write-Output $Response;
            }

            # Automatically fetch next page.
            if ($Response."@odata.nextLink" -is [string]) {
                $NextPageUri = $Response."@odata.nextLink";

                Invoke-ODataMethod -Method $Method -Uri $NextPageUri -Headers $RequestHeaders -Body $Body -UserAgent $UserAgent;
            }
        }
    }
}

function Read-ResponseContent 
{
    param ([System.Net.HttpWebResponse]$Response);

    $Stream = $Response.GetResponseStream();
    if (!$Stream.CanRead) {
        Write-Warning "Skip reading the resopnse stream because it cannot be read.";
    }
    elseif (!$Stream.CanSeek) {
        Write-Warning "Skip reading the resopnse stream because it cannot be seeked.";
    }
    else
    {
        $Stream.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null;
        $StreamReader = New-Object -TypeName System.io.StreamReader -ArgumentList $Stream;
        return $StreamReader.ReadToEnd();
    }
}

Export-ModuleMember -Function Invoke-ODataMethod;
Export-ModuleMember -Function Read-ResponseContent;