PSInsight.psm1

#region Private functions
function New-InsightHeaders {
<#

.SYNOPSIS
Generate Insight headers.
.EXAMPLE
$Headers = New-InsightHeaders -InsightApiKey $InsightApiKey
.OUTPUTS
Generic dictionary.

#>

    param (
        [Alias('ApiKey')]
        [string]$InsightApiKey
    )

    Write-Verbose 'Generating Headers'
    $Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $Headers.Add('content-type' , 'application/json')
    $Headers.Add('authorization', 'bearer ' + $InsightApiKey)

    Write-Output $Headers
}
#endregion

#region Public functions
function Get-InsightObjectTypes {
<#

.SYNOPSIS
Resource to find object types in Insight for a specific object schema. The object types are responded in a flat list.

.DESCRIPTION
Resource to find object types in Insight for a specific object schema. The object types are responded in a flat list.

.PARAMETER ID
The object type ID.
Can be the Object ID or the Schema ID etc

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 1
name : My Object Type
type : 0
description : A Sample Object Type
icon : @{id=1; name=3D Printer; url16=/rest/insight/1.0/objecttype/1/icon.png?size=16&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppcmEucGx1Z2
                            lucy5pbnNpZ2h0Iiwic3ViIjoiNWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiwiaW5zaWdodCI6dHJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiwiaXNzIjoiY29t
                            LnJpYWRhbGFicy5qaXJhLnBsdWdpbnMuaW5zaWdodCIsIm9yaWdpbmFsbHlJc3N1ZWRBdCI6MTYwMDIzMTAxMiwiZXhwIjoxNjAwMjMxMTkyLCJpYXQiOjE2MDAyMzEwMTJ9.oHQ6uHuginJCihoHT2YdSqPMBzgWD
                            KpwZmVoBtlPqY0; url48=/rest/insight/1.0/objecttype/1/icon.png?size=48&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppcmEucGx1Z2lucy5pbnN
                            pZ2h0Iiwic3ViIjoiNWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiwiaW5zaWdodCI6dHJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiwiaXNzIjoiY29tLnJpYWRhb
                            GFicy5qaXJhLnBsdWdpbnMuaW5zaWdodCIsIm9yaWdpbmFsbHlJc3N1ZWRBdCI6MTYwMDIzMTAxMiwiZXhwIjoxNjAwMjMxMTkyLCJpYXQiOjE2MDAyMzEwMTJ9.oHQ6uHuginJCihoHT2YdSqPMBzgWDKpwZmVoBt
                            lPqY0}
position : 0
created : 2020-09-16T04:36:52.885Z
updated : 2020-09-16T04:36:52.885Z
objectCount : 0
objectSchemaId : 3
inherited : False
abstractObjectType : False
parentObjectTypeInherited : False

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Object+types

.EXAMPLE
Get-InsightObjectTypes -ID 3 -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [int]$ID,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/objectschema/$($ID)/objecttypes/flat"
    }
    
    end {
        try {
                $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Method GET
            }
            catch {
                Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
            } 

            $response
    }
}

function New-InsightObjectTypes {
    <#

.SYNOPSIS
Resource to create an object type in Insight.

.DESCRIPTION
Resource to create an object type in Insight.

.PARAMETER Name
The object type name.

.PARAMETER Description
The object type description.

.PARAMETER IconID
The object type icon ID.

.PARAMETER ParentObjectTypeID
The parent object type id that new child object will be placed in

.PARAMETER objectSchemaId
The object schema id

.PARAMETER inherited
The object schema id

.PARAMETER abstractObjectType
Set to true to stop CI's from being created at this level (parent)

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 1
name : My Object Type
type : 0
description : A Sample Object Type
icon : @{id=1; name=3D Printer; url16=/rest/insight/1.0/objecttype/1/icon.png?size=16&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppcmEucGx1Z2
                            lucy5pbnNpZ2h0Iiwic3ViIjoiNWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiwiaW5zaWdodCI6dHJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiwiaXNzIjoiY29t
                            LnJpYWRhbGFicy5qaXJhLnBsdWdpbnMuaW5zaWdodCIsIm9yaWdpbmFsbHlJc3N1ZWRBdCI6MTYwMDIzMTAxMiwiZXhwIjoxNjAwMjMxMTkyLCJpYXQiOjE2MDAyMzEwMTJ9.oHQ6uHuginJCihoHT2YdSqPMBzgWD
                            KpwZmVoBtlPqY0; url48=/rest/insight/1.0/objecttype/1/icon.png?size=48&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppcmEucGx1Z2lucy5pbnN
                            pZ2h0Iiwic3ViIjoiNWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiwiaW5zaWdodCI6dHJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiwiaXNzIjoiY29tLnJpYWRhb
                            GFicy5qaXJhLnBsdWdpbnMuaW5zaWdodCIsIm9yaWdpbmFsbHlJc3N1ZWRBdCI6MTYwMDIzMTAxMiwiZXhwIjoxNjAwMjMxMTkyLCJpYXQiOjE2MDAyMzEwMTJ9.oHQ6uHuginJCihoHT2YdSqPMBzgWDKpwZmVoBt
                            lPqY0}
position : 0
created : 2020-09-16T04:36:52.885Z
updated : 2020-09-16T04:36:52.885Z
objectCount : 0
objectSchemaId : 3
inherited : False
abstractObjectType : False
parentObjectTypeInherited : False

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Object+types

.EXAMPLE
New-InsightObjectTypes -Name "My Object Type" -Description "A Sample Object Type" -IconID 1 -objectSchemaId 3 -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$Name,

        [Parameter(Mandatory = $false)]
        [string]$Description,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        $IconID,

        [Parameter(Mandatory = $false)]
        $parentObjectTypeId,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        $objectSchemaId,

        [Parameter(Mandatory = $false)]
        [ValidateSet('true','false')]
        $inherited,

        [Parameter(Mandatory = $false)]
        [ValidateSet('true','false')]
        $abstractObjectType,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/objecttype/create"

        $RequestBody = @{
            'name'           = $Name
            'iconId'         = $iconId
            'objectSchemaId' = $objectSchemaId
        }
        If ($Description) {
            $RequestBody.Add('description', $Description)
        }
        If ($parentObjectTypeId) {
            $RequestBody.Add('parentObjectTypeId', $parentObjectTypeId)
        }
        If ($inherited) {
            $RequestBody.Add('inherited', $inherited)
        }
        If ($abstractObjectType) {
            $RequestBody.Add('abstractObjectType', $abstractObjectType)
        }

        $RequestBody = ConvertTo-Json $RequestBody -Depth 1
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Body $RequestBody -Method POST
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        } 

        $response
    }
}

function Remove-InsightObjectTypes {
    <#

.SYNOPSIS
Resource to delete an object type in Insight.

.DESCRIPTION
Resource to delete an object type in Insight.

.PARAMETER ID
The Object Type ID.

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 1
name : My Object Type
type : 0
description : A Sample Object Type
icon : @{id=1; name=3D Printer; url16=/rest/insight/1.0/objecttype/1/icon.png?size=16&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppcmEucGx1Z2
                            lucy5pbnNpZ2h0Iiwic3ViIjoiNWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiwiaW5zaWdodCI6dHJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiwiaXNzIjoiY29t
                            LnJpYWRhbGFicy5qaXJhLnBsdWdpbnMuaW5zaWdodCIsIm9yaWdpbmFsbHlJc3N1ZWRBdCI6MTYwMDI0MDIwNiwiZXhwIjoxNjAwMjQwMzg2LCJpYXQiOjE2MDAyNDAyMDZ9.fOtVat8TB9dOkl-8EWw4z1ztqCBtD
                            LFNeKXi6PjFcW0; url48=/rest/insight/1.0/objecttype/1/icon.png?size=48&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppcmEucGx1Z2lucy5pbnN
                            pZ2h0Iiwic3ViIjoiNWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiwiaW5zaWdodCI6dHJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiwiaXNzIjoiY29tLnJpYWRhb
                            GFicy5qaXJhLnBsdWdpbnMuaW5zaWdodCIsIm9yaWdpbmFsbHlJc3N1ZWRBdCI6MTYwMDI0MDIwNiwiZXhwIjoxNjAwMjQwMzg2LCJpYXQiOjE2MDAyNDAyMDZ9.fOtVat8TB9dOkl-8EWw4z1ztqCBtDLFNeKXi6P
                            jFcW0}
position : 0
created : 2020-09-16T04:36:52.885Z
updated : 2020-09-16T04:50:45.458Z
objectCount : 0
objectSchemaId : 3
inherited : False
abstractObjectType : False
parentObjectTypeInherited : False

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Object+types

.EXAMPLE
Remove-InsightObjectTypes -id 1 -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$ID,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/objecttype/$($ID)"
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Method DELETE
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        } 

        $response
    }
}


function Set-InsightObjectTypes {
    <#

.SYNOPSIS
Resource to create an object type in Insight.

.DESCRIPTION
Resource to create an object type in Insight.

.PARAMETER ID
The object type ID.

.PARAMETER Name
The status Name.

.PARAMETER Description
The object type Description.

.PARAMETER IconID
The object type IconID (Can be collected via Get-InsightIcons).

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 1
name : My Object Type
type : 0
description : A Sample Object Type - Updated
icon : @{id=1; name=3D Printer; url16=/rest/insight/1.0/objecttype/1/icon.png?size=16&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppcmEucGx1Z2
                            lucy5pbnNpZ2h0Iiwic3ViIjoiNWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiwiaW5zaWdodCI6dHJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiwiaXNzIjoiY29t
                            LnJpYWRhbGFicy5qaXJhLnBsdWdpbnMuaW5zaWdodCIsIm9yaWdpbmFsbHlJc3N1ZWRBdCI6MTYwMDIzMTg0NSwiZXhwIjoxNjAwMjMyMDI1LCJpYXQiOjE2MDAyMzE4NDV9.kOqWqYE7nswEI7WZql7i3pOPp2MM8
                            frWlgwx9fB5GNI; url48=/rest/insight/1.0/objecttype/1/icon.png?size=48&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppcmEucGx1Z2lucy5pbnN
                            pZ2h0Iiwic3ViIjoiNWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiwiaW5zaWdodCI6dHJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiwiaXNzIjoiY29tLnJpYWRhb
                            GFicy5qaXJhLnBsdWdpbnMuaW5zaWdodCIsIm9yaWdpbmFsbHlJc3N1ZWRBdCI6MTYwMDIzMTg0NSwiZXhwIjoxNjAwMjMyMDI1LCJpYXQiOjE2MDAyMzE4NDV9.kOqWqYE7nswEI7WZql7i3pOPp2MM8frWlgwx9f
                            B5GNI}
position : 0
created : 2020-09-16T04:36:52.885Z
updated : 2020-09-16T04:50:45.458Z
objectCount : 0
objectSchemaId : 3
inherited : False
abstractObjectType : False
parentObjectTypeInherited : False

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Object+types

.EXAMPLE
Set-InsightObjectTypes -ID 1 -Name "My Object Type" -Description "A Sample Object Type - Updated" -IconID 1 -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $True)]
        [int]$ID,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$Name,

        [Parameter(Mandatory = $false)]
        [string]$Description,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [int]$IconID,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/objecttype/$($ID)"

        $RequestBody = @{
            'name'           = $Name
            'iconId'         = $iconId
        }
        If ($Description) {
            $RequestBody.Add('description', $Description)
        }
        
        $RequestBody = ConvertTo-Json $RequestBody -Depth 1
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Body $RequestBody -Method PUT
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        } 

        $response
    }
}


function Get-InsightStatuses {
<#

.SYNOPSIS
Resource to load a status in Insight.

.DESCRIPTION
Resource to load a status in Insight.

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id name description category
-- ---- ----------- --------
 1 Action Needed 2
 2 Active 1
 3 Closed 0
 4 In Service 2
 5 Running 1
 6 Stopped 0
 7 Support Requested 2

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Statuses

.EXAMPLE
Get-InsightObjectTypes -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/config/statustype"
    }
    
    end {
        try {
                $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Method GET
            }
            catch {
                Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
            } 

            $response
    }
}

function New-InsightStatuses {
    <#

.SYNOPSIS
Resource to create a status in Insight.

.DESCRIPTION
Resource to create a status in Insight.

.PARAMETER Name
The Status Name.

.PARAMETER Description
The Status Description.

.PARAMETER Category
The status Category.

.PARAMETER InsightApiKey
The Api Secret.

.OUTPUTS
id name description category
-- ---- ----------- --------
 8 My New Status Sample Status 1

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Statuses

.EXAMPLE
New-InsightStatuses -Name "My New Status" -Description "Sample Status" -category Active -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$Name,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$Description,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [Validateset("Inactive","Active","Pending")]
        [String]$category,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey

        switch ($category) {
            "Inactive" { $CategoryID = 0 }
            "Active" { $CategoryID = 1 }
            "Pending" { $CategoryID = 2 }
}
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/config/statustype"

        $RequestBody = @{
            'name'           = $Name
            'description'    = $Description
            'category'       = $CategoryID
        }
        
        $RequestBody = ConvertTo-Json $RequestBody -Depth 1
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Body $RequestBody -Method POST
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        } 

        $response
    }
}

function Remove-InsightObjectTypes {
    <#

.SYNOPSIS
Resource to delete a status in Insight.

.DESCRIPTION
Resource to delete a status in Insight.

.PARAMETER ID
The status ID.

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
No output from API

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Statuses

.EXAMPLE
Remove-InsightObjectTypes -ID 8 -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$ID,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/config/statustype/$($ID)"
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Method DELETE
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        } 

        $response
    }
}


function Set-InsightStatuses {
    <#

.SYNOPSIS
Resource to update a status in Insight.

.DESCRIPTION
Resource to update a status in Insight.

.PARAMETER ID
The status ID.

.PARAMETER Name
The status name.

.PARAMETER Description
The status description

.PARAMETER Category
The status category.

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id name description category
-- ---- ----------- --------
 8 My New Status Sample Status - Updated 1

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Statuses

.EXAMPLE
Set-InsightStatuses -ID 8 -Name "My New Status" -Description "Sample Status - Updated" -category Active -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$ID,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$Name,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$Description,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [Validateset("Inactive","Active","Pending")]
        [String]$Category,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey

        switch ($category) {
            "Inactive" { $CategoryID = 0 }
            "Active" { $CategoryID = 1 }
            "Pending" { $CategoryID = 2 }
}
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/config/statustype/$($ID)"

        $RequestBody = @{
            'name'           = $Name
            'description'    = $Description
            'category'       = $CategoryID
        }
        
        $RequestBody = ConvertTo-Json $RequestBody -Depth 1
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Body $RequestBody -Method PUT
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        } 

        $response
    }
}

function Get-InsightIcons {
    <#

.SYNOPSIS
Resource to find global Icons in Insight.

.DESCRIPTION
Resource to find global Icons in Insight.

.PARAMETER Full
If full switch is used then property url16 is returned

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
If switch 'Full' is used
id name url16
 -- ---- -----
  1 3D Printer /rest/insight/1.0/icon/1/icon.png?size=16&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLm...
  2 AV Receiver /rest/insight/1.0/icon/2/icon.png?size=16&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLm...

if switch 'Full' is not used
id name
 -- ----
  1 3D Printer
  2 AV Receiver
  3 Active Directory
  4 Administrative Tools
  5 Agreement

.LINK
https://documentation.mindville.com/display/ICV811/Icons+-+REST

.EXAMPLE
Get-InsightIcons -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey,

        [switch]$Full = $False
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/icon/global"
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Method GET
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        }

        if ($Full) {
            Write-Output $response
        }
        else {
            Write-Output $response | Select id,name
        }
    }
}

function Get-InsightObjectTypeAttributes {
    <#

.SYNOPSIS
Resource to find Object Type Attributes for a specified Object Type in Insight.

.DESCRIPTION
Resource to find Object Type Attributes for a specified Object Type in Insight.

.PARAMETER ID
The Object Type ID.

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 7
objectType : @{id=2; name=My Object Type; type=0; description=A Sample Object Type; icon=; position=0; created=2020-09-16T07:14:02.118Z; updated=2020-09-16T07:14:02.118Z; objectCount=0; objectSchemaId=3; inherited=False;
                          abstractObjectType=False; parentObjectTypeInherited=False}
name : Key
label : False
type : 0
defaultType : @{id=0; name=Text}
editable : False
system : True
sortable : True
summable : False
indexed : True
minimumCardinality : 1
maximumCardinality : 1
removable : False
hidden : False
includeChildObjectTypes : False
uniqueAttribute : False
options :
position : 0

id : 8
objectType : @{id=2; name=My Object Type; type=0; description=A Sample Object Type; icon=; position=0; created=2020-09-16T07:14:02.118Z; updated=2020-09-16T07:14:02.118Z; objectCount=0; objectSchemaId=3; inherited=False;
                          abstractObjectType=False; parentObjectTypeInherited=False}
name : Name
label : True
type : 0
description : The name of the object
defaultType : @{id=0; name=Text}
editable : True
system : False
sortable : True
summable : False
indexed : True
minimumCardinality : 1
maximumCardinality : 1
removable : False
hidden : False
includeChildObjectTypes : False
uniqueAttribute : False
options :
position : 1

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Object+type+attributes

.EXAMPLE
Get-InsightObjectTypeAttributes -ID 2 -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [int]$ID,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/objecttype/$($ID)/attributes"
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Method GET
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        } 

        $response
    }
}


function New-InsightObjectTypeAttributes {
    <#

.SYNOPSIS
Resource to create an object type attribute in Insight.

.DESCRIPTION
Resource to create an object type attribute in Insight.

.PARAMETER Name
The name.

.PARAMETER Description
The Description for the Attribute.

.PARAMETER Type
The type. ["Default", "Object", "User", "Confluence", "Group", "Status"]

.PARAMETER ParentObjectTypeId
The Object type ID of the Parent

.PARAMETER defaultType
The default type id. Dynamic Parameter if 'Type' is 'Default' ["Text", "Integer", "Boolean", "Double", "Date", "Time", "Date_Time", "URL", "Email", "TextArea", "Select", "IP_Address"]

.PARAMETER typeValue
The referenced object type id. Dynamic Parameter if 'Type' is 'Object'

.PARAMETER typeValueMulti
The JIRA groups to restrict selection. Dynamic Parameter if 'Type' is 'User'

.PARAMETER additionalValue. Dynamic Parameter if 'Type' is 'Object','URL' or 'Confluence'
URL: DISABLED, ENABLED
OBJECT: Reference Type Id
User: SHOW_PROFILE, HIDE_PROFILE
Confluence: Confluence Space Id

.PARAMETER minimumCardinality. Dynamic Parameter if 'Type' is 'Email','Select','Object','User','Group','Version','Project'
The minimum cardinality (default 0)

.PARAMETER maximumCardinality. Dynamic Parameter if 'Type' is 'Email','Select','Object','User','Group','Version','Project'
The maximum cardinality (default 1)

.PARAMETER suffix
If suffix on value types (Integer, Float). Dynamic Parameter if 'Default Type' is 'Integer' or 'Float'

.PARAMETER includeChildObjectTypes
If objects should be valid for object type children. Dynamic Parameter if 'Type' is 'Object'

.PARAMETER iql
If objects should be filtered by IQL. Dynamic Parameter if 'Type' is 'Object'

.PARAMETER uniqueAttribute
If the attribute should be unique (true, false)

.PARAMETER summable
If a sum should be shown in list view (true, false). Dynamic Parameter if 'Default Type' is 'Integer' or 'Float'

.PARAMETER regexValidation. Dynamic Parameter if 'Type' is 'Text' or 'Email'
If a regex validation should apply

.PARAMETER options. Dynamic Parameter if 'Type' is 'Options'
The options for the attribute in comma separate list

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 6
name : Test Attribute
label : False
type : 0
defaultType : @{id=0; name=Text}
editable : True
system : False
sortable : True
summable : False
indexed : True
minimumCardinality : 0
maximumCardinality : 1
removable : True
hidden : False
includeChildObjectTypes : False
uniqueAttribute : False
options :
position : 4

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Object+type+attributes

.EXAMPLE
New-InsightObjectTypeAttributes -Name "Test Attribute" -Type Default -DefaultType Text -ObjectTypeId 1 -InsightApiKey $InsightApiKey
New-InsightObjectTypeAttributes -Name "Email Address" -Type Default -DefaultType Email -ParentObjectTypeId 1 -InsightApiKey $InsightApiKey
New-InsightObjectTypeAttributes -Name "Link to Parent" -Type Object -typeValue 2 -ParentObjectTypeId 1 -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$Name,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $false)]
        [string]$Description,

        [Parameter(Mandatory = $true)]
        [ValidateSet("Default", "Object", "User", "Confluence", "Group", "Status")]
        [string]$Type,

        [Parameter(Mandatory = $false)]
        [Alias('ObjectTypeId')]
        [int]$ParentObjectTypeId,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    DynamicParam {
        # Create a new dictionary to contain all the dynamic parameters
        $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()

        if ($Type -like "Default") {
            $parameter = [System.Management.Automation.RuntimeDefinedParameter]::new(
                        'DefaultType',
                        [string],
                        [Attribute[]]@(
                            [Parameter]@{ Mandatory = $true; Position = 1 }
                            [ValidateSet]::new("Text", "Integer", "Boolean", "Double", "Date", "Time", "Date_Time", "URL", "Email", "TextArea", "Select", "IP_Address")
                        )
                    )
                    $paramDictionary.Add($parameter.Name, $parameter)
        }

        if ($DefaultType -in "Integer","Float") {
            $parameter = [System.Management.Automation.RuntimeDefinedParameter]::new(
                        'suffix',
                        [string],
                        [Attribute[]]@(
                            [Parameter]@{ Mandatory = $true; Position = 1 }
                            #[ValidateSet]::new("Start", "Stop", "Create", "Delete")
                        )
                    )
                    $paramDictionary.Add($parameter.Name, $parameter)

            $parameter = [System.Management.Automation.RuntimeDefinedParameter]::new(
                        'summable',
                        [string],
                        [Attribute[]]@(
                            [Parameter]@{ Mandatory = $true; Position = 1 }
                            [ValidateSet]::new("True", "False")
                        )
                    )
                    $paramDictionary.Add($parameter.Name, $parameter)
        }

        if ($Type -in "Object") {

            $parameter = [System.Management.Automation.RuntimeDefinedParameter]::new(
                        'typeValue',
                        [string],
                        [Attribute[]]@(
                            [Parameter]@{ Mandatory = $true; Position = 1 }
                        )
                    )
                    $paramDictionary.Add($parameter.Name, $parameter)

            $parameter = [System.Management.Automation.RuntimeDefinedParameter]::new(
                        'includeChildObjectTypes',
                        [string],
                        [Attribute[]]@(
                            [Parameter]@{ Mandatory = $false; Position = 1 }
                            #[ValidateSet]::new("Start", "Stop", "Create", "Delete")
                        )
                    )
                    $paramDictionary.Add($parameter.Name, $parameter)

            $parameter = [System.Management.Automation.RuntimeDefinedParameter]::new(
                        'iql',
                        [string],
                        [Attribute[]]@(
                            [Parameter]@{ Mandatory = $false; Position = 1 }
                            #[ValidateSet]::new("Start", "Stop", "Create", "Delete")
                        )
                    )
                    $paramDictionary.Add($parameter.Name, $parameter)
        }

        if ($Type -in "User") {
            $parameter = [System.Management.Automation.RuntimeDefinedParameter]::new(
                        'typeValueMulti',
                        [array],
                        [Attribute[]]@(
                            [Parameter]@{ Mandatory = $false; Position = 1 }
                        )
                    )
                    $paramDictionary.Add($parameter.Name, $parameter)
        }

        if ($Type -in "Object","URL","Confluence","User") {

            if ($Type -like "Object") {
                $Mandatory =  $true
            }
            else {
                $Mandatory = $false
            }

            if ($Type -like "URL") {
                $ValidatedSet = "DISABLED","ENABLED"
            }
            if ($Type -like "User") {
                $ValidatedSet = "SHOW_PROFILE","HIDE_PROFILE"
            }

            $parameter = [System.Management.Automation.RuntimeDefinedParameter]::new(
                        'additionalValue',
                        [string],
                        [Attribute[]]@(
                            [Parameter]@{ Mandatory = $Mandatory; Position = 1 }
                                if ($type -in "URL","User") {
                                    [ValidateSet]::new($ValidatedSet)
                                }
                        )
                    )
                    $paramDictionary.Add($parameter.Name, $parameter)
        }

        if ($Type -in "Email","Select","Object","User","Group","Version","Project") {
            $parameter = [System.Management.Automation.RuntimeDefinedParameter]::new(
                        'minimumCardinality',
                        [int],
                        [Attribute[]]@(
                            [Parameter]@{ Mandatory = $false; Position = 1 }
                        )
                    )
                    $paramDictionary.Add($parameter.Name, $parameter)

            $parameter = [System.Management.Automation.RuntimeDefinedParameter]::new(
                        'maximumCardinality',
                        [int],
                        [Attribute[]]@(
                            [Parameter]@{ Mandatory = $false; Position = 1 }
                        )
                    )
                    $paramDictionary.Add($parameter.Name, $parameter)
        }

        if ($Type -in "Text","Email") {
            $parameter = [System.Management.Automation.RuntimeDefinedParameter]::new(
                        'regexValidation',
                        [string],
                        [Attribute[]]@(
                            [Parameter]@{ Mandatory = $false; Position = 1 }
                        )
                    )
                    $paramDictionary.Add($parameter.Name, $parameter)
        }

        if ($Type -like "Options") {
            $parameter = [System.Management.Automation.RuntimeDefinedParameter]::new(
                        'options',
                        [array],
                        [Attribute[]]@(
                            [Parameter]@{ Mandatory = $false; Position = 1 }
                            #[ValidateSet]::new("Start", "Stop", "Create", "Delete")
                        )
                    )
                    $paramDictionary.Add($parameter.Name, $parameter)
        }
        
        $paramDictionary
    }

    begin {
        
        # Translate the Object Type Name to the Object Type ID
        switch ($Type) {
            "Default" { $objectTypeID = "0" }
            "Object" { $objectTypeID = "1" }
            "User" { $objectTypeID = "2" }
            "Confluence" { $objectTypeID = "3" }
            "Group" { $objectTypeID = "4" }
            "Status" { $objectTypeID = "7" }
        }

        # Translate the Default Object Type Name to the Default Object Type ID
        switch ($PSBoundParameters["DefaultType"]) {
            "Text" { $DefaultTypeID = 0 }
            "Integer" { $DefaultTypeID = "1" }
            "Boolean" { $DefaultTypeID = "2" }
            "Double" { $DefaultTypeID = "3" }
            "Date" { $DefaultTypeID = "4" }
            "Time" { $DefaultTypeID = "5" }
            "Date Time" { $DefaultTypeID = "6" }
            "URL" { $DefaultTypeID = "7" }
            "Email" { $DefaultTypeID = "8" }
            "Textarea" { $DefaultTypeID = "9" }
            "Select" { $DefaultTypeID = "10" }
            "IP Address" { $DefaultTypeID = "11" }
        } 
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {

        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/objecttypeattribute/$($ParentObjectTypeId)"

        #Create default Hash
        $RequestBody = @{
            name = $Name
        }

        #Add required attributes to the hash for all Object Types except Default
        switch ($Type) {
            { @("Object", "Confluence") -contains $_ } {
                $RequestBody.Add("type", $objectTypeID)
                $RequestBody.Add("typeValue", $PSBoundParameters["typeValue"])
                $RequestBody.Add("additionalValue", $PSBoundParameters["additionalValue"])
            }
            "User" { 
                $RequestBody.Add("type", $objectTypeID)
                $RequestBody.Add("typeValue", $PSBoundParameters["typeValue"])
                $RequestBody.Add("typeValueMulti", $PSBoundParameters["typeValueMulti"])
                $RequestBody.Add("additionalValue", $PSBoundParameters["additionalValue"])
            }
            "Group" { 
                $RequestBody.Add("type", $objectTypeID)
                $RequestBody.Add("additionalValue", $PSBoundParameters["additionalValue"])
            }
            "Status" { 
                $RequestBody.Add("type", $objectTypeID)
                $RequestBody.Add("typeValueMulti", $PSBoundParameters["typeValueMulti"])
            }
        }

        #Add required attributes to the hash for all Object Type "Default"
        switch ($PSBoundParameters["DefaultType"]) {
            { @("Text", "Integer", "Boolean", "Double", "Date", "Date_Time", "Email", "TextArea", "IP_Address") -contains $_ } {
                $RequestBody.Add("type", 0)
                $RequestBody.Add("defaultTypeId", $DefaultTypeID)
            }
            "URL" { 
                $RequestBody.Add("type", 0)
                $RequestBody.Add("defaultTypeId", $DefaultTypeID)
                $RequestBody.Add("additionalValue", $PSBoundParameters["additionalValue"])
            }
            "Select" { 
                $RequestBody.Add("type", 0)
                $RequestBody.Add("defaultTypeId", $DefaultTypeID)
                $RequestBody.Add("options", $PSBoundParameters["options"])
            }
        }

        # Add all the aditional Attributes.
        if ($uniqueAttribute) {
            $RequestBody.Add("uniqueAttribute", $UniqueAttribute)
        }
        if ($Description) {
            $RequestBody.Add("Description", $Description)
        }
        if ($PSBoundParameters["minimumCardinality"]) {
            $RequestBody.Add("minimumCardinality", $PSBoundParameters["minimumCardinality"])
        }
        if ($PSBoundParameters["maximumCardinality"]) {
            $RequestBody.Add("maximumCardinality", $PSBoundParameters["maximumCardinality"])
        }
        if ($PSBoundParameters["suffix"]) {
            $RequestBody.Add("suffix", $PSBoundParameters["suffix"])
        }
        if ($PSBoundParameters["includeChildObjectTypes"]) {
            $RequestBody.Add("includeChildObjectTypes", $PSBoundParameters["includeChildObjectTypes"])
        }
        if ($PSBoundParameters["iql"]) {
            $RequestBody.Add("iql", $PSBoundParameters["iql"])
        }
        if ($PSBoundParameters["summable"]) {
            $RequestBody.Add("summable", $PSBoundParameters["summable"])
        }
        if ($PSBoundParameters["regexValidation"]) {
            $RequestBody.Add("regexValidation", $PSBoundParameters["regexValidation"])
        }
        if ($PSBoundParameters["options"]) {
            $RequestBody.Add("options", $PSBoundParameters["options"])
        }
        
        $RequestBody = ConvertTo-Json $RequestBody -Depth 10
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Body $RequestBody -Method POST
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        } 

        $response
    }
}

function Remove-InsightObjectTypeAttributes {
    <#

.SYNOPSIS
Resource to delete an object type attribute in Insight.

.DESCRIPTION
Resource to delete an object type attribute in Insight.

.PARAMETER ID
The Object Type Attribute ID.

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
No output from API

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Object+type+attributes

.EXAMPLE
Remove-InsightObjectTypeAttributes -ID 11 -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$ID,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/objecttypeattribute/$($ID)"
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Method DELETE
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        } 

        $response
    }
}


function Set-InsightObjectTypeAttributes {
    <#

.SYNOPSIS
Resource to update an object type attribute in Insight.

.DESCRIPTION
Resource to update an object type attribute in Insight.

.PARAMETER Name
The name.

.PARAMETER Type
The type. ["Default", "Object", "User", "Confluence", "Group", "Status"]

.PARAMETER ParentObjectTypeId
The Object type ID of the Parent

.PARAMETER defaultType
The default type id. Dynamic Parameter if 'Type' is 'Default' ["Text", "Integer", "Boolean", "Double", "Date", "Time", "Date_Time", "URL", "Email", "TextArea", "Select", "IP_Address"]

.PARAMETER typeValue
The referenced object type id. Dynamic Parameter if 'Type' is 'Object'

.PARAMETER typeValueMulti
The JIRA groups to restrict selection. Dynamic Parameter if 'Type' is 'User'

.PARAMETER additionalValue. Dynamic Parameter if 'Type' is 'Object','URL' or 'Confluence'
URL: DISABLED, ENABLED
OBJECT: Reference Type Id
User: SHOW_PROFILE, HIDE_PROFILE
Confluence: Confluence Space Id

.PARAMETER minimumCardinality. Dynamic Parameter if 'Type' is 'Email','Select','Object','User','Group','Version','Project'
The minimum cardinality (default 0)

.PARAMETER maximumCardinality. Dynamic Parameter if 'Type' is 'Email','Select','Object','User','Group','Version','Project'
The maximum cardinality (default 1)

.PARAMETER suffix
If suffix on value types (Integer, Float). Dynamic Parameter if 'Default Type' is 'Integer' or 'Float'

.PARAMETER includeChildObjectTypes
If objects should be valid for object type children. Dynamic Parameter if 'Type' is 'Object'

.PARAMETER iql
If objects should be filtered by IQL. Dynamic Parameter if 'Type' is 'Object'

.PARAMETER uniqueAttribute
If the attribute should be unique (true, false)

.PARAMETER summable
If a sum should be shown in list view (true, false). Dynamic Parameter if 'Default Type' is 'Integer' or 'Float'

.PARAMETER regexValidation. Dynamic Parameter if 'Type' is 'Text' or 'Email'
If a regex validation should apply

.PARAMETER options. Dynamic Parameter if 'Type' is 'Options'
The options for the attribute in comma separate list

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 30
name : A different name then before
label : False
type : 0
defaultType : @{id=0; name=Text}
editable : True
system : False
sortable : True
summable : False
indexed : True
minimumCardinality : 0
maximumCardinality : 1
removable : True
hidden : False
includeChildObjectTypes : False
uniqueAttribute : False
options :
position : 4

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Object+type+attributes

.EXAMPLE
Set-InsightObjectTypeAttributes -ParentObjectTypeId 2 -objectTypeAttributeId 30 -Name "A different name then before"

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [Alias('ObjectTypeId')]
        [int]$ParentObjectTypeId,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [int]$objectTypeAttributeId,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $false)]
        [string]$Name,

        [Parameter(Mandatory = $false)]
        [ValidateSet("Default", "Object", "User", "Confluence", "Group", "Status")]
        [string]$Type,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    DynamicParam {
        # Create a new dictionary to contain all the dynamic parameters
        $paramDictionary = new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary

        if ($Type -like "Default") {
            #region DefaultType
            #Create the base DynamicParam attributes
            $attributes = new-object System.Management.Automation.ParameterAttribute
            $attributes.ParameterSetName = "__AllParameterSets"
            $attributes.Mandatory = $false

            # Create the ValidateSet list
            $attributesValidate = New-Object System.Management.Automation.ValidateSetAttribute @("Text", "Integer", "Boolean", "Double", "Date", "Time", "Date_Time", "URL", "Email", "TextArea", "Select", "IP_Address")
        
            # Add the Base Attributes and the Validated list attributes to a collection
            $attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
            $attributeCollection.Add($attributes)
            $attributeCollection.Add($attributesValidate)

            #Create a new object with the Name, Type, and attributes collection.
            $DefaultType_dynParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("DefaultType", [STRING], $attributeCollection)

            # Add Parameter to the Dictionary collection
            $paramDictionary.Add("DefaultType", $DefaultType_dynParam)
            #endregion DefaultType
        }

        if ($DefaultType -like "Integer" -or $DefaultType -like "Float") {
            #region suffix
            #Create the base DynamicParam attributes
            $attributes = new-object System.Management.Automation.ParameterAttribute
            $attributes.ParameterSetName = "__AllParameterSets"
            $attributes.Mandatory = $true

            # Create the ValidateSet list
            #$attributesValidate = New-Object System.Management.Automation.ValidateSetAttribute @("Text", "Integer", "Boolean", "Double", "Date", "Time", "Date Time", "URL", "Email", "Text Area", "Select", "IP Address")
        
            # Add the Base Attributes and the Validated list attributes to a collection
            $suffix_attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
            $suffix_attributeCollection.Add($attributes)
            #$attributeCollection.Add($attributesValidate)

            #Create a new object with the Name, Type, and attributes collection.
            $suffix_dynParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("suffix", [STRING], $suffix_attributeCollection)

            # Add Parameter to the Dictionary collection
            $paramDictionary.Add("suffix", $suffix_dynParam)
            #endregion suffix

            #region summable
            #Create the base DynamicParam attributes
            $attributes = new-object System.Management.Automation.ParameterAttribute
            $attributes.ParameterSetName = "__AllParameterSets"
            $attributes.Mandatory = $true

            # Create the ValidateSet list
            $attributesValidate = New-Object System.Management.Automation.ValidateSetAttribute @("True", "False")
        
            # Add the Base Attributes and the Validated list attributes to a collection
            $attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
            $attributeCollection.Add($attributes)
            $attributeCollection.Add($attributesValidate)

            #Create a new object with the Name, Type, and attributes collection.
            $summable_dynParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("summable", [STRING], $attributeCollection)

            # Add Parameter to the Dictionary collection
            $paramDictionary.Add("summable", $summable_dynParam)
            #endregion summable
        }

        if ($Type -like "Object") {
            #region typeValue
            #Create the base DynamicParam attributes
            $attributes = new-object System.Management.Automation.ParameterAttribute
            $attributes.ParameterSetName = "__AllParameterSets"
            $attributes.Mandatory = $false

            # Create the ValidateSet list
            #$attributesValidate = New-Object System.Management.Automation.ValidateSetAttribute @("Text", "Integer", "Boolean", "Double", "Date", "Time", "Date Time", "URL", "Email", "Text Area", "Select", "IP Address")
        
            # Add the Base Attributes and the Validated list attributes to a collection
            $attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
            $attributeCollection.Add($attributes)
            #$attributeCollection.Add($attributesValidate)

            #Create a new object with the Name, Type, and attributes collection.
            $typeValue_dynParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("typeValue", [STRING], $attributeCollection)

            # Add Parameter to the Dictionary collection
            $paramDictionary.Add("typeValue", $typeValue_dynParam)
            #endregion typeValue

            #region includeChildObjectTypes
            #Create the base DynamicParam attributes
            $attributes = new-object System.Management.Automation.ParameterAttribute
            $attributes.ParameterSetName = "__AllParameterSets"
            $attributes.Mandatory = $false

            # Create the ValidateSet list
            #$attributesValidate = New-Object System.Management.Automation.ValidateSetAttribute @("Text", "Integer", "Boolean", "Double", "Date", "Time", "Date Time", "URL", "Email", "Text Area", "Select", "IP Address")

            # Add the Base Attributes and the Validated list attributes to a collection
            $attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
            $attributeCollection.Add($attributes)
            #$attributeCollection.Add($attributesValidate)

            #Create a new object with the Name, Type, and attributes collection.
            $includeChildObjectTypes_dynParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("includeChildObjectTypes", [STRING], $attributeCollection)

            # Add Parameter to the Dictionary collection
            $paramDictionary.Add("includeChildObjectTypes", $includeChildObjectTypes_dynParam)
            #endregion includeChildObjectTypes

            #region iql
            #Create the base DynamicParam attributes
            $attributes = new-object System.Management.Automation.ParameterAttribute
            $attributes.ParameterSetName = "__AllParameterSets"
            $attributes.Mandatory = $false

            # Create the ValidateSet list
            #$attributesValidate = New-Object System.Management.Automation.ValidateSetAttribute @("Text", "Integer", "Boolean", "Double", "Date", "Time", "Date Time", "URL", "Email", "Text Area", "Select", "IP Address")

            # Add the Base Attributes and the Validated list attributes to a collection
            $attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
            $attributeCollection.Add($attributes)
            #$attributeCollection.Add($attributesValidate)

            #Create a new object with the Name, Type, and attributes collection.
            $iql_dynParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("iql", [STRING], $attributeCollection)

            # Add Parameter to the Dictionary collection
            $paramDictionary.Add("iql", $iql_dynParam)
            #endregion iql
        }

        if ($Type -like "User") {
            #region typeValueMulti
            #The JIRA groups to restrict selection
            #Create the base DynamicParam attributes
            $attributes = new-object System.Management.Automation.ParameterAttribute
            $attributes.ParameterSetName = "__AllParameterSets"
            $attributes.Mandatory = $false

            # Create the ValidateSet list
            #$attributesValidate = New-Object System.Management.Automation.ValidateSetAttribute @("Text", "Integer", "Boolean", "Double", "Date", "Time", "Date Time", "URL", "Email", "Text Area", "Select", "IP Address")
        
            # Add the Base Attributes and the Validated list attributes to a collection
            $attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
            $attributeCollection.Add($attributes)
            #$attributeCollection.Add($attributesValidate)

            #Create a new object with the Name, Type, and attributes collection.
            $typeValueMulti_dynParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("typeValueMulti", [ARRAY], $attributeCollection)

            # Add Parameter to the Dictionary collection
            $paramDictionary.Add("typeValueMulti", $typeValueMulti_dynParam)
            #endregion typeValueMulti
        }

        if ($Type -like "Object" -or $Type -like "URL" -or $Type -like "Confluence") {
            #region additionalValue
            #Create the base DynamicParam attributes
            $attributes = new-object System.Management.Automation.ParameterAttribute
            $attributes.ParameterSetName = "__AllParameterSets"
            $attributes.Mandatory = $false
            
            # Create the ValidateSet list
            if ($Type -like "URL") {
                $attributesValidate = New-Object System.Management.Automation.ValidateSetAttribute @("DISABLED", "ENABLED")
            }
            if ($Type -like "User") {
                $attributesValidate = New-Object System.Management.Automation.ValidateSetAttribute @("SHOW_PROFILE", "HIDE_PROFILE")
            }
            #$attributesValidate = New-Object System.Management.Automation.ValidateSetAttribute @("Text", "Integer", "Boolean", "Double", "Date", "Time", "Date Time", "URL", "Email", "Text Area", "Select", "IP Address")
        
            # Add the Base Attributes and the Validated list attributes to a collection
            $attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
            $attributeCollection.Add($attributes)
            #$attributeCollection.Add($attributesValidate)

            #Create a new object with the Name, Type, and attributes collection.
            $additionalValue_dynParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("additionalValue", [STRING], $attributeCollection)

            # Add Parameter to the Dictionary collection
            $paramDictionary.Add("additionalValue", $additionalValue_dynParam)
            #endregion additionalValue
        }

        if ($Type -like "Email" -or $Type -like "Select" -or $Type -like "Object" -or $Type -like "User" -or $Type -like "Group" -or $Type -like "Version" -or $Type -like "Project") {
            #region minimumCardinality
            #The JIRA groups to restrict selection
            #Create the base DynamicParam attributes
            $attributes = new-object System.Management.Automation.ParameterAttribute
            $attributes.ParameterSetName = "__AllParameterSets"

            $attributes.Mandatory = $false
            
            # Create the ValidateSet list
            #$attributesValidate = New-Object System.Management.Automation.ValidateSetAttribute @("Text", "Integer", "Boolean", "Double", "Date", "Time", "Date Time", "URL", "Email", "Text Area", "Select", "IP Address")
        
            # Add the Base Attributes and the Validated list attributes to a collection
            $attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
            $attributeCollection.Add($attributes)
            #$attributeCollection.Add($attributesValidate)

            #Create a new object with the Name, Type, and attributes collection.
            $minimumCardinality_dynParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("minimumCardinality", [INT], $attributeCollection)
            $PSBoundParameters["minimumCardinality_dynParam"] = 0

            # Add Parameter to the Dictionary collection
            $paramDictionary.Add("minimumCardinality", $minimumCardinality_dynParam)
            #endregion minimumCardinality

            #region maximumCardinality
            #The JIRA groups to restrict selection
            #Create the base DynamicParam attributes
            $attributes = new-object System.Management.Automation.ParameterAttribute
            $attributes.ParameterSetName = "__AllParameterSets"

            $attributes.Mandatory = $false
            
            # Create the ValidateSet list
            #$attributesValidate = New-Object System.Management.Automation.ValidateSetAttribute @("Text", "Integer", "Boolean", "Double", "Date", "Time", "Date Time", "URL", "Email", "Text Area", "Select", "IP Address")
        
            # Add the Base Attributes and the Validated list attributes to a collection
            $attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
            $attributeCollection.Add($attributes)
            #$attributeCollection.Add($attributesValidate)

            #Create a new object with the Name, Type, and attributes collection.
            $maximumCardinality_dynParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("maximumCardinality", [INT], $attributeCollection)
            $PSBoundParameters["maximumCardinality_dynParam"] = 1

            # Add Parameter to the Dictionary collection
            $paramDictionary.Add("maximumCardinality", $maximumCardinality_dynParam)
            #endregion maximumCardinality
        }

        if ($Type -like "Text" -or $Type -like "Email") {
            #region regexValidation
            #Create the base DynamicParam attributes
            $attributes = new-object System.Management.Automation.ParameterAttribute
            $attributes.ParameterSetName = "__AllParameterSets"
            $attributes.Mandatory = $false

            # Create the ValidateSet list
            #$attributesValidate = New-Object System.Management.Automation.ValidateSetAttribute @("Text", "Integer", "Boolean", "Double", "Date", "Time", "Date Time", "URL", "Email", "Text Area", "Select", "IP Address")

            # Add the Base Attributes and the Validated list attributes to a collection
            $attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
            $attributeCollection.Add($attributes)
            #$attributeCollection.Add($attributesValidate)

            #Create a new object with the Name, Type, and attributes collection.
            $regexValidation_dynParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("regexValidation", [STRING], $attributeCollection)

            # Add Parameter to the Dictionary collection
            $paramDictionary.Add("regexValidation", $regexValidation_dynParam)
            #endregion regexValidation
        }

        if ($Type -like "Options") {
            #region options
            #Create the base DynamicParam attributes
            #The options for the attribute in comma separate list - Might need to add script validation or use .join(",")
            $attributes = new-object System.Management.Automation.ParameterAttribute
            $attributes.ParameterSetName = "__AllParameterSets"
            $attributes.Mandatory = $false

            # Create the ValidateSet list
            #$attributesValidate = New-Object System.Management.Automation.ValidateSetAttribute @("Text", "Integer", "Boolean", "Double", "Date", "Time", "Date Time", "URL", "Email", "Text Area", "Select", "IP Address")

            # Add the Base Attributes and the Validated list attributes to a collection
            $attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
            $attributeCollection.Add($attributes)
            #$attributeCollection.Add($attributesValidate)

            #Create a new object with the Name, Type, and attributes collection.
            $options_dynParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("options", [ARRAY], $attributeCollection)

            # Add Parameter to the Dictionary collection
            $paramDictionary.Add("options", $options_dynParam)
            #endregion options
        }
        #Return all the dynamic parameters inside the dictionary
        return $paramDictionary
    }

    begin {
        
        # Translate the Object Type Name to the Object Type ID
        switch ($Type) {
            "Default" { $objectTypeID = "0" }
            "Object" { $objectTypeID = "1" }
            "User" { $objectTypeID = "2" }
            "Confluence" { $objectTypeID = "3" }
            "Group" { $objectTypeID = "4" }
            "Status" { $objectTypeID = "7" }
        }

        # Translate the Default Object Type Name to the Default Object Type ID
        switch ($PSBoundParameters["DefaultType"]) {
            "Text" { $DefaultTypeID = 0 }
            "Integer" { $DefaultTypeID = "1" }
            "Boolean" { $DefaultTypeID = "2" }
            "Double" { $DefaultTypeID = "3" }
            "Date" { $DefaultTypeID = "4" }
            "Time" { $DefaultTypeID = "5" }
            "Date Time" { $DefaultTypeID = "6" }
            "URL" { $DefaultTypeID = "7" }
            "Email" { $DefaultTypeID = "8" }
            "Textarea" { $DefaultTypeID = "9" }
            "Select" { $DefaultTypeID = "10" }
            "IP Address" { $DefaultTypeID = "11" }
        } 
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {

        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/objecttypeattribute/$($ParentObjectTypeId)/$($objectTypeAttributeId)"

        #Create default Hash
        $RequestBody = @{
            name = $Name
        }

        #Add required attributes to the hash for all Object Types except Default
        switch ($Type) {
            { @("Object", "Confluence") -contains $_ } {
                $RequestBody.Add("type", $objectTypeID)
                $RequestBody.Add("typeValue", $PSBoundParameters["typeValue"])
                $RequestBody.Add("additionalValue", $PSBoundParameters["additionalValue"])
            }
            "User" { 
                $RequestBody.Add("type", $objectTypeID)
                $RequestBody.Add("typeValueMulti", $PSBoundParameters["typeValueMulti"])
                $RequestBody.Add("additionalValue", $PSBoundParameters["additionalValue"])
            }
            "Group" { 
                $RequestBody.Add("type", $objectTypeID)
                $RequestBody.Add("additionalValue", $PSBoundParameters["additionalValue"])
            }
            "Status" { 
                $RequestBody.Add("type", $objectTypeID)
                $RequestBody.Add("typeValueMulti", $PSBoundParameters["typeValueMulti"])
            }
        }

        #Add required attributes to the hash for all Object Type "Default"
        switch ($PSBoundParameters["DefaultType"]) {
            { @("Text", "Integer", "Boolean", "Double", "Date", "Date_Time", "Email", "TextArea", "IP_Address") -contains $_ } {
                $RequestBody.Add("type", 0)
                $RequestBody.Add("defaultTypeId", $DefaultTypeID)
            }
            "URL" { 
                $RequestBody.Add("type", 0)
                $RequestBody.Add("defaultTypeId", $DefaultTypeID)
                $RequestBody.Add("additionalValue", $PSBoundParameters["additionalValue"])
            }
            "Select" { 
                $RequestBody.Add("type", 0)
                $RequestBody.Add("defaultTypeId", $DefaultTypeID)
                $RequestBody.Add("options", $PSBoundParameters["options"])
            }
        }

        # Add all the aditional Attributes.
        if ($uniqueAttribute) {
            $RequestBody.Add("uniqueAttribute", $UniqueAttribute)
        }
        if ($PSBoundParameters["minimumCardinality"]) {
            $RequestBody.Add("minimumCardinality", $PSBoundParameters["minimumCardinality"])
        }
        if ($PSBoundParameters["maximumCardinality"]) {
            $RequestBody.Add("maximumCardinality", $PSBoundParameters["maximumCardinality"])
        }
        if ($PSBoundParameters["suffix"]) {
            $RequestBody.Add("suffix", $PSBoundParameters["suffix"])
        }
        if ($PSBoundParameters["includeChildObjectTypes"]) {
            $RequestBody.Add("includeChildObjectTypes", $PSBoundParameters["includeChildObjectTypes"])
        }
        if ($PSBoundParameters["iql"]) {
            $RequestBody.Add("iql", $PSBoundParameters["iql"])
        }
        if ($PSBoundParameters["summable"]) {
            $RequestBody.Add("summable", $PSBoundParameters["summable"])
        }
        if ($PSBoundParameters["regexValidation"]) {
            $RequestBody.Add("regexValidation", $PSBoundParameters["regexValidation"])
        }
        if ($PSBoundParameters["options"]) {
            $RequestBody.Add("options", $PSBoundParameters["options"])
        }
        
        $RequestBody = ConvertTo-Json $RequestBody -Depth 1
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Body $RequestBody -Method PUT
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        } 

        $response
    }
}

function Build-ObjectType {
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [Array]$ObjectArray,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$InsightApiKey,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$SchemaID
    )
    
    foreach ($ObjectType in $ObjectArray) {
        
        #Create ObjectType or return info
        try {
            $HashArguments = @{
                objectschemaID = $SchemaID
                InsightApiKey  = $InsightApiKey
            }
            $ObjectTypeParameters = Get-InsightObjectTypes @HashArguments | Where-Object { $_.Name -like $ObjectType.Name }
            if ($ObjectTypeParameters -eq $null) {
                throw "$($ObjectType.Name) - Object not found"
                Write-Verbose 'Object Type not found'
            }
        }
        catch {
            $HashArguments = @{
                Name           = $ObjectType.Name
                IconID         = (Get-InsightIcons -InsightApiKey $InsightApiKey | Where-Object { $_.name -like $ObjectType.Icon }).id
                objectSchemaId = $SchemaID
                InsightApiKey  = $InsightApiKey
            }
            switch ($ObjectType) {
                { $ObjectType.description } {
                    $HashArguments.Add('description', $ObjectType.Description) 
                }
                { $ObjectType.inherited } {
                    $HashArguments.Add('inherited', $ObjectType.inherited) 
                }
                { $ObjectType.abstractObjectType } {
                    $HashArguments.Add('abstractObjectType', $ObjectType.abstractObjectType) 
                }
                { $ObjectType.parentObjectTypeId } {
                    $HashArguments.Add('parentObjectTypeId', $ObjectType.parentObjectTypeId) 
                }
            }
            $ObjectTypeParameters = New-InsightObjectTypes @HashArguments
            Write-Verbose 'Object Type has been created'
        }

        #Create Object Attributes
        if ($ObjectType.Attributes) {
            $HashArguments = @{
                ID            = $ObjectTypeParameters.id
                InsightApiKey = $InsightApiKey
            }
            $ObjectTypeAttributes = Get-InsightObjectTypeAttributes @HashArguments
            
            # Find missing Attributes
            $MissingObjectTypeAttributes = $ObjectType.Attributes | Where-Object { $ObjectTypeAttributes.name -notcontains $_.name }
        
            # Create any missing attributes
            foreach ($Attribute in $MissingObjectTypeAttributes) {
                Write-Host $Attribute -BackgroundColor Yellow
                $HashArguments = @{
                    Name          = $Attribute.name
                    Type          = $Attribute.Type
                    ObjectType    = $ObjectTypeParameters.id
                    InsightApiKey = $InsightApiKey
                }
                switch ($Attribute) {
                    { $Attribute.Description } {
                        $HashArguments.Add('Description', $Attribute.Description) 
                    }
                    { $Attribute.DefaultType } {
                        $HashArguments.Add('DefaultType', $Attribute.DefaultType) 
                    }
                    { $Attribute.ParentObjectTypeId } {
                        $HashArguments.Add('ParentObjectTypeId', $Attribute.ParentObjectTypeId) 
                    }
                    { $Attribute.typeValue } {
                        $HashArguments.Add('typeValue', $Attribute.typeValue) 
                    }
                    { $Attribute.typeValueMulti } {
                        $HashArguments.Add('typeValueMulti', $Attribute.typeValueMulti) 
                    }
                    { $Attribute.additionalValue } {
                        $HashArguments.Add('additionalValue', $Attribute.additionalValue) 
                    }
                    { $Attribute.minimumCardinality } {
                        $HashArguments.Add('minimumCardinality', $Attribute.minimumCardinality) 
                    }
                    { $Attribute.maximumCardinality } {
                        $HashArguments.Add('maximumCardinality', $Attribute.maximumCardinality) 
                    }
                    { $Attribute.suffix } {
                        $HashArguments.Add('suffix', $Attribute.suffix) 
                    }
                    { $Attribute.includeChildObjectTypes } {
                        $HashArguments.Add('includeChildObjectTypes', $Attribute.includeChildObjectTypes) 
                    }
                    { $Attribute.iql } {
                        $HashArguments.Add('iql', $Attribute.iql) 
                    }
                    { $Attribute.uniqueAttribute } {
                        $HashArguments.Add('uniqueAttribute', $Attribute.uniqueAttribute) 
                    }
                    { $Attribute.summable } {
                        $HashArguments.Add('summable', $Attribute.summable) 
                    }
                    { $Attribute.regexValidation } {
                        $HashArguments.Add('regexValidation', $Attribute.regexValidation) 
                    }
                    { $Attribute.options } {
                        $HashArguments.Add('options', $Attribute.options) 
                    }
                }
                New-InsightObjectTypeAttributes @HashArguments
                Write-Verbose "$($Attribute.name): Created"
            }
        } 

        if ($ObjectType.ObjectTypes) {
            # add the parent object type id so that next loop is for a child
            foreach ($child in $ObjectType.ObjectTypes.psobject.Properties) {
                $child.value | Add-Member -NotePropertyName 'parentObjectTypeId' -NotePropertyValue $ObjectTypeParameters.id
            }
            #call itself for recursion
            Build-ObjectType -ObjectArray $ObjectType.ObjectTypes -SchemaID $SchemaID -InsightApiKey $InsightApiKey
        }
    }
}

function Build-Schema {
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [Array]$ObjectArray,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$InsightApiKey
    )
    
    foreach ($item in $ObjectArray) {
        try {
            $HashArguments = @{
                InsightApiKey = $InsightApiKey
            }
            $ObjectSchema = Get-InsightObjectSchema @HashArguments | Where-Object { $_.name -like $item.ObjectSchemaName }
            if (!($ObjectSchema)) {
                throw 'Object Schema not found'
            }
        }
        catch {
            $HashArguments = @{
                Name            = $item.ObjectSchemaName
                ObjectSchemaKey = $item.ObjectSchemaKey
                Description     = $item.ObjectSchemaDescription
                InsightApiKey   = $InsightApiKey
            }
            $ObjectSchema = New-InsightObjectSchema @HashArguments
            Write-Verbose 'Object Schema has been created'
        }
        $ObjectSchema
    }
    
}

function Connect-MEMCM {
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$Server,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$SiteCode
    )

    # Import the ConfigurationManager.psd1 module
    if((Get-Module ConfigurationManager) -eq $null) {
        Import-Module "$($ENV:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1"
    }

    # Connect to the site's drive if it is not already present
    if((Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue) -eq $null) {
        New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $Server
    }

    # Set the current location to be the site
    Set-Location "$($SiteCode):\"
}

function Convert-ChassisType {
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [int]$ChassisID
    )

    class Chassis {
        [string]$Type
        [int]$ID
        [string]$Category
    }
    
    $Chassis = [Chassis]::new()
    
    switch ($ChassisID) {
        1 { 
            $Chassis.Type = 'Other'
            $Chassis.ID = $_
            $Chassis.Category = 'Unknown'
        }
        2 {  
            $Chassis.Type = 'Unknown'
            $Chassis.ID = $_
            $Chassis.Category = 'Unknown'
        }
        3 {
            $Chassis.Type = 'Desktop'
            $Chassis.ID = $_
            $Chassis.Category = 'Desktop'
        }
        4 { 
            $Chassis.Type = 'Low Profile Desktop'
            $Chassis.ID = $_
            $Chassis.Category = 'Desktop'
        }
        5 { 
            $Chassis.Type = 'Pizza Box'
            $Chassis.ID = $_
            $Chassis.Category = 'Desktop'
        }
        6 { 
            $Chassis.Type = 'Mini Tower'
            $Chassis.ID = $_
            $Chassis.Category = 'Desktop'
        }
        7 { 
            $Chassis.Type = 'Tower'
            $Chassis.ID = $_
            $Chassis.Category = 'Desktop'
        }
        8 { 
            $Chassis.Type = 'Portable'
            $Chassis.ID = $_
            $Chassis.Category = 'Laptop'
        }
        9 { 
            $Chassis.Type = 'Laptop'
            $Chassis.ID = $_
            $Chassis.Category = 'Laptop'
        }
        10 { 
            $Chassis.Type = 'Notebook'
            $Chassis.ID = $_
            $Chassis.Category = 'Laptop'
        }
        11 { 
            $Chassis.Type = 'Hand Held'
            $Chassis.ID = $_
            $Chassis.Category = 'Tablet'
        }
        12 { 
            $Chassis.Type = 'Docking Station'
            $Chassis.ID = $_
            $Chassis.Category = 'Dock'
        }
        13 { 
            $Chassis.Type = 'All in One'
            $Chassis.ID = $_
            $Chassis.Category = 'Desktop'
        }
        14 { 
            $Chassis.Type = 'Sub Notebook'
            $Chassis.ID = $_
            $Chassis.Category = 'Laptop'
        }
        15 { 
            $Chassis.Type = 'Space-Saving'
            $Chassis.ID = $_
            $Chassis.Category = 'Server'
        }
        16 { 
            $Chassis.Type = 'Lunch Box'
            $Chassis.ID = $_
            $Chassis.Category = 'Server'
        }
        17 { 
            $Chassis.Type = 'Main System Chassis'
            $Chassis.ID = $_
            $Chassis.Category = 'Server'
        }
        18 { 
            $Chassis.Type = 'Expansion Chassis'
            $Chassis.ID = $_
            $Chassis.Category = 'Desktop'
        }
        19 { 
            $Chassis.Type = 'SubChassis'
            $Chassis.ID = $_
            $Chassis.Category = 'Unknown'
        }
        20 { 
            $Chassis.Type = 'Bus Expansion Chassis'
            $Chassis.ID = $_
            $Chassis.Category = 'Server'
        }
        21 { 
            $Chassis.Type = 'Peripheral Chassis'
            $Chassis.ID = $_
            $Chassis.Category = 'Unknown'
        }
        22 { 
            $Chassis.Type = 'Storage Chassis'
            $Chassis.ID = $_
            $Chassis.Category = 'Unknown'
        }
        23 { 
            $Chassis.Type = 'Rack Mount Chassis'
            $Chassis.ID = $_
            $Chassis.Category = 'Server'
        }
        24 { 
            $Chassis.Type = 'Sealed-Case PC'
            $Chassis.ID = $_
            $Chassis.Category = 'Desktop'
        }
        30 { 
            $Chassis.Type = 'Tablet'
            $Chassis.ID = $_
            $Chassis.Category = 'Tablet'
        }
        31 { 
            $Chassis.Type = 'Convertible'
            $Chassis.ID = $_
            $Chassis.Category = 'Tablet'
        }
        32 { 
            $Chassis.Type = 'Detachable'
            $Chassis.ID = $_
            $Chassis.Category = 'Tablet'
        }
    }
    return $Chassis
}

function Get-MEMCMSQL_ExtendedInfo {
    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [array]$Devices,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$SQLServer,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$Database
    )
    
    begin {
        #Test-Module 'PSInsight'
        Test-Module 'SQLServer'
        #Test-Module 'Join-Object'

        # Grab SQL Data
        $Connection = @{
            ServerInstance = $SQLServer
            Database = $Database
        }
        $v_GS_COMPUTER_SYSTEM   = Invoke-Sqlcmd @Connection -Query 'Select * From v_GS_COMPUTER_SYSTEM'
        $v_GS_PHYSICAL_MEMORY   = Invoke-Sqlcmd @Connection -Query 'Select * From v_GS_PHYSICAL_MEMORY'
        $v_GS_DISK              = Invoke-Sqlcmd @Connection -Query 'Select * From v_GS_DISK'
        $v_GS_OPERATING_SYSTEM  = Invoke-Sqlcmd @Connection -Query 'Select * From v_GS_OPERATING_SYSTEM'
        $v_GS_LOGICAL_DISK      = Invoke-Sqlcmd @Connection -Query 'Select ResourceID,Size0,FreeSpace0 From v_GS_LOGICAL_DISK'
        $v_GS_SYSTEM_ENCLOSURE  = Invoke-Sqlcmd @Connection -Query 'Select ResourceID,ChassisTypes0 From v_GS_SYSTEM_ENCLOSURE'
        $v_GS_X86_PC_MEMORY     = Invoke-Sqlcmd @Connection -Query 'Select ResourceID,TotalPhysicalMemory0 From v_GS_X86_PC_MEMORY'
        $v_GS_VIRTUAL_MACHINE   = Invoke-Sqlcmd @Connection -Query 'Select * From v_GS_VIRTUAL_MACHINE'
        
        # Progress bar if running interactively
        if ([Environment]::UserInteractive -eq $true) {
            $DeviceCount = $Devices.count
            $i = 0
        }
    }
    
    process {
        ForEach ($Device in $Devices) {

            if ($DeviceCount) {
                $Percentage = [math]::Round($(($i / $DeviceCount)*100),2)
        
                $Progress = @{
                    Activity = "Building Computer Objects"
                    Status = "$Percentage% Complete:"
                    PercentComplete = $Percentage
                }
                # add SecondsRemaining
                Write-Progress @Progress
                $i++
            }
            
            $Device | Add-Member -MemberType NoteProperty -Name 'v_GS_COMPUTER_SYSTEM' -Value $($v_GS_COMPUTER_SYSTEM | Where { $_.ResourceID -like $Device.ResourceID }) -Force
            $Device | Add-Member -MemberType NoteProperty -Name 'v_GS_PHYSICAL_MEMORY' -Value $($v_GS_PHYSICAL_MEMORY | Where { $_.ResourceID -like $Device.ResourceID }) -Force
            $Device | Add-Member -MemberType NoteProperty -Name 'v_GS_OPERATING_SYSTEM' -Value $($v_GS_OPERATING_SYSTEM | Where { $_.ResourceID -like $Device.ResourceID }) -Force
            $Device | Add-Member -MemberType NoteProperty -Name 'v_GS_MAPPEDDRIVES640' -Value $($v_GS_MAPPEDDRIVES640 | Where { $_.ResourceID -like $Device.ResourceID }) -Force
            $Device | Add-Member -MemberType NoteProperty -Name 'v_GS_DISK' -Value $($v_GS_DISK | Where { $_.ResourceID -like $Device.ResourceID }) -Force
        
            $Device | Add-Member -MemberType NoteProperty -Name 'v_GS_LOGICAL_DISK' -Value $($v_GS_LOGICAL_DISK | Where { $_.ResourceID -like $Device.ResourceID }) -Force
            $Device | Add-Member -MemberType NoteProperty -Name 'v_GS_SYSTEM_ENCLOSURE' -Value $($v_GS_SYSTEM_ENCLOSURE | Where { $_.ResourceID -like $Device.ResourceID }) -Force
            $Device | Add-Member -MemberType NoteProperty -Name 'v_GS_X86_PC_MEMORY' -Value $($v_GS_X86_PC_MEMORY | Where { $_.ResourceID -like $Device.ResourceID }) -Force
        
            if ($Device.v_GS_SYSTEM_ENCLOSURE.ChassisTypes0) {
                $Chassis = Convert-ChassisType [int]$Device.v_GS_SYSTEM_ENCLOSURE.ChassisTypes0
            }
            
            $Object = [PSCustomObject]@{
                "Hostname" = $Device.Name
                "ResourceID" = $Device.ResourceID
                "Domain" = $Device.Domain
                "SerialNumber" = $Device.SerialNumber
                "IsVirtualMachine" = $Device.IsVirtualMachine
                "LastHardwareScan" = $Device.LastHardwareScan
                "LastLogonUser" = $Device.LastLogonUser
                "PrimaryUser" = $Device.PrimaryUser
                "MACAddress" = $Device.MACAddress
                
                # v_GS_COMPUTER_SYSTEM
                "Manufacturer" = $Device.v_GS_COMPUTER_SYSTEM.Manufacturer0
                "Model" = $Device.v_GS_COMPUTER_SYSTEM.Model0
                "TotalPhysicalMemory" = $Device.v_GS_COMPUTER_SYSTEM.TotalPhysicalMemory0
        
                # v_GS_SYSTEM_ENCLOSURE
                "ChassisType" = $Chassis.Type
                "ChassisTypeID" = [int]$Chassis.ID
                "ChassisCategory" = $Chassis.Category
        
                # v_GS_OPERATING_SYSTEM
                "Operating System" = $Device.DeviceOS
                "OS Build" = $Device.DeviceOSbuild
                "Caption" = $Device.v_GS_OPERATING_SYSTEM.Caption0
                "CSDVersion" = $Device.v_GS_OPERATING_SYSTEM.CSDVersion0
                "InstallDate" = $Device.v_GS_OPERATING_SYSTEM.InstallDate0
                "OSArchitecture" = $Device.v_GS_OPERATING_SYSTEM.OSArchitecture0
                "Version" = $Device.v_GS_OPERATING_SYSTEM.Version0
        
                # v_GS_PHYSICAL_MEMORY - Physical Memory (Desktop?)
                "DeviceLocator" = $Device.v_GS_PHYSICAL_MEMORY.DeviceLocator0
                "FormFactor" = $Device.v_GS_PHYSICAL_MEMORY.FormFactor0
                "Capacity" = $Device.v_GS_PHYSICAL_MEMORY.Capacity0
                "Speed" = $Device.v_GS_PHYSICAL_MEMORY.Speed0
        
                # $v_GS_X86_PC_MEMORY
                "TotalPhysicalMemory0" = $Device.v_GS_X86_PC_MEMORY.TotalPhysicalMemory0
        
                # v_GS_DISK - Disk (Desktop?)
                "Disk_Caption" = $Device.v_GS_DISK.Caption0
                "Disk_DeviceID" = $Device.v_GS_DISK.DeviceID0
                "Disk_InterfaceType" = $Device.v_GS_DISK.InterfaceType0
                "Disk_Manufacturer" = $Device.v_GS_DISK.Manufacturer0
                "Disk_MediaType" = $Device.v_GS_DISK.MediaType0
                "Disk_Model" = $Device.v_GS_DISK.Model0
        
                # v_GS_LOGICAL_DISK
                "Disk_Size" = $Device.v_GS_LOGICAL_DISK.Size0
                "Disk_FreeSpace" = $Device.v_GS_LOGICAL_DISK.FreeSpace0
        
            }
            $Object
            }
    }
    
    end {
        
    }
}

function Get-ZoomRoomsObjects {
    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$ObjectTypeName,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$ZoomApiKey,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$ZoomApiSecret
    )
    
    begin {
    Test-Module "PSZoom"
    Test-Module "PSInsight"
    Test-Module "Join-Object"

    $ZoomRoomIDs = Get-ZoomRooms -PageSize 300
    $DashboardZoomRooms = Get-ZoomRoomsDashboard -PageSize 300
    $ZoomRoomLocations = Get-ZoomRoomLocations -page_size 300

    function Get-SingleZoomRoomLocation {
        [CmdletBinding()]
        param (
            [String]$location_id,
            [Array]$Locations
        )
        
        begin {
            if ($Locations) {
                
            }
            Else {
                $Locations = Get-ZoomRoomLocations -PageSize 300
            }
        }
        
        process {
    
            $FloorObj = $Locations | Where-Object { $_.id -like $location_id }
            $CampusObj = $Locations | Where-Object { $_.id -like $FloorObj.parent_location_id }
            $CityObj = $Locations | Where-Object { $_.id -like $CampusObj.parent_location_id }
            $StateObj = $Locations | Where-Object { $_.id -like $CityObj.parent_location_id }
            $CountryObj = $Locations | Where-Object { $_.id -like $StateObj.parent_location_id }
    
            $hierarchy = @{
                Floor   = $FloorObj.name
                Campus  = $CampusObj.name
                City    = $CityObj.name
                State   = $StateObj.name
                Country = $CountryObj.name
            }
    
        }
        
        end {
            $hierarchy
        }
    }

    $ZoomRooms = @()    
    }
    
    process {
        foreach ($Room in $ZoomRoomIDs) {

            # Join Objects
            $ZRoom = Join-Object -Left $Room -Right $DashboardZoomRooms -LeftJoinProperty 'name' -RightJoinProperty 'room_name' -ExcludeRightProperties id, status
            
            # Add Devices with unique custom name for use in CMDB
            $Devices = foreach ($device in $(Get-ZoomRoomDevices -RoomID $Room.id)) {
                Add-Member -InputObject $device -NotePropertyName 'device_name' -NotePropertyValue "$($device.room_name) - $($device.device_type)" -Force
                $device
            }
            Add-Member -InputObject $ZRoom -NotePropertyName 'Devices' -NotePropertyValue $Devices -Force
            
            # Add Hierarchy
            Add-Member -InputObject $ZRoom -NotePropertyName 'Location' -NotePropertyValue $(Get-SingleZoomRoomLocation -location_id $Room.location_id -Locations $ZoomRoomLocations) -Force
            
            $ZRoom
            }
    }
    
    end {
        
    }
}

function Join-Object
{
    <#
    .SYNOPSIS
        Join data from two sets of objects based on a common value

    .DESCRIPTION
        Join data from two sets of objects based on a common value

        For more details, see the accompanying blog post:
            http://ramblingcookiemonster.github.io/Join-Object/

        For even more details, see the original code and discussions that this borrows from:
            Dave Wyatt's Join-Object - http://powershell.org/wp/forums/topic/merging-very-large-collections
            Lucio Silveira's Join-Object - http://blogs.msdn.com/b/powershell/archive/2012/07/13/join-object.aspx

    .PARAMETER Left
        'Left' collection of objects to join. You can use the pipeline for Left.

        The objects in this collection should be consistent.
        We look at the properties on the first object for a baseline.
    
    .PARAMETER Right
        'Right' collection of objects to join.

        The objects in this collection should be consistent.
        We look at the properties on the first object for a baseline.

    .PARAMETER LeftJoinProperty
        Property on Left collection objects that we match up with RightJoinProperty on the Right collection

    .PARAMETER RightJoinProperty
        Property on Right collection objects that we match up with LeftJoinProperty on the Left collection

    .PARAMETER LeftProperties
        One or more properties to keep from Left. Default is to keep all Left properties (*).

        Each property can:
            - Be a plain property name like "Name"
            - Contain wildcards like "*"
            - Be a hashtable like @{Name="Product Name";Expression={$_.Name}}.
                 Name is the output property name
                 Expression is the property value ($_ as the current object)
                
                 Alternatively, use the Suffix or Prefix parameter to avoid collisions
                 Each property using this hashtable syntax will be excluded from suffixes and prefixes

    .PARAMETER RightProperties
        One or more properties to keep from Right. Default is to keep all Right properties (*).

        Each property can:
            - Be a plain property name like "Name"
            - Contain wildcards like "*"
            - Be a hashtable like @{Name="Product Name";Expression={$_.Name}}.
                 Name is the output property name
                 Expression is the property value ($_ as the current object)
                
                 Alternatively, use the Suffix or Prefix parameter to avoid collisions
                 Each property using this hashtable syntax will be excluded from suffixes and prefixes

    .PARAMETER Prefix
        If specified, prepend Right object property names with this prefix to avoid collisions

        Example:
            Property Name = 'Name'
            Suffix = 'j_'
            Resulting Joined Property Name = 'j_Name'

    .PARAMETER Suffix
        If specified, append Right object property names with this suffix to avoid collisions

        Example:
            Property Name = 'Name'
            Suffix = '_j'
            Resulting Joined Property Name = 'Name_j'

    .PARAMETER Type
        Type of join. Default is AllInLeft.

        AllInLeft will have all elements from Left at least once in the output, and might appear more than once
          if the where clause is true for more than one element in right, Left elements with matches in Right are
          preceded by elements with no matches.
          SQL equivalent: outer left join (or simply left join)

        AllInRight is similar to AllInLeft.
        
        OnlyIfInBoth will cause all elements from Left to be placed in the output, only if there is at least one
          match in Right.
          SQL equivalent: inner join (or simply join)
         
        AllInBoth will have all entries in right and left in the output. Specifically, it will have all entries
          in right with at least one match in left, followed by all entries in Right with no matches in left,
          followed by all entries in Left with no matches in Right.
          SQL equivalent: full join

    .EXAMPLE
        #
        #Define some input data.

        $l = 1..5 | Foreach-Object {
            [pscustomobject]@{
                Name = "jsmith$_"
                Birthday = (Get-Date).adddays(-1)
            }
        }

        $r = 4..7 | Foreach-Object{
            [pscustomobject]@{
                Department = "Department $_"
                Name = "Department $_"
                Manager = "jsmith$_"
            }
        }

        #We have a name and Birthday for each manager, how do we find their department, using an inner join?
        Join-Object -Left $l -Right $r -LeftJoinProperty Name -RightJoinProperty Manager -Type OnlyIfInBoth -RightProperties Department


            # Name Birthday Department
            # ---- -------- ----------
            # jsmith4 4/14/2015 3:27:22 PM Department 4
            # jsmith5 4/14/2015 3:27:22 PM Department 5

    .EXAMPLE
        #
        #Define some input data.

        $l = 1..5 | Foreach-Object {
            [pscustomobject]@{
                Name = "jsmith$_"
                Birthday = (Get-Date).adddays(-1)
            }
        }

        $r = 4..7 | Foreach-Object{
            [pscustomobject]@{
                Department = "Department $_"
                Name = "Department $_"
                Manager = "jsmith$_"
            }
        }

        #We have a name and Birthday for each manager, how do we find all related department data, even if there are conflicting properties?
        $l | Join-Object -Right $r -LeftJoinProperty Name -RightJoinProperty Manager -Type AllInLeft -Prefix j_

            # Name Birthday j_Department j_Name j_Manager
            # ---- -------- ------------ ------ ---------
            # jsmith1 4/14/2015 3:27:22 PM
            # jsmith2 4/14/2015 3:27:22 PM
            # jsmith3 4/14/2015 3:27:22 PM
            # jsmith4 4/14/2015 3:27:22 PM Department 4 Department 4 jsmith4
            # jsmith5 4/14/2015 3:27:22 PM Department 5 Department 5 jsmith5

    .EXAMPLE
        #
        #Hey! You know how to script right? Can you merge these two CSVs, where Path1's IP is equal to Path2's IP_ADDRESS?
        
        #Get CSV data
        $s1 = Import-CSV $Path1
        $s2 = Import-CSV $Path2

        #Merge the data, using a full outer join to avoid omitting anything, and export it
        Join-Object -Left $s1 -Right $s2 -LeftJoinProperty IP_ADDRESS -RightJoinProperty IP -Prefix 'j_' -Type AllInBoth |
            Export-CSV $MergePath -NoTypeInformation

    .EXAMPLE
        #
        # "Hey Warren, we need to match up SSNs to Active Directory users, and check if they are enabled or not.
        # I'll e-mail you an unencrypted CSV with all the SSNs from gmail, what could go wrong?"
        
        # Import some SSNs.
        $SSNs = Import-CSV -Path D:\SSNs.csv

        #Get AD users, and match up by a common value, samaccountname in this case:
        Get-ADUser -Filter "samaccountname -like 'wframe*'" |
            Join-Object -LeftJoinProperty samaccountname -Right $SSNs `
                        -RightJoinProperty samaccountname -RightProperties ssn `
                        -LeftProperties samaccountname, enabled, objectclass

    .NOTES
        This borrows from:
            Dave Wyatt's Join-Object - http://powershell.org/wp/forums/topic/merging-very-large-collections/
            Lucio Silveira's Join-Object - http://blogs.msdn.com/b/powershell/archive/2012/07/13/join-object.aspx

        Changes:
            Always display full set of properties
            Display properties in order (left first, right second)
            If specified, add suffix or prefix to right object property names to avoid collisions
            Use a hashtable rather than ordereddictionary (avoid case sensitivity)

    .LINK
        http://ramblingcookiemonster.github.io/Join-Object/

    .FUNCTIONALITY
        PowerShell Language

    #>

    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,
        ValueFromPipeLine = $true)]
        [object[]] $Left,

        # List to join with $Left
        [Parameter(Mandatory=$true)]
        [object[]] $Right,

        [Parameter(Mandatory = $true)]
        [string] $LeftJoinProperty,

        [Parameter(Mandatory = $true)]
        [string] $RightJoinProperty,

        [object[]]$LeftProperties = '*',

        # Properties from $Right we want in the output.
        # Like LeftProperties, each can be a plain name, wildcard or hashtable. See the LeftProperties comments.
        [object[]]$RightProperties = '*',

        [validateset( 'AllInLeft', 'OnlyIfInBoth', 'AllInBoth', 'AllInRight')]
        [Parameter(Mandatory=$false)]
        [string]$Type = 'AllInLeft',

        [string]$Prefix,
        [string]$Suffix
    )
    Begin
    {
        function AddItemProperties($item, $properties, $hash)
        {
            if ($null -eq $item)
            {
                return
            }

            foreach($property in $properties)
            {
                $propertyHash = $property -as [hashtable]
                if($null -ne $propertyHash)
                {
                    $hashName = $propertyHash["name"] -as [string]         
                    $expression = $propertyHash["expression"] -as [scriptblock]

                    $expressionValue = $expression.Invoke($item)[0]
            
                    $hash[$hashName] = $expressionValue
                }
                else
                {
                    foreach($itemProperty in $item.psobject.Properties)
                    {
                        if ($itemProperty.Name -like $property)
                        {
                            $hash[$itemProperty.Name] = $itemProperty.Value
                        }
                    }
                }
            }
        }

        function TranslateProperties
        {
            [cmdletbinding()]
            param(
                [object[]]$Properties,
                [psobject]$RealObject,
                [string]$Side)

            foreach($Prop in $Properties)
            {
                $propertyHash = $Prop -as [hashtable]
                if($null -ne $propertyHash)
                {
                    $hashName = $propertyHash["name"] -as [string]         
                    $expression = $propertyHash["expression"] -as [scriptblock]

                    $ScriptString = $expression.tostring()
                    if($ScriptString -notmatch 'param\(')
                    {
                        Write-Verbose "Property '$HashName'`: Adding param(`$_) to scriptblock '$ScriptString'"
                        $Expression = [ScriptBlock]::Create("param(`$_)`n $ScriptString")
                    }
                
                    $Output = @{Name =$HashName; Expression = $Expression }
                    Write-Verbose "Found $Side property hash with name $($Output.Name), expression:`n$($Output.Expression | out-string)"
                    $Output
                }
                else
                {
                    foreach($ThisProp in $RealObject.psobject.Properties)
                    {
                        if ($ThisProp.Name -like $Prop)
                        {
                            Write-Verbose "Found $Side property '$($ThisProp.Name)'"
                            $ThisProp.Name
                        }
                    }
                }
            }
        }

        function WriteJoinObjectOutput($leftItem, $rightItem, $leftProperties, $rightProperties)
        {
            $properties = @{}

            AddItemProperties $leftItem $leftProperties $properties
            AddItemProperties $rightItem $rightProperties $properties

            New-Object psobject -Property $properties
        }

        #Translate variations on calculated properties. Doing this once shouldn't affect perf too much.
        foreach($Prop in @($LeftProperties + $RightProperties))
        {
            if($Prop -as [hashtable])
            {
                foreach($variation in ('n','label','l'))
                {
                    if(-not $Prop.ContainsKey('Name') )
                    {
                        if($Prop.ContainsKey($variation) )
                        {
                            $Prop.Add('Name',$Prop[$Variation])
                        }
                    }
                }
                if(-not $Prop.ContainsKey('Name') -or $Prop['Name'] -like $null )
                {
                    Throw "Property is missing a name`n. This should be in calculated property format, with a Name and an Expression:`n@{Name='Something';Expression={`$_.Something}}`nAffected property:`n$($Prop | out-string)"
                }


                if(-not $Prop.ContainsKey('Expression') )
                {
                    if($Prop.ContainsKey('E') )
                    {
                        $Prop.Add('Expression',$Prop['E'])
                    }
                }
            
                if(-not $Prop.ContainsKey('Expression') -or $Prop['Expression'] -like $null )
                {
                    Throw "Property is missing an expression`n. This should be in calculated property format, with a Name and an Expression:`n@{Name='Something';Expression={`$_.Something}}`nAffected property:`n$($Prop | out-string)"
                }
            }        
        }

        $leftHash = @{}
        $rightHash = @{}

        # Hashtable keys can't be null; we'll use any old object reference as a placeholder if needed.
        $nullKey = New-Object psobject
        
        $bound = $PSBoundParameters.keys -contains "InputObject"
        if(-not $bound)
        {
            [System.Collections.ArrayList]$LeftData = @()
        }
    }
    Process
    {
        #We pull all the data for comparison later, no streaming
        if($bound)
        {
            $LeftData = $Left
        }
        Else
        {
            foreach($Object in $Left)
            {
                [void]$LeftData.add($Object)
            }
        }
    }
    End
    {
        foreach ($item in $Right)
        {
            $key = $item.$RightJoinProperty

            if ($null -eq $key)
            {
                $key = $nullKey
            }

            $bucket = $rightHash[$key]

            if ($null -eq $bucket)
            {
                $bucket = New-Object System.Collections.ArrayList
                $rightHash.Add($key, $bucket)
            }

            $null = $bucket.Add($item)
        }

        foreach ($item in $LeftData)
        {
            $key = $item.$LeftJoinProperty

            if ($null -eq $key)
            {
                $key = $nullKey
            }

            $bucket = $leftHash[$key]

            if ($null -eq $bucket)
            {
                $bucket = New-Object System.Collections.ArrayList
                $leftHash.Add($key, $bucket)
            }

            $null = $bucket.Add($item)
        }

        $LeftProperties = TranslateProperties -Properties $LeftProperties -Side 'Left' -RealObject $LeftData[0]
        $RightProperties = TranslateProperties -Properties $RightProperties -Side 'Right' -RealObject $Right[0]

        #I prefer ordered output. Left properties first.
        [string[]]$AllProps = $LeftProperties

        #Handle prefixes, suffixes, and building AllProps with Name only
        $RightProperties = foreach($RightProp in $RightProperties)
        {
            if(-not ($RightProp -as [Hashtable]))
            {
                Write-Verbose "Transforming property $RightProp to $Prefix$RightProp$Suffix"
                @{
                    Name="$Prefix$RightProp$Suffix"
                    Expression=[scriptblock]::create("param(`$_) `$_.'$RightProp'")
                }
                $AllProps += "$Prefix$RightProp$Suffix"
            }
            else
            {
                Write-Verbose "Skipping transformation of calculated property with name $($RightProp.Name), expression:`n$($RightProp.Expression | out-string)"
                $AllProps += [string]$RightProp["Name"]
                $RightProp
            }
        }

        $AllProps = $AllProps | Select -Unique

        Write-Verbose "Combined set of properties: $($AllProps -join ', ')"

        foreach ( $entry in $leftHash.GetEnumerator() )
        {
            $key = $entry.Key
            $leftBucket = $entry.Value

            $rightBucket = $rightHash[$key]

            if ($null -eq $rightBucket)
            {
                if ($Type -eq 'AllInLeft' -or $Type -eq 'AllInBoth')
                {
                    foreach ($leftItem in $leftBucket)
                    {
                        WriteJoinObjectOutput $leftItem $null $LeftProperties $RightProperties | Select $AllProps
                    }
                }
            }
            else
            {
                foreach ($leftItem in $leftBucket)
                {
                    foreach ($rightItem in $rightBucket)
                    {
                        WriteJoinObjectOutput $leftItem $rightItem $LeftProperties $RightProperties | Select $AllProps
                    }
                }
            }
        }

        if ($Type -eq 'AllInRight' -or $Type -eq 'AllInBoth')
        {
            foreach ($entry in $rightHash.GetEnumerator())
            {
                $key = $entry.Key
                $rightBucket = $entry.Value

                $leftBucket = $leftHash[$key]

                if ($null -eq $leftBucket)
                {
                    foreach ($rightItem in $rightBucket)
                    {
                        WriteJoinObjectOutput $null $rightItem $LeftProperties $RightProperties | Select $AllProps
                    }
                }
            }
        }
    }
}

#Used to check if modle is already installed and import if it is, else install.
function Test-Module {
    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$Module
    )

    # If the module is already imported, notify and then do nothing
    if (Get-Module | Where-Object { $_.Name -eq $Module }) {
        
        if ($(Get-Module $Module).Version -notlike $((Find-Module $Module).version)) {
            try {
                Update-Module $Module -Force
                Write-Verbose "$Module was found on disk and updated to latest version"
                Remove-Module $Module
                Import-Module $Module
            }
            catch {
                Write-Verbose "Failed to update $Module"
            }
        }
        else {
            Write-Verbose "$Module already imported and latest version"
            Import-Module $Module
        }

    }
    else {
        # If the module is not imported but is available on disk then import
        if (Get-Module -ListAvailable | Where-Object { $_.Name -eq $Module }) {

            if ((Get-Module -ListAvailable | Where-Object { $_.Name -eq $Module }).version -notlike $((Find-Module $Module).version)) {
                    try {
                        Update-Module $Module -Force
                        Write-Verbose "$Module was found on disk and updated to latest version"
                        Import-Module $Module
                        Write-Verbose "$Module was imported"
                    }
                    catch {
                        Write-Verbose "Failed to import $Module"
                    }
                }
                else {
                    try {
                        Import-Module $Module
                        Write-Verbose "$Module was imported"
                    }
                    catch {
                        Write-Verbose "Failed to import $Module"
                    }
                }
            }
        else {

            # If the module is not imported\available on the disk but is in online gallery then install and then import
            if (Find-Module -Name $Module | Where-Object { $_.Name -eq $Module }) {
                
                try {
                    Install-Module -Name $Module -Force -Verbose -Scope CurrentUser
                    Write-Verbose "$Module was downloaded from Repo and installed"
                    Import-Module $Module -Verbose
                }
                catch {
                    Write-Verbose "Failed to download $Module from Repo"
                }
            }
            else {
                # If the module is not imported\available or found in the online gallery then notify and abort
                Write-Verbose "Module $Module not imported, not available, and not in online gallery, exiting."
                EXIT 1
            }
        }
    }
}

function Get-InsightObjectSchema {
    <#

.SYNOPSIS
Resource to find object schemas in Insight for a specific object schema. The object schemas are responded in a list.

.DESCRIPTION
Resource to find object schemas in Insight for a specific object schema. The object schemas are responded in a list.

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 3
name : MyObjectSchema
objectSchemaKey : MOS
status : Ok
description : Object Schema
created : 2020-09-16T00:50:30.919Z
updated : 2020-09-16T01:56:33.430Z
objectCount : 0
objectTypeCount : 1

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Object+schema

.EXAMPLE
Get-InsightObjectSchema -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/objectschema/list"
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Method GET
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        }        

        If (!($response.objectschemas)) {
            # Throw error so that catch block in script is triggered
            throw "API call successful but no object schemas found on server"
        }
        Else {
            If($response.objectschemas){
                #Returns multiple schemas
                $response.objectschemas
            }
            else{
                #Returns single schema
                $response
            }
        }
    }
}

function New-InsightObjectSchema {
    <#

.SYNOPSIS
Resource to create an object schema in Insight.

.DESCRIPTION
Resource to create an object schema in Insight.

.PARAMETER Name
The object schema name.

.PARAMETER ObjectSchemaKey
The object schema key.

.PARAMETER Description
The object schema description.

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 1
name : CMDB
objectSchemaKey : ABC
status : Ok
description : My Object Schema
created : 2020-09-16T00:22:31.948Z
updated : 2020-09-16T00:22:31.963Z
objectCount : 0
objectTypeCount : 0

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Object+schema

.EXAMPLE
New-InsightObjectSchema -Name "MyObjectSchema" -ObjectSchemaKey "NAS" -Description "My New Object Schema" -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$Name,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$ObjectSchemaKey,

        [Parameter(Mandatory = $false)]
        [string]$Description,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/objectschema/create"

        $RequestBody = @{
            'name'              = $Name
            'objectSchemaKey'   = $ObjectSchemaKey
            }
            $RequestBody.Add('description',$Description)
        
        $RequestBody = ConvertTo-Json $RequestBody -Depth 1
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Body $RequestBody -Method POST
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        }
        
        Write-Output $response
        }
}

function Remove-InsightObjectSchema {
    <#

.SYNOPSIS
Resource to delete an object schema in Insight.

.DESCRIPTION
Resource to delete an object schema in Insight.

.PARAMETER ID
The object schema ID.

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 1
name : CMDB
objectSchemaKey : ABC
status : Ok
description : My Object Schema
created : 2020-09-16T00:22:31.948Z
updated : 2020-09-16T00:22:31.963Z
objectCount : 0
objectTypeCount : 0

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Object+schema

.EXAMPLE
Remove-InsightObjectSchema -ID 1 -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [int]$ID,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/objectschema/$($ID)"

    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Method Delete
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        }
        
        Write-Output $response
        }
}

function Set-InsightObjectSchema {
    <#

.SYNOPSIS
Resource to update an object schema in Insight.

.DESCRIPTION
Resource to update an object schema in Insight.

.PARAMETER ID
The id of the schema.

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 1
name : MyObjectSchema
objectSchemaKey : MOS
status : Ok
description : My New Object Schema - Updated
created : 2020-09-16T00:22:31.948Z
updated : 2020-09-16T00:22:31.963Z
objectCount : 0
objectTypeCount : 0

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Object+schema

.EXAMPLE
Set-InsightObjectSchema -ID 3 -Name "MyObjectSchema" -ObjectSchemaKey "MOS" -Description "My New Object Schema - Updated" -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [int]$ID,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$Name,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$ObjectSchemaKey,

        [Parameter(Mandatory = $false)]
        [string]$Description,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/objectschema/$($ID)"

        $RequestBody = @{
            'name'              = $Name
            'objectSchemaKey'   = $ObjectSchemaKey
            }
            If($Description){
            $RequestBody.Add('description',$Description)
            }
        
        $RequestBody = ConvertTo-Json $RequestBody -Depth 1
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Body $RequestBody -Method PUT
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        }
        
        Write-Output $response
        }
}

function Find-InsightObjects {
    <#

.SYNOPSIS
Resource to load an object in Insight.

.DESCRIPTION
Resource to load an object in Insight.

.PARAMETER ObjectTypeID
The objects ID.

.PARAMETER query
The query string is a simple text match against the start of the name string.

.PARAMETER start
The start index (0..N).

.PARAMETER limit
The number of objects to fetch.

.PARAMETER includeChildren
If children objects should be included.

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 3
label : MyObject
objectKey : ABC-3
avatar : @{url16=/rest/insight/1.0/objecttype/2/icon.png?size=16&uuid=3269b6c6-10cc-41de-88ba-99efae71f8...IjoxNjAwMzA3MDc4LCJpYXQiOjE2MDAzMDY4OTh9._CaSkX-QvW1BlK7-4XJB9UikOvegJS-YSjCrCtYUl7A; objectId=3}
objectType : @{id=2; name=My Object Type; type=0; description=A Sample Object Type; icon=; position=0; created=2020-09-16T07:14:02.118Z; updated=2020-09-16T07:14:02.118Z; objectCount=0; objectSchemaId=3; inherited=False; abstractObjectType=False;
               parentObjectTypeInherited=False}
created : 2020-09-17T01:11:02.596Z
updated : 2020-09-17T01:11:02.596Z
hasAvatar : False
timestamp : 1600305062596
attributes : {@{id=9; objectTypeAttribute=; objectTypeAttributeId=7; objectAttributeValues=System.Object[]; objectId=3}, @{id=12; objectTypeAttribute=; objectTypeAttributeId=8; objectAttributeValues=System.Object[]; objectId=3}, @{id=10;
               objectTypeAttribute=; objectTypeAttributeId=9; objectAttributeValues=System.Object[]; objectId=3}, @{id=11; objectTypeAttribute=; objectTypeAttributeId=10; objectAttributeValues=System.Object[]; objectId=3}...}
extendedInfo : @{openIssuesExists=False; attachmentsExists=False}
_links : @{self=/secure/ShowObject.jspa?id=3}
name : MyObject

.LINK
https://documentation.mindville.com/display/ICV8/Objects+-+REST

.EXAMPLE
Find-InsightObjects -ObjectTypeID "286" -limit 5000 -includeChildren $true -InsightApiKey $InsightApiKey
Find-InsightObjects -ObjectTypeID "295" -limit 100 -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [String]$ObjectTypeID,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $false)]
        [String]$query,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $false)]
        [int]$start,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $false)]
        [int]$limit,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $false)]
        [bool]$includeChildren,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {

        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/objecttype/$($ObjectTypeID)/objects"

        $RequestBody = @{
            'objectTypeId' = $objectTypeId
            }
        if ($query) {
            $RequestBody.Add("query",$query )
        }
        if ($start) {
            $RequestBody.Add("start",$start )
        }
        if ($limit) {
            $RequestBody.Add("limit",$limit )
        }
        if ($includeChildren) {
            $RequestBody.Add("includeChildren",$includeChildren )
        }
        
        $RequestBody = ConvertTo-Json $RequestBody -Depth 20

    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Body $RequestBody -Method Post
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        }

        $response
    }
}

function Find-InsightObjectsAdvanced {
    <#

.SYNOPSIS
Resource to load an object in Insight.

.DESCRIPTION
Resource to load an object in Insight.

.PARAMETER page
The page to fetch. (1..N).

.PARAMETER asc
Ascending or descending, asc = 1, desc = 0.

.PARAMETER objectTypeId
The id of the Object Type to search objects from.

.PARAMETER objectId
Set the page to the page where this object is shown

.PARAMETER objectSchemaId
The id of the Object Schema to search from.

.PARAMETER iql
An valid IQL string to filter the returned objects.

.PARAMETER resultsPerPage
The number of objects to fetch.

.PARAMETER orderByTypeAttrId
The id of the attribute to sort the Objects from.

.PARAMETER includeAttributes
If object attributes should be included.

.PARAMETER attributesToDisplay
List of objectTypeAttributes ids to return.

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS

.LINK
https://documentation.mindville.com/display/ICV8/Objects+-+REST

.EXAMPLE
Find-InsightObjects -ObjectTypeID "286" -limit 5000 -includeChildren $true -InsightApiKey $InsightApiKey
Find-InsightObjects -ObjectTypeID "295" -limit 100 -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [int]$ObjectTypeID,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [int]$objectSchemaId,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $false)]
        [int]$objectId,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [int]$resultsPerPage,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $false)]
        [int]$page,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $false)]
        [int]$asc,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $false)]
        [string]$iql,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $false)]
        [int]$orderByTypeAttrId,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $false)]
        [bool]$includeAttributes,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $false)]
        [int[]]$attributesToDisplay,

        [Parameter(Mandatory = $false)]
        [switch]$ShowJSON,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {

        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/object/navlist/iql"

        $RequestBody = @{
            'objectTypeId' = $objectTypeId
            'objectSchemaId' = $objectSchemaId
            'resultsPerPage' = $resultsPerPage
            }
        if ($page) {
            $RequestBody.Add("page",$page )
        }
        if ($asc) {
            $RequestBody.Add("asc",$asc )
        }
        if ($objectId) {
            $RequestBody.Add("objectId",$objectId )
        }
        if ($iql) {
            $RequestBody.Add("iql",$iql )
        }
        if ($orderByTypeAttrId) {
            $RequestBody.Add("orderByTypeAttrId",$orderByTypeAttrId )
        }
        if ($includeAttributes) {
            $RequestBody.Add("includeAttributes",$includeAttributes )
        }
        if ($attributesToDisplay) {
            $list = @{"attributesToDisplayIds" = @($attributesToDisplay)}
            $RequestBody.Add("attributesToDisplay",$list )
        }
        
        $RequestBody = ConvertTo-Json $RequestBody -Depth 20

        if ($ShowJSON -eq $true) {
            Write-Output $RequestBody
            break
        }

    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Body $RequestBody -Method Post
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        }

        $response
    }
}

function Get-InsightObject {
    <#

.SYNOPSIS
Resource to load an object in Insight.

.DESCRIPTION
Resource to load an object in Insight.

.PARAMETER ID
The objects ID.

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 3
label : MyObject
objectKey : ABC-3
avatar : @{url16=/rest/insight/1.0/objecttype/2/icon.png?size=16&uuid=3269b6c6-10cc-41de-88ba-99efae71f8...IjoxNjAwMzA3MDc4LCJpYXQiOjE2MDAzMDY4OTh9._CaSkX-QvW1BlK7-4XJB9UikOvegJS-YSjCrCtYUl7A; objectId=3}
objectType : @{id=2; name=My Object Type; type=0; description=A Sample Object Type; icon=; position=0; created=2020-09-16T07:14:02.118Z; updated=2020-09-16T07:14:02.118Z; objectCount=0; objectSchemaId=3; inherited=False; abstractObjectType=False;
               parentObjectTypeInherited=False}
created : 2020-09-17T01:11:02.596Z
updated : 2020-09-17T01:11:02.596Z
hasAvatar : False
timestamp : 1600305062596
attributes : {@{id=9; objectTypeAttribute=; objectTypeAttributeId=7; objectAttributeValues=System.Object[]; objectId=3}, @{id=12; objectTypeAttribute=; objectTypeAttributeId=8; objectAttributeValues=System.Object[]; objectId=3}, @{id=10;
               objectTypeAttribute=; objectTypeAttributeId=9; objectAttributeValues=System.Object[]; objectId=3}, @{id=11; objectTypeAttribute=; objectTypeAttributeId=10; objectAttributeValues=System.Object[]; objectId=3}...}
extendedInfo : @{openIssuesExists=False; attachmentsExists=False}
_links : @{self=/secure/ShowObject.jspa?id=3}
name : MyObject

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Objects

.EXAMPLE
Get-InsightObject -ID "3" -InsightApiKey $InsightApiKey
Get-InsightObject -ID "ABC-3" -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [String]$ID,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/object/$($ID)"
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Method GET
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        }        

        $response
    }
}

function New-InsightObject {
    <#

.SYNOPSIS
Resource to create object in Insight.

.DESCRIPTION
Resource to create object in Insight.

.PARAMETER objectTypeId
The Object Type ID

.PARAMETER attributes
An Array of parameters built via 'New-InsightObjectAttribute'

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 5
label : Test name
objectKey : ABC-5
avatar : @{url16=/rest/insight/1.0/objecttype/2/icon.png?size=16&uuid=3269b6c6-10cc-41de-88ba-99efae71f889&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOi
             JIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppcmEucGx1Z2lucy5pbnNpZ2h0Iiwic3ViIjoiNWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiwiaW5zaWdodCI6d
             HJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiwiaXNzIjoiY29tLnJpYWRhbGFicy5qaXJhLnBsdWdpbnMuaW5zaWdod
             CIsIm9yaWdpbmFsbHlJc3N1ZWRBdCI6MTYwMDMxNDk2MywiZXhwIjoxNjAwMzE1MTQzLCJpYXQiOjE2MDAzMTQ5NjN9.vzQiy3zF1cgjWFBeymAv1Q6lU0dk-Ewv6kuE
             7Gh0ins; url48=/rest/insight/1.0/objecttype/2/icon.png?size=48&uuid=3269b6c6-10cc-41de-88ba-99efae71f889&jwt=eyJ0eXAiOiJKV1QiLCJ
             hbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppcmEucGx1Z2lucy5pbnNpZ2h0Iiwic3ViIjoiNWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiwiaW5zaW
             dodCI6dHJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiw...; objectId=5}
objectType : @{id=2; name=My Object Type; type=0; description=A Sample Object Type; icon=; position=0; created=2020-09-16T07:14:02.118Z;
             updated=2020-09-16T07:14:02.118Z; objectCount=0; objectSchemaId=3; inherited=False; abstractObjectType=False;
             parentObjectTypeInherited=False}
created : 2020-09-17T03:56:04.262Z
updated : 2020-09-17T03:56:04.262Z
hasAvatar : False
timestamp : 1600314964262
_links : @{self=/secure/ShowObject.jspa?id=5}
name : Test name

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Objects

.EXAMPLE
New-InsightObject -objectTypeId 2 -attributes $array -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [int]$objectTypeId,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true,valuefrompipelinebypropertyname = $true)]
        [array]$attributes,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/object/create"

        $RequestBody = @{
            'objectTypeId' = $objectTypeId
            'attributes'   = @($attributes)
            }
            
        
        $RequestBody = ConvertTo-Json $RequestBody -Depth 20
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Body $RequestBody -Method POST
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        }
        
        Write-Output $response
        }
}


function New-InsightObjectAttribute {
    <#

.SYNOPSIS
Resource to create an attribute array to send to New-InsightObject.

.DESCRIPTION
Resource to create an attribute array to send to New-InsightObject.

.PARAMETER objectTypeAttributeId
The object type attribute ID to populate.

.PARAMETER objectAttributeValues
The object attribute value.

.OUTPUTS
Name Value
---- -----
objectTypeAttributeId 8
objectAttributeValues {System.Collections.Hashtable}

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Object+type+attributes

.EXAMPLE
$1 = New-InsightObjectAttribute -objectTypeAttributeId 8 -objectAttributeValues "Test name"
$2 = New-InsightObjectAttribute -objectTypeAttributeId 12 -objectAttributeValues "test ID"

$array = @($1,$2) # For use with New-InsightObject

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true,valuefrompipelinebypropertyname = $true)]
        [int]$objectTypeAttributeId,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true,valuefrompipelinebypropertyname = $true)]
        [String[]]$objectAttributeValues
    )
    
    begin {
        $values = New-Object System.Collections.ArrayList 
    }
    
    process {
        $objectAttributeValues | ForEach-Object {
            $values.Add(@{'value' = $_}) | Out-Null
        }        
        $Attribute = @{
            'objectTypeAttributeId' = $objectTypeAttributeId
            'objectAttributeValues'   = @($values)
            }
    }

    end {
        Write-Output $Attribute
        }
}



function Remove-InsightObject {
    <#

.SYNOPSIS
Resource to delete an object in Insight.

.DESCRIPTION
Resource to delete an object in Insight.

.PARAMETER ID
The object ID. Takes a string of the ID or objectKey

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS
id : 3
label : MyObject
objectKey : ABC-3
avatar : @{url16=/rest/insight/1.0/objecttype/2/icon.png?size=16&uuid=3269b6c6-10cc-41de-88ba-99efae71f889&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppcmEucGx1Z2lucy5pbnNpZ2h0Iiwic3ViIjoiNWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiw
               iaW5zaWdodCI6dHJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiwiaXNzIjoiY29tLnJpYWRhbGFicy5qaXJhLnBsdWdpbnMuaW5zaWdodCIsIm9yaWdpbmFsbHlJc3N1ZWRBdCI6MTYwMDMwNjg5OCwiZXhwIjoxNjAwMzA3MDc4LCJpYXQiOjE2MDAzMDY4OTh9._CaSkX-Q
               vW1BlK7-4XJB9UikOvegJS-YSjCrCtYUl7A; url48=/rest/insight/1.0/objecttype/2/icon.png?size=48&uuid=3269b6c6-10cc-41de-88ba-99efae71f889&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppcmEucGx1Z2lucy5pbnNpZ2h0Iiwic3ViIjoi
               NWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiwiaW5zaWdodCI6dHJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiwiaXNzIjoiY29tLnJpYWRhbGFicy5qaXJhLnBsdWdpbnMuaW5zaWdodCIsIm9yaWdpbmFsbHlJc3N1ZWRBdCI6MTYwMDMwNjg5OCwiZXhwIjoxNjAwMzA3MD
               c4LCJpYXQiOjE2MDAzMDY4OTh9._CaSkX-QvW1BlK7-4XJB9UikOvegJS-YSjCrCtYUl7A; url72=/rest/insight/1.0/objecttype/2/icon.png?size=72&uuid=3269b6c6-10cc-41de-88ba-99efae71f889&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppc
               mEucGx1Z2lucy5pbnNpZ2h0Iiwic3ViIjoiNWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiwiaW5zaWdodCI6dHJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiwiaXNzIjoiY29tLnJpYWRhbGFicy5qaXJhLnBsdWdpbnMuaW5zaWdodCIsIm9yaWdpbmFsbHlJc3N1ZWRBdCI
               6MTYwMDMwNjg5OCwiZXhwIjoxNjAwMzA3MDc4LCJpYXQiOjE2MDAzMDY4OTh9._CaSkX-QvW1BlK7-4XJB9UikOvegJS-YSjCrCtYUl7A; url144=/rest/insight/1.0/objecttype/2/icon.png?size=144&uuid=3269b6c6-10cc-41de-88ba-99efae71f889&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1N
               iJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppcmEucGx1Z2lucy5pbnNpZ2h0Iiwic3ViIjoiNWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiwiaW5zaWdodCI6dHJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiwiaXNzIjoiY29tLnJpYWRhbGFicy5qaXJhLnBsdWdpbnMuaW
               5zaWdodCIsIm9yaWdpbmFsbHlJc3N1ZWRBdCI6MTYwMDMwNjg5OCwiZXhwIjoxNjAwMzA3MDc4LCJpYXQiOjE2MDAzMDY4OTh9._CaSkX-QvW1BlK7-4XJB9UikOvegJS-YSjCrCtYUl7A; url288=/rest/insight/1.0/objecttype/2/icon.png?size=288&uuid=3269b6c6-10cc-41de-88ba-99efae71f889&
               jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjb20ucmlhZGFsYWJzLmppcmEucGx1Z2lucy5pbnNpZ2h0Iiwic3ViIjoiNWVkZjBhNDNlMzFmNjIwYWJhNjYyZjAyIiwiaW5zaWdodCI6dHJ1ZSwiY2xpZW50S2V5IjoiN2VmZmExZGQtYzNiMS0zMjQ4LWFjZDUtNjdjNDcxZWFkOGQzIiwiaXNzIjoiY
               29tLnJpYWRhbGFicy5qaXJhLnBsdWdpbnMuaW5zaWdodCIsIm9yaWdpbmFsbHlJc3N1ZWRBdCI6MTYwMDMwNjg5OCwiZXhwIjoxNjAwMzA3MDc4LCJpYXQiOjE2MDAzMDY4OTh9._CaSkX-QvW1BlK7-4XJB9UikOvegJS-YSjCrCtYUl7A; objectId=3}
objectType : @{id=2; name=My Object Type; type=0; description=A Sample Object Type; icon=; position=0; created=2020-09-16T07:14:02.118Z; updated=2020-09-16T07:14:02.118Z; objectCount=0; objectSchemaId=3; inherited=False; abstractObjectType=False;
               parentObjectTypeInherited=False}
created : 2020-09-17T01:11:02.596Z
updated : 2020-09-17T01:11:02.596Z
hasAvatar : False
timestamp : 1600305062596
attributes : {@{id=9; objectTypeAttribute=; objectTypeAttributeId=7; objectAttributeValues=System.Object[]; objectId=3}, @{id=12; objectTypeAttribute=; objectTypeAttributeId=8; objectAttributeValues=System.Object[]; objectId=3}, @{id=10;
               objectTypeAttribute=; objectTypeAttributeId=9; objectAttributeValues=System.Object[]; objectId=3}, @{id=11; objectTypeAttribute=; objectTypeAttributeId=10; objectAttributeValues=System.Object[]; objectId=3}...}
extendedInfo : @{openIssuesExists=False; attachmentsExists=False}
_links : @{self=/secure/ShowObject.jspa?id=3}
name : MyObject

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Objects

.EXAMPLE
Remove-InsightObject -ID "3" -InsightApiKey $InsightApiKey
Remove-InsightObject -ID "ABC-3" -InsightApiKey $InsightApiKey

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string]$ID,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/object/$($ID)"

    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Method DELETE
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        }
        
        Write-Output $response
        }
}

function Set-InsightObject {
    <#

.SYNOPSIS
Resource to update an object in Insight.

.DESCRIPTION
Resource to update an object in Insight.

.PARAMETER ID
The ID

.PARAMETER Attributes
An array of attributes. colelcted from New-InsightObjectAttributes

.PARAMETER InsightApiKey
The Api key.

.OUTPUTS

.LINK
https://documentation.mindville.com/display/INSCLOUD/REST+API+-+Object+schema

.EXAMPLE

#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [int]$ID,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $false)]
        [int]$objectTypeId,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [array]$Attributes,

        [Parameter(Mandatory = $false)]
        [switch]$ShowJSON,

        [ValidateNotNullOrEmpty()]
        [Alias('ApiKey')]
        [string]$InsightApiKey = $InsightApiKey
    )
    
    begin {
        #Generate Headers
        $headers = New-InsightHeaders -InsightApiKey $InsightApiKey
    }
    
    process {
        $Request = [System.UriBuilder]"https://insight-api.riada.io/rest/insight/1.0/object/$($ID)"

        if ($objectTypeId) {
            $RequestBody = @{
                'objectTypeId' = $objectTypeId
                'attributes'   = @($attributes)
                }
        }
        else {
            $RequestBody = @{
                'attributes'   = @($attributes)
                }
        }
        
            $RequestBody = ConvertTo-Json $RequestBody -Depth 20

            if ($ShowJSON -eq $true) {
                Write-Output $RequestBody
                break
            }
    }
    
    end {
        try {
            $response = Invoke-RestMethod -Uri $Request.Uri -Headers $headers -Body $RequestBody -Method PUT
        }
        catch {
            Write-Error -Message "$($_.Exception.Message)" -ErrorId $_.Exception.Code -Category InvalidOperation
        }
        
        Write-Output $response
        }
}
#endregion