Quamotion.Cloud.psm1

$global:VerbosePreference = "Continue"

$prefix = "https://cloud.quamotion.mobi"
$script:accessToken = ""
$headers = @{}
$relativeUrl = ""

function Login-QuamotionCloud
{
    param (
        [Parameter(Mandatory=$true)]  [string] $apiKey)
    
    $body = @{
        "apiKey"=$apiKey
    }

    $response = Invoke-QMRestMethod -Uri "$script:prefix/api/login" -Body $body -Method Post
    $script:accessToken = $response.access_token

    $script:headers = @{
        "Authorization"="Bearer $script:accessToken";
        "Content-Type" = "application/json"
    }
    $response = Invoke-QMRestMethod -Uri "$script:prefix/api/project" -Headers $script:headers -Method Get
    $script:relativeUrl = $response.relativeUrl
}

function Get-CloudApps
{
    return Invoke-QMRestMethod -Uri "$($script:prefix)$($script:relativeUrl)api/app" -Headers $script:headers -Method Get
}

function Get-TestRunJobs
{
    param (
        [Parameter(Mandatory=$true)]  [string] $testRunId)

    return Invoke-QMRestMethod -Uri "$($script:prefix)$($script:relativeUrl)api/testRun/$testRunId/jobs" -Headers $script:headers -Method Get
}

function Get-TestRunSchedules
{
    return Invoke-QMRestMethod -Uri "$($script:prefix)$($script:relativeUrl)api/schedule" -Headers $script:headers -Method Get
}

function Delete-TestRunSchedule
{
    param (
        [Parameter(Mandatory=$true)]  [string] $scheduleId)
    
    Invoke-QMRestMethod -Uri "$($script:prefix)$($script:relativeUrl)api/schedule/$scheduleId" -Headers $script:headers -Method Delete
}

function Delete-TestRunSchedules
{
    $testRunSchedules = Get-TestRunSchedules

    Foreach ($schedule in $testRunSchedules)
    {
        Delete-TestRunSchedule -scheduleId $schedule.id
    }
}

function Upload-TestPackage
{
    param (
        [Parameter(Mandatory=$true)] [string] $path)
    
    Add-Type -AssemblyName System.Net.Http
    Add-Type -AssemblyName System.IO

    $fullPath = Resolve-Path $path
    if(-not (Test-Path $fullPath))
    {
        Throw "The file at $fullPath could not be found"
    }

    $fileName = Split-Path $path -leaf

    $url = "$($script:prefix)$($script:relativeUrl)api/testPackage"

    $httpClient = New-Object System.Net.Http.Httpclient
    
    $fileStream = [System.IO.File]::OpenRead($path)
    $fileStreamContent = New-Object System.Net.Http.StreamContent $fileStream

    $formData = New-Object System.Net.Http.MultipartFormDataContent
    $formData.Add($fileStreamContent, "files", $fileName);

    $requestMessage = New-Object System.Net.Http.HttpRequestMessage([System.Net.Http.HttpMethod]::Post, $url) 
    $requestMessage.Headers.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", $script:accessToken);
    $requestMessage.Content = $formData;
    $response = $httpClient.SendAsync($requestMessage).Result

    $fileStream.Dispose()
    $formData.Dispose()
    $httpClient.Dispose()
    $fileStreamContent.Dispose()
    $requestMessage.Dispose()

    return $response
}

function Schedule-TestRun {
    param (
        [Parameter(Mandatory=$true)]  [string] $deviceGroupName,
        [Parameter(Mandatory=$true)]  [string[]] $deviceGroupLabels,
        [Parameter(Mandatory=$true)]  [string] $testPackageName,
        [Parameter(Mandatory=$true)]  [string] $testPackageVersion,
        [string] $appId,
        [string] $appVersion,
        [string] $appOperatingSystem,
        [string] $schedule = "",
        [hashtable] $environmentVariables,
        [string] $scriptArguments,
        [string] $callbackEndpoint,
        [string] $callbackApiKey)

    $deviceGroupId = ([guid]::NewGuid()).Guid
    Write-Host $deviceGroupName
    
    if($callbackEndpoint)
    {
        $resultsCallBack = @{
            apiKey = $callbackApiKey;
            endPoint = $callbackEndpoint;
        }
    }

    if($appId)
    {
        [Array]$apps = Get-CloudApps
        $apps = $apps | Where-Object {$_.appId -eq $appId}
        if($appVersion)
        {
            $apps = $apps | Where-Object {$_.Version -eq $appVersion}
        }
        if($appOperatingSystem)
        {
            $apps = $apps | Where-Object {$_.operatingSystem -eq $appOperatingSystem}
        }

        if($apps.Count -eq 1)
        {
            $app = $apps[0]
        }
        if($apps.Count -lt 1)
        {
            Throw "The application is not uniquely specified."
        }
        if($apps.Count -eq 0)
        {
            Throw "No application matches the specification."
        }
    }

    $testRunRequest = @{
        testPackage = @{
            name = $testPackageName;
            version = $testPackageVersion;
        };
        app = $app;
        deviceGroup = @{
            deviceGroupId = $deviceGroupId;
            name = $deviceGroupName;
            devices = @(@{
                deviceSelectionId = $deviceGroupId;
                name = $deviceGroupName;
                tags = $deviceGroupLabels;
            });
        }
        testScriptParameters = $scriptArguments;
        schedule = $schedule;
        resultsCallBack = $resultsCallBack;
        testScriptEnvironmentVariables = $environmentVariables;
    };

    write-Host (ConvertTo-Json $testRunRequest -Depth 10)

    Invoke-QMRestMethod -Uri "$($script:prefix)$($script:relativeUrl)api/testRun" -Body (ConvertTo-Json $testRunRequest -Depth 10)  -Headers $script:headers -Method Post
}


function Invoke-QMRestMethod
{
    param()
    
    try
    {
        return Invoke-RestMethod @args -ErrorAction Stop
    }
    catch
    {
        Rethrow-CloudException $_
    }
}


function Invoke-QMWebRequest
{
    param()

    try
    {
     
        Invoke-WebRequest @args -ErrorAction Stop
    }
    catch
    {
        Rethrow-CloudException $_
    }
}

# Shared error handling
function Rethrow-CloudException($error)
{
    # If something went wrong in the HTTP pipeline, the result may not be JSON, or we may not have a
    # response at all (if the server was unavailable)

    # PowerShell "Core" stores error details in the ErrorDetails variable. We first check if this info exists, see
    # https://github.com/PowerShell/PowerShell/issues/5555
    # https://github.com/PowerShell/PowerShell/pull/3089
    $errorDetails = $error.ErrorDetails
    $exception = $error.Exception
    if($errorDetails)
    {
        Throw $error
    }
    elseif ($exception.Response -and $exception.Response.ContentType -and $exception.Response.ContentType.StartsWith("application/json"))
    {
        $result = $exception.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader($result)
        $reader.BaseStream.Position = 0
        $reader.DiscardBufferedData()
        $responseBody = $reader.ReadToEnd();
        
        Write-Debug($exception.Response.ContentType)
        Write-Debug($responseBody)
        $jsonResponseBody = ConvertFrom-Json $responseBody

        Throw $jsonResponseBody.value.message
    }
    else
    {
        Throw $exception
    }
}