common.psm1

#requires -version 5
# to enable System.Web classes
[System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
Add-Type -AssemblyName System.Web

.(commonLib) 
  

Function GetXtremErrorMsg([AllowNull()][object]$errordata){   

    #return $errordata.ToString()
    $ed = $errordata
    
    if ($ed.ErrorDetails -ne $null) {
        return $ed.ErrorDetails
    }

    try{ 
        $ed = $_.Exception.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader($ed)
        $responseBody = $reader.ReadToEnd(); 
        $errorcontent = $responseBody | ConvertFrom-Json
        $errormsg = $errorcontent.message

        if ($errormsg -eq $null) {
            $errorcontent = $errordata.Exception;
        }
        return $errorcontent
    }
    catch {
        Write-Host ""
        return $errordata.ToString()
    } 
}

######### REQUEST HELPERS #########

#Generates Header to be used in requests to XtremIO
Function GetXtremAuthHeader([string]$username,[string]$password) {

  $basicAuth = ("{0}:{1}" -f $username,$password)
  $basicAuth = [System.Text.Encoding]::UTF8.GetBytes($basicAuth)
  $basicAuth = [System.Convert]::ToBase64String($basicAuth)
  $headers = @{Authorization=("Basic {0}" -f $basicAuth)}

  return $headers
}

Function GetXtremRawRequest{
[CmdletBinding()]
    Param(
    [Parameter()]
    [object]$Session =  (Get-XtremDefaultSession),
    [Parameter(Mandatory=$true)]
    [String]$URI,
    [Parameter()]
    [switch]$ShowRest)

    $Username = $Session._XtremUsername
    $XmsName = $Session._XtremName
    $XtremClusterName = $Session._XtremDefaultCluster
    $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Session._XtremPassword)
    $Password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
    

    try
    {
        ##Construct Auth Header and full URI
        $Header = GetXtremAuthHeader -username $username -password $password

        $result = (Invoke-RestMethod -Method "POST" -Uri $Uri -Headers $Header)
        
    } catch {
        $err = GetXtremErrorMsg -errordata $_
        throw $err
    }

     
    return $result
}


function showRestIfset(){
    if ($ShowRest) {
        $show = "$($Method): $Uri"
        if ($Body -and $Body.length -gt 2){
            $show += "`nBody: $Body"
        }
        Write-Host $show
    }
}

# Build the URL For a request.
Function BuildURIRequest {
    Param(
    [String]$Endpoint,
    [Parameter()]
    [String]$XmsName,
    [Parameter()]
    $XtremClusterName,
    [Parameter()]
    [String[]]$Properties = $null,
    [Parameter()]
    [String]$GetProperty = $null,
    [Parameter()]
    [Array]$Filters = $null,
    [Parameter()]
    [switch]$Full = $false,
    [Parameter()]
    [switch]$Events = $false)
    $BaseUri = "https://$XmsName/api/json/v3"

    $PropertyString = ''
    $FilterString = ''
    $ClusterString = '' 

    $v2UriExceptions = @(
        "/types/snapshots", 
        "/types/consistency-group-volumes"
    )



    if ($Endpoint -in $v2UriExceptions) {
        $BaseUri = $BaseUri -replace ("/v3$", "/v2")
    }


    if ($Events) {
        if ($Property.Count -ne 0) {
            $PropertyString += "prop-list=["
            Foreach($Property in $Properties) {
                if($Property -eq $Properties[($Property.Length -1)]) {
                    $PropertyString += $Property+"]"
                } 
                else
                {
                    $PropertyString += $Property+","
                }
            }
                    
        }
    } elseif($Property -and !$Full) {

        $propsArray = [System.Web.HttpUtility]::ParseQueryString('') 
        #If properties were specified, builds a string for querying those
        Foreach($Property in $Properties) {
            $propsArray.Add( "prop", $Property );
        }
        $PropertyString = $propsArray.ToString()
    } else {
        $PropertyString = $null
    }

    # Adding filters
    if($Filter)
    {
        Foreach($Filter in $Filters) {
            if($Filter -eq $Filters[($Filters.Length-1)]) {
                $FilterString += 'filter=' + $Filter
            }
            else {
                $FilterString += 'filter=' + $Filter + '&'
            }
        }
    }
    #If a cluster name was specified, builds a string for that
    if($XtremClusterName){
        if($XtremClusterName -is [int]){
            $nameFromTable = $global:XtremDefaultSessionObject._XtremClustersTable.$XtremClusterName
            if($nameFromTable){
                $XtremClusterName = $nameFromTable
            }else{
                $XtremClusterName = (Get-XtremCluster -Index $XtremClusterName).name
            }
        }

        $ClusterString = 'cluster-name='+$XtremClusterName
        if ($Endpoint -like "*remote-protections*"){
            $ClusterString = 'cluster-id=' + $XtremClusterName
        }

    } else {
        $ClusterString = $null 
    }

    # if($PropertyString -or $Full) {
        #another special case for performance calls...
        if($Endpoint -like "*performance*" -or $Endpoint -like "*event*") {
            $PropertyString = $PropertyString
        } else {
            $PropertyString = 'full=1&'+$PropertyString
        }
    # }


    #build URI
    $Uri = $baseUri + $Endpoint + '?'

    if ($GetProperty) {
        $Uri += $GetProperty + '&'
    }

    if ($ClusterString) {
        if ($Endpoint -notmatch 'user-accounts|r-snapshot-sets') {
            $Uri += $ClusterString + '&'
        }
    }

    if ($PropertyString) {
        $Uri += $PropertyString +'&'
    }

    if ($FilterString) {
        $Uri += $FilterString
    }

    #Check if we have left overs.
    if ($Uri.EndsWith('?') -or $Uri.EndsWith('&')) {
        $Uri = $Uri.Substring(0,$Uri.Length-1)
    }

    return $Uri;
}

Function XtremGETRequest ($Header, $Endpoint, $URI) {

    #special way of handling performance calls, special JSON serializer needs to be used as payload is large
    if($Endpoint -like "*performance*") {

        $jsonserial= New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer 
        $jsonserial.MaxJsonLength = [int]::MaxValue
        $data = $jsonserial.DeserializeObject((Invoke-WebRequest -Method $Method -Uri $Uri -Headers $Header -UseBasicParsing))
                        
        return $data
    }

    # $result = (Invoke-RestMethod -Method "GET" -Uri $Uri -Headers $Header )
    $result = InvokeAsyncAnim { 
        param($Uri, $Header, $Body) 
            try {
                $res1 = (Invoke-RestMethod -Method "GET" -Uri $Uri -Headers $Header -ErrorAction Stop)
            } catch {
                $res1 = 'Error: '
                if(!$_.Exception.Response){
                    $res1 += $_.Exception.Message
                }else{
                    $ed = $_.Exception.Response.GetResponseStream()
                    $reader = New-Object System.IO.StreamReader($ed)
                    $responseBody = $reader.ReadToEnd(); 
                    $errorcontent = $responseBody | ConvertFrom-Json
                    $res1 += '' + $errorcontent.error_code + ' ' + $errorcontent.message; 
                }
            }
            return $res1 
        }

    $result = findSelectedObject  

    # Weird fix but it helps to formatOutput of single objects
    if ($result -and $result.getType().name -eq 'PSCustomObject'){
        $result = $result | ConvertTo-Json | ConvertFrom-Json
    }

    return $result
}

function InvokeAsyncAnim ($code){
    initAnimation
    # $rsPool = [runspacefactory]::CreateRunspacePool(1, 3)
    # $rsPool.Open()
    $PSinstance = [powershell]::Create().AddScript($code).AddArgument($Uri).AddArgument($Header).AddArgument($Body)
    # $PSinstance.RunspacePool = $rsPool
    $Handle = $PSinstance.BeginInvoke()
    for ($i = 0; $i -lt 99990; $i++) {
        if (!$Handle.IsCompleted) {
            doAnimation
        } else {
            $res = $PSinstance.EndInvoke($Handle)
            $PSinstance.Dispose()
            # $rsPool.Close()
            # $rsPool.Dispose()
            endAnimation
            return $res
        }
    }
    $PSinstance.Dispose()  
}

$script:consoleName = $null
function isConsoleHost{
    if ($consoleName){
        return ($consoleName -eq 'ConsoleHost')
    }else{
        $script:consoleName = $host.Name
        return isConsoleHost
    }
}

function initAnimation {
    if (isConsoleHost) {
        [int]$script:l = [Console]::CursorLeft
        [int]$script:t = [Console]::CursorTop
        
        [Console]::CursorVisible = $false
        [int]$script:animSpan = 8
        [int]$script:animCount = 0
        [int]$script:animDir = 1
    }
}

function endAnimation{
    if (isConsoleHost) {
        clearAnimationText
        [Console]::CursorVisible = $true
    }
}

function clearAnimationText {
    if (isConsoleHost) {
        [Console]::CursorLeft = $l
        [Console]::CursorTop = $t
        Write-Host "$(' '*$animCount)" -NoNewline
        [Console]::CursorLeft = $l
        [Console]::CursorTop = $t
    }
}

function doAnimation {
    if (isConsoleHost) {
        clearAnimationText
        $script:animCount += $animDir
        Write-Host "$('.'*$animCount)" -NoNewline

        if ($animDir -eq 1 -and $animCount -ge $animSpan) {
            $script:animDir = -1
        }
        if ($animDir -eq -1 -and $animCount -le 0) {
            $script:animDir = 1
        }
    }
    Start-Sleep -Milliseconds 50 
}

function findSelectedObject{
    if(!$result) {return ''}
    if ($ObjectSelection) {
        return $result.$ObjectSelection
    } else {
        if ($result.getType().name -eq 'String') {
            return $result
        }
        foreach ($property in $result.PSObject.Properties) {
            if (($property.name -ne 'params') -and ($property.name -ne 'links')) {
                $result = $property.value
                break
            }
        }
        return $result
    }
}

Function XtremPOSTRequest {
    Param(
    [Parameter(Mandatory=$true)]
    [Object]$Header,
    [Parameter(Mandatory=$true)]
    [String]$Uri,
    [Parameter()]
    [String]$Body)

    # $data = (Invoke-RestMethod -Method "POST" -Uri $Uri -Body $Body -Headers $Header)
    $data = InvokeAsyncAnim { 
        param($Uri, $Header, $Body) 
            try {
                $res1 = (Invoke-RestMethod -Method 'POST' -Uri $Uri -Headers $Header -Body $Body)
            } catch {
                $res1 = 'Error: '
                if(!$_.Exception.Response){
                    $res1 += $_.Exception.Message
                }else{
                    $ed = $_.Exception.Response.GetResponseStream()
                    $reader = New-Object System.IO.StreamReader($ed)
                    $responseBody = $reader.ReadToEnd(); 
                    $errorcontent = $responseBody | ConvertFrom-Json
                    $res1 += '' + $errorcontent.error_code + ' ' + $errorcontent.message; 
                }
            }
            return $res1 
        }

    if(!$data.links){
        return $data
    }

    $time = Get-Date
    $href = $data.links.href
    # Sometimes there are more than one object being created, so we'll return them all in an array
    if($href.count -gt 1) {
        $arr = @()

        for($i = 0; $i -lt $href.count; $i++) {
            # Resolve Each object
            $objectIndex = ($href[$i].Split('/')[-1])
            $ResponseObject = new-object PSObject

            $ResponseObject | add-member -Type NoteProperty -Name Guid -Value ([int]$objectIndex)
            $ResponseObject | add-member -type NoteProperty -Name CreationTime -Value $time

            $arr += $ResponseObject
        }

        return $arr
    } else  {  
        $objectIndex = $href.Split('/')[-1]
        $ResponseObject = new-object PSObject
        if ($objectIndex.GetType().Name -eq "String") {
            $ResponseObject | add-member -type NoteProperty -Name Guid -Value ([String]$objectIndex)
        } else {
            $ResponseObject | add-member -type NoteProperty -Name Guid -Value ([int]$objectIndex)
        }
        $ResponseObject | add-member -type NoteProperty -Name CreationTime -Value $time
    
        return $ResponseObject
    }
}

Function XtremPUTRequest {
    Param(
    [Parameter(Mandatory=$true)]
    [Object]$Header,
    [Parameter(Mandatory=$true)]
    [String]$Uri,
    [Parameter()]
    [Array]$Body)

    # $res = (Invoke-WebRequest -Method "PUT" -Uri $Uri -Headers $Header -Body $Body -UseBasicParsing)
    # $res = '' + $res.StatusCode + ' ' + $res.StatusDescription
    $res = InvokeAsyncAnim { 
        param($Uri, $Header, $Body) 
            try {
                $res1 = (Invoke-WebRequest -Method 'PUT' -Uri $Uri -Headers $Header -Body $Body -UseBasicParsing)
                $res1 = '' + $res1.StatusCode + ' ' + $res1.StatusDescription; 
            } catch {
                $res1 = 'Error: '
                if(!$_.Exception.Response){
                    $res1 += $_.Exception.Message
                }else{
                    $ed = $_.Exception.Response.GetResponseStream()
                    $reader = New-Object System.IO.StreamReader($ed)
                    $responseBody = $reader.ReadToEnd(); 
                    $errorcontent = $responseBody | ConvertFrom-Json
                    $res1 += '' + $errorcontent.error_code + ' ' + $errorcontent.message; 
                }
            }
            return $res1 
        }
    return $res
}

Function XtremDELETERequest {
    Param(
    [Parameter(Mandatory=$true)]
    [Object]$Header,
    [Parameter(Mandatory=$true)]
    [String]$Uri,
    [Parameter()]
    [Array]$Body)

    $res = InvokeAsyncAnim { 
        param($Uri, $Header, $Body) 
            try {
                $res1 = (Invoke-WebRequest -Method 'DELETE' -Uri $Uri -Headers $Header -Body $Body -UseBasicParsing)
                $res1 = '' + $res1.StatusCode + ' ' + $res1.StatusDescription; 
            } catch {
                $res1 = 'Error: '
                if(!$_.Exception.Response){
                    $res1 += $_.Exception.Message
                }else{
                    $ed = $_.Exception.Response.GetResponseStream()
                    $reader = New-Object System.IO.StreamReader($ed)
                    $responseBody = $reader.ReadToEnd(); 
                    $errorcontent = $responseBody | ConvertFrom-Json
                    $res1 += '' + $errorcontent.error_code + ' ' + $errorcontent.message; 
                }
            }
            return $res1 
        }
    # $res = (Invoke-WebRequest -Method "DELETE" -Uri $Uri -Headers $Header -Body $Body -UseBasicParsing -ErrorAction Continue)
    # $res = '' + $res.StatusCode + ' ' + $res.StatusDescription

    return $res
}

#Builds the REST request
Function NewXtremRequest {
[cmdletbinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [ValidateSet('GET','PUT','POST','DELETE')]
        [String]$Method,
        [Parameter(Mandatory=$true)]
        [String]$Endpoint,
        [Parameter()]
        $XtremClusterName,
        [Parameter()]
        [object]$Session,
        [Parameter()]
        [Object]$Body,
        [Parameter()]
        [Alias("Properties")]
        [String[]]$Property = $null,
        [Parameter()]
        [String]$ObjectSelection = '',
        [Parameter()]
        [String]$GetProperty = $null,
        [Parameter()]
        [Alias("Filters")]
        [Array]$Filter = $null,
        [Parameter()]
        [switch]$ShowRest,
        [Parameter()]
        [switch]$Multi = $false,
        [Parameter()]
        [switch]$Full = $false

    )

    if($Session._XtremUsername) {
        $Username = $Session._XtremUsername
        $XmsName = $Session._XtremName
        if ($null -eq $XtremClusterName) {
            $XtremClusterName = $Session._XtremClusterName
        }

        $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Session._XtremPassword)
        $Password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
    } else {
        throw "Session was not created prior to this call"
    }
    
    $err = ""
    $result = ""
    #Special case...tags doesn't take cluster name and neither does performance
    if($Body -like "*cluster-id*")
    {
        $XtremClusterName = $null
    }
    if (($Endpoint -like '*xms*' -or $Endpoint -like '*clusters*' -or $Endpoint -like '*tags*' -or $Endpoint -like '*performance*') -and ($Method -eq 'GET' -or $Method -eq 'DELETE' ))
    {
        $XtremClusterName = $null
    }

    if (($Endpoint -like "*/remote-protections*") -and ($Method -eq 'PUT' )){
        $XtremClusterName = $null
    }

    if(!$Body){ $Body = '{}' }

    try  {
        ##Construct Auth Header and full URI
        $Header = GetXtremAuthHeader -username $username -password $password

        if ($Endpoint -like '*event*'){
            $Uri = BuildURIRequest -endpoint $Endpoint -XmsName $XmsName -XtremClusterName $XtremClusterName -Properties $Property -GetProperty $GetProperty -Filters $Filter -Events
        } else {
            $Uri = BuildURIRequest -endpoint $Endpoint -XmsName $XmsName -XtremClusterName $XtremClusterName -Properties $Property -GetProperty $GetProperty -Filters $Filter -Full:$Full.IsPresent
        }
        
        showRestIfset

        $result = switch ($Method){
            'GET' { XtremGETRequest $Header $Endpoint $Uri } 
            'POST' { XtremPOSTRequest -Header $Header -Uri $Uri -Body $Body } 
            'PUT' { XtremPUTRequest -Header $Header -Uri $Uri -Body $Body } 
            'DELETE' { XtremDELETERequest -Header $Header -Uri $Uri -Body $Body } 
        }

    } catch {
        $err = GetXtremErrorMsg -errordata $_
        throw $err
    }
    return $result
}

# function decide if we should use a name or id and set up the parameters based on the choice.
Function SetParametersForRequest
{
    Param(
    [Parameter(Mandatory=$true)]
    $Route, 
    [Parameter(Mandatory=$true)]
    $Name
    )
    
    If ($Name.GetType().Name -eq "Int32" -or $Name.GetType().Name -eq "Object[]")
    {
        $Route = $Route + "/"+$Name
        return $Route,$null
    }
    $GetProperty = 'name='+ $Name
    return $Route,$GetProperty
}

function Set-XtremTrustAllCertsPolicy { 
    <#
    .DESCRIPTION
        Set CertificatePolicy to trust all certs. This will remain in effect for this session.
    .NOTES
        Not sure where this originated. A few references:
            http://connect.microsoft.com/PowerShell/feedback/details/419466/new-webserviceproxy-needs-force-parameter-to-ignore-ssl-errors
            http://stackoverflow.com/questions/11696944/powershell-v3-invoke-webrequest-https-error
 
    .EXAMPLE
        Set-XtremTrustAllCertsPolicy
 
    #>

    [CmdletBinding()]
    Param(
        [Parameter()]
        [switch]$SilentWarnings=$false
    )
    
    if([System.Net.ServicePointManager]::CertificatePolicy.ToString() -eq "TrustAllCertsPolicy") {
        # Write-Host "Current policy is already set to TrustAllCertsPolicy"
    } else {
        add-type @"
            using System.Net;
            using System.Security.Cryptography.X509Certificates;
            public class TrustAllCertsPolicy : ICertificatePolicy {
                public bool CheckValidationResult(
                    ServicePoint srvPoint, X509Certificate certificate,
                    WebRequest request, int certificateProblem) {
                    return true;
                }
            }
"@

        [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
    }

    # if ($SilentWarnings -ne $true) {
    # Write-Warning "Please be aware that certificate validation was removed for the entire session."
    # }
 }

 function Get-XtremTypes {
     <#
    .DESCRIPTION
        This command (GET-XtremType) displays the list of all supported objects.
    .NOTES
 
    .EXAMPLE
        Get-XtremTypes
 
    #>

    [CmdletBinding()]
    Param(
        [parameter()]
        $XtremClusterName = (Get-XtremDefaultSession)._XtremClusterName,
        [Parameter()]
        [object]$Session =  (Get-XtremDefaultSession),
        [Parameter()]
        [switch]$ShowRest
    )

    $Route = '/types'
    $ObjectSelection = 'children'
    
    $result = NewXtremRequest -Method GET -endpoint $Route -Session $Session -SelectProps ('name') -XtremClusterName $XtremClusterName -ObjectSelection $ObjectSelection -ShowRest:$ShowRest.IsPresent 
    
    return $result 
 }

 Function AddIfExists($name,$value,$list) {
    <# Add a value to a list only if exists #>

    try {
        if ($value.GetType().Name -eq "Int32") {
            if ($null -ne $value) {
                $list.Add($name,$value)
                return
            }
        }

        if ($value) {
            $list.Add($name,$value)
        }
    } Catch { }
    return
}

Function IsConfirmed($confirmFlag) {
    # if flag suggest we always confirm (SDK state) confirm.
    if (($confirmFlag -eq $false))
    {
        return $true;
    }
    $confirm = Read-Host("Are you sure you want to proceed (y/n) ? " );

    if ($confirm -ieq 'y') {
        return $true
    }
    return $false
}
Function BuildXtremJson($list) {
<#
    return a Json from a list
#>

    return $list | ConvertTo-Json
}


Function Export-XtremCSV{
[Cmdletbinding()]
Param(
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
    [PSObject]$PerformanceData,
    [Parameter(Mandatory=$true)]
    [String]$ExportPath
)

    $data = $PerformanceData
    $members = $data.members
    $counters = $data.counters

    $dataarray = @()

    for($i = 0; $i -lt $counters.count; $i++) {

        $dataobj = New-Object System.Object
        [datetime]$EpochTime = '1970-01-01 00:00:00'

        for($j = 0; $j -lt $members.count; $j++) {
            #This is how I'm dealing with epoch time...
            if($j -eq 0) {
                $time = $EpochTime.AddMilliSeconds($counters[$i][$j])
                $dataobj | Add-Member -type NoteProperty -name $members[$j] -Value $time
            }
            else {
                $dataobj | Add-Member -type NoteProperty -name $members[$j] -Value $counters[$i][$j]
            }
        }
   
    $dataarray = $dataarray + $dataobj
    }

    $sort_order = "timestamp"
   
    $dataarray |sort $sort_order | Export-Csv $ExportPath -NoTypeInformation
}
<#
 
function Build-RESTcommandWidthDynamicParameter() {
    <#
 
    .DESCRIPTION
    Build step-by-step arbitrary REST request and get the results
 
    .PARAMETER type
    Type of the command
 
    .PARAMETER category
    Request category
     
    .PARAMETER request
    Request endpoint
 
    .PARAMETER entity
    Name of entity.
     
    .EXAMPLE
    Build-RESTcommand use [TAB] or [Ctrl+Space]
     
    .EXAMPLE
    Build-RESTcommand GET types volumes v1
 
 
# # >
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory = $true)]
        [ValidateSet("GET", "POST", "PUT", "DELETE")]
        [string]$type,
        [Parameter(Mandatory = $true)]
        [ValidateSet("types", "commands")]
        [string]$category
    )
    DynamicParam {
        $param1Attr = New-Object System.Management.Automation.ParameterAttribute
        $param2Attr = New-Object System.Management.Automation.ParameterAttribute
        $param1Attr.Mandatory = $true
        $param2Attr.Mandatory = $false
        $attrCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]
        $attrCollection2 = new-object System.Collections.ObjectModel.Collection[System.Attribute]
        $attrCollection.Add($param1Attr)
        $attrCollection2.Add($param2Attr)
        $vals = getRequestsList $type $category
        $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        if ($vals) {
            $attrCollection.Add((New-Object System.Management.Automation.ValidateSetAttribute($vals)))
            $p1 = New-Object System.Management.Automation.RuntimeDefinedParameter('request', [string], $attrCollection)
            $paramDictionary.Add('request', $p1)
        }
        if ($PSBoundParameters.request){
            $vals2 = getEntities $type $category $PSBoundParameters.request
            if ($vals2) {
                $attrCollection2.Add((New-Object System.Management.Automation.ValidateSetAttribute($vals2)))
                $paramDictionary.Add('entity', $p2)
            }
     
            $p1 = New-Object System.Management.Automation.RuntimeDefinedParameter('entity', [string], $attrCollection2)
        }
         
  
         
         
 
        return $paramDictionary
 
    }
 
    Process {
        $request = $PSBoundParameters.request
        $ent = $PSBoundParameters.entity
        return "$request $ent heh"
 
    }
}
#>


# function Build-RESTcommand {
# <#

# .DESCRIPTION
# Build step-by-step arbitrary REST request and get the results

# .PARAMETER type
# Type of the command (GET|POST|PUT|DELETE)

# .PARAMETER category
# Request category (type|command)
    
# .PARAMETER endpoint
# Request endpoint (ex. /volumes)

# .PARAMETER entity
# Name of entity (ex. name of volume)
    
# .EXAMPLE
# Build-RESTcommand use [TAB] or [Ctrl+Space]
    
# .EXAMPLE
# Build-RESTcommand GET types volumes v1


# #>
# [CmdletBinding()]
# param (
# [Parameter(Mandatory = $true)]
# [ValidateSet("GET", "POST", "PUT", "DELETE")]
# [string]$type,
# [Parameter(Mandatory = $true)]
# [ValidateSet("types", "commands")]
# [string]$category,
# [Parameter(Mandatory = $true)]
# [string]
# [ArgumentCompleter( {
# param ( $commandName,
# $parameterName,
# $wordToComplete,
# $commandAst,
# $fakeBoundParameters )

# if ($fakeBoundParameters.ContainsKey('category')) {
# $Route = "/$($fakeBoundParameters.category)"
# $vals = (NewXtremRequest -Method GET -Endpoint $Route -Session (Get-XtremDefaultSession) -XtremClusterName '' -ObjectSelection 'children').name

# if (!$vals) {
# return "No_results_for_$Route"
# }else{
# $vals | Where-Object {
# $_ -like "$wordToComplete*"
# }
# }
# }
# } )]
# $endpoint,
# [Parameter(Mandatory = $false)]
# [string]
# [ArgumentCompleter( {
# param ( $commandName,
# $parameterName,
# $wordToComplete,
# $commandAst,
# $fakeBoundParameters )

# if ($fakeBoundParameters.ContainsKey('category') -and $fakeBoundParameters.ContainsKey('endpoint')) {
# $Route = "/$($fakeBoundParameters.category)/$($fakeBoundParameters.endpoint)"
# $session = (Get-XtremDefaultSession)
# $vals = (NewXtremRequest -Method GET -Endpoint $Route -Session $session -XtremClusterName $session._XtremClusterName -GetProperty "prop=guid&full=1").name

# if(!$vals){
# return "No_results_for_$Route"
# }else{
# $vals | Where-Object {
# $_ -like "$wordToComplete*"
# } | ForEach-Object {
# New-Object -Type System.Management.Automation.CompletionResult -ArgumentList $_, "| $_ ", "ParameterValue", $_
# }
# }
# }
# } )]
# $entity
# )

# $Route = "/$category/$endpoint"
# if($type -eq 'GET'){
# $getProps = "full=1"
# if($entity){
# $getProps += "&filter=name:eq:$entity"
# }
# Write-Host "$type $Route`?$getProps"
# $session = (Get-XtremDefaultSession)
# $res = NewXtremRequest -Method $type -Endpoint $Route -Session $session -XtremClusterName $session._XtremClusterName -GetProperty $getProps
    
# $res
# }
# }

function Invoke-XtremGetRequest {
    <#
    .DESCRIPTION
    Executes arbitrary GET request and get the results
 
    .PARAMETER path
    Endpoint path.
 
    .PARAMETER Property
    Query parameters array
 
    .PARAMETER Filter
    Filter by a property value.
     
    .EXAMPLE
    Build-RESTcommand use [TAB] or [Ctrl+Space]
  #>

    [CmdletBinding()]
    [Alias('xmsGet')]
    Param(
        [Parameter()]
        [string]$path,
        [Parameter()]
        [string[]]$Property,
        [parameter()]
        [Alias("Filters")]
        [array]$Filter
    )
    $result = NewXtremRequest -Method GET -Session (Get-XtremDefaultSession) -Endpoint $path -Properties $Property -Filters $Filter
    return $result
}

function Get-XMScommand{
    <#
    .DESCRIPTION
    Find XtremIO XMS command step-by-step using suggestions and autocomplete via [TAB] or [Ctrl+Space]
 
    .PARAMETER entity
    An entity against which you want to call an action.
 
    .PARAMETER action
    An action (verb) or action with subentity.
 
     
    .EXAMPLE
    xms Volume Modify (use [TAB] or [Ctrl+Space] to get suggestions)
 
  #>

    [CmdletBinding()]
    [Alias('xms')]
    Param(
        [Parameter()]
        [Argumentcompleter( { ' '; (New-Object -ComObject wscript.shell).SendKeys('{backspace}{tab}') })]
        [string]$entity,
        [Parameter()]
        [Argumentcompleter( { ' '; (New-Object -ComObject wscript.shell).SendKeys('{backspace}{tab}') })]
        [string]$action,
        [Parameter()]
        [string]$goToCommand
    )
    if(!$entity) {return}
    if (!$action) { return}

    $theAlias = 'xms' + $entity + $action
    if (($xmsEntitiesList -contains $entity) -and (test-path alias:$theAlias)) {
        # move current entity to the first position
        # [void]($global:xmsEntitiesList.Remove($entity))
        # [void]($global:xmsEntitiesList.Insert(0, $entity))

        (New-Object -co wscript.shell).SendKeys($theAlias)
    }

}
$global:xmsEntitiesList = [System.Collections.ArrayList]('Volume', 'ConsistencyGroup', 'InitiatorGroup', 'Initiator', 'RemoteProtection', 'RetentionPolicy', 'QoS', 'Tag', 'Snapshot', 'SnapshotSet', 'LocalProtection', 'Copy', 'Session', 'Scheduler')

$XmsEntityCompleter = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $vals = $xmsEntitiesList

    if ($vals) {
        $vals | Where-Object {
            $_ -like "$wordToComplete*"
        }
    }
}

$XmsActionCompleter = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $action = Get-Alias -Name "xms$($fakeBoundParameters.entity)*" | select name | where name -ne 'xms' | where name -notlike "xms$($fakeBoundParameters.entity)Group*" | where name -notlike "xms$($fakeBoundParameters.entity)Set*" | foreach { $_.name -replace "xms$($fakeBoundParameters.entity)", '' }
    $vals = $action
        
    if ($vals) {

        [void]($global:xmsEntitiesList.Remove($fakeBoundParameters.entity))
        [void]($global:xmsEntitiesList.Insert(0, $fakeBoundParameters.entity))

        $vals | Where-Object {
            $_ -like "$wordToComplete*"
        }
    }
}
$XmsGoToCommandCompleter = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    ' '
    $keys = '{backspace}' * [int]$($commandAst.ToString().length+2)
    $keys += 'xms' + $fakeBoundParameters.entity + $fakeBoundParameters.action + ' {tab}'
    (New-Object -co wscript.shell).SendKeys($keys)
}
Register-ArgumentCompleter -Command Get-XMScommand -Param entity -ScriptBlock $XmsEntityCompleter
Register-ArgumentCompleter -Command Get-XMScommand -Param action -ScriptBlock $XmsActionCompleter
Register-ArgumentCompleter -Command Get-XMScommand -Param goToCommand -ScriptBlock $XmsGoToCommandCompleter

# function getListOfAllCommands {
# $allCommands = [System.Collections.ArrayList]((Get-Command -mod Xtremlib3).name)
# $allAliases = [System.Collections.ArrayList]@()

# foreach ($cmd in $allCommands) {
# $al = (Get-Alias -Definition "$cmd" -ErrorAction 0).Name
# foreach ($alias in $al) {
# if ($alias.length -le 3) { continue }
# if ($alias) {
# [void]$allAliases.Add($alias)
# }
# }
# }
# $script:allCmdAls = $allCommands + $allAliases
# }

# function getCmdletUserParams($mod){
# $appParams = (Get-Command $parts[0]).Parameters
# $paramsList = $appParams.Values.Name
# $userProp = [ordered]@{ }
# $ignoreList = @('XtremClusterName', 'Session', 'ShowRest', 'Full')
# foreach ($nm in $paramsList) {
# if($mod -eq 'main' -and $nm -in $ignoreList){ continue }
# if ($mod -eq 'main' -and $nm -eq 'Verbose') { break }
# $userProp[$nm] = $appParams[$nm].SwitchParameter
# }
# return $userProp
# }
function getCmdletUserParams($mod) {
    $appParams = (Get-Command $parts[0]).Parameters
    $paramsList = $appParams.Values.Name
    $userProp = [ordered]@{ }
    $ignoreList = @('XtremClusterName', 'Session', 'ShowRest', 'Full')
    foreach ($nm in $paramsList) {
        if ($mod -eq 'main' -and $nm -in $ignoreList) { continue }
        if ($mod -eq 'main' -and $nm -eq 'Verbose') { break }
        $isSwitch = $appParams[$nm].SwitchParameter
        $paramSet = $appParams[$nm].ParameterSets.Keys
        if ($paramSet -eq '__AllParameterSets') { $paramSet = $false }
        $userProp[$nm] = @{ 
            isSwitch = $isSwitch
            paramSet = $paramSet
        }
    }
    return $userProp
}

function getNextParamToInsert ($isReverse) {
    $cmdParamsArr = ([array]$userParams.Keys)

    if ($line -notmatch " -\S+") {
        return $cmdParamsArr[0]
    }
    $lastParam = ([array](($line | Select-String " -\S+" -AllMatches).Matches.Value))[-1]
    $lastParam = ($lastParam -replace '-', '').Trim()
    if ($isReverse) {
        [array]::Reverse($cmdParamsArr)
    }
    $usedParamSets = [System.Collections.ArrayList]@()
    foreach ($nm in $cmdParamsArr) {
        $paramSet = $userParams[$nm].paramSet
        if ($useNext -and ($line -notlike "* -$nm*")) {
            if ($paramSet -and ($usedParamSets -and $paramSet -notin $usedParamSets)) { continue }
            return $nm
        }
        if ($nm -eq $lastParam) {
            $useNext = $true
        }
        if ($paramSet){
            if($line -like "* -$nm* "){
                [void]$usedParamSets.Add($paramSet)
            }
        }
    }
    return ''
}

function countUsedParams($parts){
    $count = 0
    foreach ($part in $parts){
        if($part -eq $parts[0]){continue}
        if($part -notlike "-*"){
            # is value
            $count++
        }elseif ( $userParams[($part -replace '-','')] ){
            # is switch
            $count++
        }
    }
    return $count
}

function lineHandleQuotedStings{
    # replace long strings in quotes, they may contain spaces
    $line = $line -replace ("\'[^\']+\'"), "_"
    $line = $line -replace ('\"[^\"]+\"'), '_'
    return $line
}

# Add-Type -AssemblyName PresentationCore, PresentationFramework

# function setKeyHandlers{
    # setting keyboard shortcuts if PSReadLine is installed, it is by default since PS v.5.0
    if (Get-Command Set-PSReadLineKeyHandler -errorAction SilentlyContinue)  { 
        Set-PSReadLineKeyHandler -Chord Tab -ScriptBlock { tabHandle $args } 
        Set-PSReadLineKeyHandler -Chord Shift+Tab -ScriptBlock { shiftTabHandle $args }
        Set-PSReadLineKeyHandler -Chord Backspace -ScriptBlock { backSpaceHandle $args }
        Set-PSReadLineKeyHandler -Chord Ctrl+Spacebar -ScriptBlock { cntrlSpaceHandle $args }
        Set-PSReadLineKeyHandler -Chord Spacebar -ScriptBlock { spaceBarHandle $args }

        Set-PSReadLineKeyHandler -Chord Shift+Oem5 -ScriptBlock { pipeHandle $args }
        Set-PSReadLineKeyHandler -Chord Enter -ScriptBlock { enterHandle $args } 

    # search latest XMS commands in history
    # Set-PSReadLineKeyHandler -Chord Ctrl+UpArrow -ScriptBlock {
    # $line = $null
    # $cursor = $null

    # [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)

    # $vals = getXMSHistoryCommands
    # if ($XtremDefaultSessionObject){
    # if (!$line) {
    # if($vals -and $vals.length -ge 0){
    # $item = $vals[$vals.length - 1]
    # [Microsoft.PowerShell.PSConsoleReadLine]::Insert($item+' ');
    # }
    # }elseif ($vals -contains $line.Trim()){
    # $ind = [array]::IndexOf($vals, $line.Trim())
    # if($ind -ge 1){
    # $item = $item = $vals[$ind - 1]
    # [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine();
    # [Microsoft.PowerShell.PSConsoleReadLine]::Insert($item+' ');
    # }
    # }
    # }
    # }
    # scroll down in XMS commands in history
    # Set-PSReadLineKeyHandler -Chord Ctrl+DownArrow -ScriptBlock {
    # $line = $null
    # $cursor = $null

    # [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)

    # $vals = getXMSHistoryCommands
    # if ($XtremDefaultSessionObject) {
    # if (!$line) {
                
    # }
    # elseif ($vals -contains $line.Trim()) {
    # $ind = [array]::IndexOf($vals, $line.Trim())
    # if ($ind -le $vals.length-2) {
    # $item = $item = $vals[$ind + 1]
    # [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine();
    # [Microsoft.PowerShell.PSConsoleReadLine]::Insert($item+' ');
    # }
    # }
    # }
    # }

}

# }

function enterHandle($sbArgs) { 
    $key, $arg = $sbArgs
    $line, $cursor = $null, $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)
    $isLineEnd = ($line.Substring(0, $cursor ) -eq $line)
    $parts = $line -split " "

    if (($parts[0] -eq 'xms') -and ($parts.length -eq 3) -and $isLineEnd) {
        $entity = $parts[1]
        if ($xmsEntitiesList -contains $entity){
            $action = $parts[2]
            $theAlias = 'xms' + $entity + $action
            if (test-path alias:$theAlias) {
                [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine();
                [Microsoft.PowerShell.PSConsoleReadLine]::Insert($theAlias);
            }
        }
    }

    [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine();
    
}

function pipeHandle($sbArgs){ 
    $key, $arg = $sbArgs
    $line, $cursor = $null, $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)
    $isLineEnd = ($line.Substring(0, $cursor ) -eq $line)
    $parts = $line -split " "

    if (($parts[0] -eq 'xms') -and ($parts.length -eq 3) -and $isLineEnd) {
        $entity = $parts[1]
        if ($xmsEntitiesList -contains $entity) {
            $action = $parts[2]
            $theAlias = 'xms' + $entity + $action
            if (test-path alias:$theAlias) {
                [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine();
                [Microsoft.PowerShell.PSConsoleReadLine]::Insert($theAlias);
            }
        }
    }

    if (($line.length -gt 0) -and $isLineEnd){

        $addSpace = ' '
        $spaceAtTheEnd = $line.substring($line.length-1) -eq ' '
        if($spaceAtTheEnd){
            $addSpace = ''
        }

        [Microsoft.PowerShell.PSConsoleReadLine]::Insert($addSpace + '|' + ' ');
    }else{
        [Microsoft.PowerShell.PSConsoleReadLine]::Insert('|');
    }

}

function tabHandle($sbArgs){ 
    $key, $arg = $sbArgs
    $line, $cursor = $null, $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)
    $isLineEnd = ($line.Substring(0, $cursor ) -eq $line)

    if ($XtremDefaultSessionObject){
        if (!$line) {
            [Microsoft.PowerShell.PSConsoleReadLine]::Insert('xms ');
        }elseif ($isLineEnd){
            $line = lineHandleQuotedStings
            $parts = $line -split " "
            $userParams = getCmdletUserParams ('all')
            $cmdParamsArr = ([Array]$userParams.Keys)
            $lastWordIsParam = (($parts[-1] -like "-*") -and (($parts[-1] -replace "^-", '') -in $cmdParamsArr) )    
            # $countLineParams = ([array](($line | Select-String " -\S+" -AllMatches).Matches.Value)).Count
            # $haveMoreParams = $countLineParams -lt $cmdParamsArr.Count
            $nextParam = getNextParamToInsert
            if($lastWordIsParam -and $nextParam){
                [Microsoft.PowerShell.PSConsoleReadLine]::BackwardWord();
                [Microsoft.PowerShell.PSConsoleReadLine]::DeleteWord();
                [Microsoft.PowerShell.PSConsoleReadLine]::MoveToEndOfLine();
                [Microsoft.PowerShell.PSConsoleReadLine]::Insert( $nextParam ); 
            }else{
                [Microsoft.PowerShell.PSConsoleReadLine]::TabCompleteNext();
            }
        }else{
            [Microsoft.PowerShell.PSConsoleReadLine]::TabCompleteNext();
        }
    }else{
        [Microsoft.PowerShell.PSConsoleReadLine]::TabCompleteNext();
    }
}

function shiftTabHandle($sbArgs){ 
    $key, $arg = $sbArgs
    $line, $cursor = $null, $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)
    $isLineEnd = ($line.Substring(0, $cursor ) -eq $line)

    if ($isLineEnd){
        $line = lineHandleQuotedStings
        $parts = $line -split " "
        $userParams = getCmdletUserParams ('all')
        $cmdParamsArr = ([Array]$userParams.Keys)
        $lastWordIsParam = (($parts[-1] -like "-*") -and (($parts[-1] -replace "^-", '') -in $cmdParamsArr) )    
        # $countLineParams = ([array](($line | Select-String " -\S+" -AllMatches).Matches.Value)).Count
        # $haveMoreParams = $countLineParams -lt $cmdParamsArr.Count
        $nextParam = getNextParamToInsert $true
        if($lastWordIsParam -and $nextParam){
            [Microsoft.PowerShell.PSConsoleReadLine]::BackwardWord();
            [Microsoft.PowerShell.PSConsoleReadLine]::DeleteWord();
            [Microsoft.PowerShell.PSConsoleReadLine]::MoveToEndOfLine();
            [Microsoft.PowerShell.PSConsoleReadLine]::Insert( $nextParam ); 
        }else{
            [Microsoft.PowerShell.PSConsoleReadLine]::TabCompletePrevious();
        }
    }else{
        [Microsoft.PowerShell.PSConsoleReadLine]::TabCompletePrevious();
    }
}

function backSpaceHandle($sbArgs){
    $key, $arg = $sbArgs
    $line, $cursor = $null, $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)
    if(!$line){ return }
    $isLineEnd = ($line.Substring(0, $cursor ) -eq $line)
    $parts = $line -split " "

    if ($isLineEnd -and ($parts[-1] -like "-*")) {
        
        $userParams = getCmdletUserParams ('all')
        $cmdParamsArr = ([Array]$userParams.Keys)
        $lastWordIsParam = (($parts[-1] -like "-*") -and (($parts[-1] -replace "^-", '') -in $cmdParamsArr) ) 
        
        if ($lastWordIsParam ) {
            [Microsoft.PowerShell.PSConsoleReadLine]::BackwardWord();
            [Microsoft.PowerShell.PSConsoleReadLine]::DeleteWord();
            [Microsoft.PowerShell.PSConsoleReadLine]::MoveToEndOfLine();
        }
        else {
            [Microsoft.PowerShell.PSConsoleReadLine]::BackwardDeleteChar();
        }
    }else{
        [Microsoft.PowerShell.PSConsoleReadLine]::BackwardDeleteChar();
    }
}

function cntrlSpaceHandle($sbArgs){
    $key, $arg = $sbArgs
    $line, $cursor = $null, $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)

    if (!$line -and $XtremDefaultSessionObject) {
        [Microsoft.PowerShell.PSConsoleReadLine]::Insert('xms ');
    }

    [Microsoft.PowerShell.PSConsoleReadLine]::MenuComplete();
}

function spaceBarHandle($sbArgs){
    $key, $arg = $sbArgs
    $line, $cursor = $null, $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)  
    $isLineEnd = ($line.Substring(0, $cursor ) -eq $line)
    # $line = $line.Trim()
    $line = $line + ' '
    
    $line = lineHandleQuotedStings
    $parts = ($line.Trim()) -split " "
    # [System.Windows.MessageBox]::Show("$($arg) " , 'Message')
    if(!$isLineEnd -or ($line -match "\|")){
        [Microsoft.PowerShell.PSConsoleReadLine]::Insert(' ')
        return
    }

    if ($parts[0] -eq 'xms') {
        # xms command builder/navigation section (eg. xms Volume Get => xmsVolumeGet)
        if ($parts[2] -and $parts[2].Length -ge 3) {
            $theAlias = $parts -join ""
            if (test-path alias:$theAlias) {
                [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine()
                [Microsoft.PowerShell.PSConsoleReadLine]::Insert($theAlias)
                (New-Object -ComObject wscript.shell).SendKeys(' ')
                # [Microsoft.PowerShell.PSConsoleReadLine]::Insert(' ')
            }
        }else{
            [Microsoft.PowerShell.PSConsoleReadLine]::Insert(' ')
        }
    }elseif (isXmsCommand){
            # show properties of xms commands section
            $userParams = getCmdletUserParams ('main')
            $noParamsYet = ($parts[-1] -eq $parts[0])
            $lastWordIsValueOrSwitch = (($parts[-1] -notLike "-*") -or ($userParams[($parts[-1] -replace "^-",'')].isSwitch) )    
            # $haveMoreParams = ((countUsedParams ($parts)) -lt $userParams.Keys.Count)
            $nextParam = getNextParamToInsert
            if(($noParamsYet -or $lastWordIsValueOrSwitch) -and $nextParam){
                [Microsoft.PowerShell.PSConsoleReadLine]::Insert(' -')
                [Microsoft.PowerShell.PSConsoleReadLine]::Insert( $nextParam )
                # (New-Object -ComObject wscript.shell).SendKeys('{tab}')
            }else{
                [Microsoft.PowerShell.PSConsoleReadLine]::Insert(' ')
            }
    }else{
        [Microsoft.PowerShell.PSConsoleReadLine]::Insert(' ')
    }
}

function isXmsCommand(){
    if($parts[0].length -gt 3){
        if($parts[0] -like "*-Xtrem*" -or $parts[0] -like "xms*"){
            return $true
        }
    }
    return $false
}

function getXMSHistoryCommands(){
    $fPath = (Get-PSReadlineOption).HistorySavePath

    $arr = Get-Content $fPath | Select-String -pattern "^xms[A-Za-z]+ .+" 
    $vals = $arr.line | foreach { $a = ($_ -split ' '); $a[0] } | Get-Unique 

    [array]::Reverse($vals)
    $vals = $vals | select -Unique
    [array]::Reverse($vals)
    return $vals
}



Export-ModuleMember Get-XMScommand -Alias *
# Export-ModuleMember Build-RESTcommand
Export-ModuleMember Invoke-XtremGetRequest -Alias *


Export-ModuleMember GetXtremErrorMsg
Export-ModuleMember GetXtremAuthHeader
# Export-ModuleMember GetXtremRawRequest
# Export-ModuleMember CreateResponseObject
Export-ModuleMember BuildURIRequest
Export-ModuleMember XtremGETRequest
Export-ModuleMember XtremPUTRequest
Export-ModuleMember XtremDELETERequest
Export-ModuleMember NewXtremRequest
Export-ModuleMember SetParametersForRequest

Export-ModuleMember Set-XtremTrustAllCertsPolicy
Export-ModuleMember Export-XtremCSV
Export-ModuleMember BuildXtremJson
Export-ModuleMember IsConfirmed
Export-ModuleMember AddIfExists

# Export-ModuleMember Get-XTremType