WorkflowAndDocuments.psm1


class Server : System.Management.Automation.IValidateSetValuesGenerator {
    [String[]] GetValidValues() {
        $servers = (Get-Content -Path $env:ServerList | ConvertFrom-Json).ServerList
        return $servers
    }
}
class Query : System.Management.Automation.IValidateSetValuesGenerator {
    [String[]] GetValidValues() {
        $query = (Get-Content -Path $env:ServerList | ConvertFrom-Json).MyQueries
        return $query
    }
}

class Port: System.Management.Automation.IValidateSetValuesGenerator {
    [String[]] GetValidValues() {
        $query = (Get-Content -Path $env:ServerList | ConvertFrom-Json).PortList
        return $query
    }
}
<#
.SYNOPSIS
    Call Windchill RV&S Documents
.DESCRIPTION
    Show-Document is using Windchill RV&S Command im viewsegment --hostname host --port port --user userID --query query
    by using Mksapiviewer.exe --iplocal --xml
.NOTES
    Command is available only over Powershell 7.0 Version
.EXAMPLE
    show-Document -Item 8935655 09:46:17
 
    Section :
    Category :
    Reference Mode :
    ID : 893325655
    Document ID : 855935655
 
    Section : 1
    Category : Heading
    Reference Mode : Author
    ID : 893565623
    Document ID : 89335655
 
    Section : 1.1
    Category : Heading
    Reference Mode : Author
    ID : 893571912
    Document ID : 8931565513
 
    Section : 1.1.1
    Category : Information
    Reference Mode : Author
    ID : 8935849123
    Document ID : 8935655213
 
    Section : 1.2
    Category : Heading
    Reference Mode : Author
    ID : 89315720
    Document ID : 893565523
.EXAMPLE
    show-Document -Item 8935655 -fields id,project,component 09:51:16
 
    ID Project Component
    -- ------- ---------
    8935655 /Windchill/Project System (ID:1234567)
    8935656 /Windchill/Project
    8935719 /Windchill/Project
    8935849 /Windchill/Project
    8935720 /Windchill/Project
#>

function Show-PTCDocument {
    [CmdletBinding()]
    param(
      [Parameter(Mandatory=$true, Position=0)]
      [ValidateNotNullOrEmpty()]
      [int]$Item,

      [Parameter()]
      [string[]]$Fields,

      [Parameter()]
      [ValidateSet([Server])]
      [String]$Server = $env:PTCSERVER,

      [Parameter()]
      [ValidateSet([Port])]
      [int]$port = $env:PTCPORT
    )
    $cred = Use-Credential -IntegrityAdministrator -asPlainText
    $param = '--fields='
    $customeFields = $null -eq $Fields ? (Out-Null) : [string]::Concat($param,$($fields -join ","))

        Write-Debug "Fields:$($customeFields)"

    $job = Start-Job {
        mksAPIViewer.exe --iplocal --xml im viewsegment $($args[0]) --user=$($args[1]) --password=$($args[2]) --hostname=$($args[3]) --port=$($args[4]) $args[5]
    } -ArgumentList $customeFields, $($cred.Username), $($cred.Password), $Server, $port, $Item

    while($job.state -eq "Running"){ $complet = Get-Random -Minimum 1 -Maximum 99 ; Write-Progress -Activity "Building Document: $($Item)" -PercentComplete $complet}
    $info = Receive-Job -Job $job
    $final = Convert-PTCCommand $info

    switch ($final.Response.ExitCode) {
        { $_ -ne 0 } {
            Write-PTCError $final
            ; break
        }
        0 {
            $final.Response.Workitems.WorkItem | ForEach-Object { ConvertFrom-PTCXML -entry $_ }
            ; break
        }
    }
}
<#
.SYNOPSIS
    A short one-line action-based description, e.g. 'Tests if a function is valid'
.DESCRIPTION
    A longer description of the function, its purpose, common use cases, etc.
.NOTES
    Command is available only over Powershell 7.0 Version
.LINK
    Specify a URI to a help page, this will show when Get-Help -Online is used.
.EXAMPLE
    Test-MyTestFunction -Verbose
    Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
#>

function Show-PTCItemHistory {

    [CmdletBinding()]
    param (
    [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
    [ValidateNotNullOrEmpty()]
    [string]$Item,

    [Parameter()]
    [ValidateSet([Server])]
    [String]$Server = $env:PTCSERVER,

    [Parameter()]
    [ValidateSet([Port])]
    [int]$port = $env:PTCPORT
    )

    begin {
        $cred = Use-Credential -IntegrityAdministrator -asPlainText
    }

    process {

        Write-Debug "Current Item:$($Item)"

        $info = mksAPIViewer.exe --iplocal --xml  im viewissue --hostname $server --port $port --user $($cred.username) --password $($cred.password) --showhistory $Item
        Write-Information "Historic Entry of Item $($item)"
            $final = Convert-PTCCommand $info

            switch ($final.Response.ExitCode) {
                { $_ -ne 0 } {
                    Write-PTCError $final
                    ; break
                }
                0 { $history = $final.Response.WorkItems.WorkItem.Field
                    | Where-Object  {$_.name -eq 'MKSIssueHistory'}
                    | Select-Object -ExpandProperty List
                    | Select-Object -ExpandProperty Item
                    ; break
                 }
                 Default {"Unknown Problem"}
            }
        $history | ForEach-Object { ConvertFrom-PTCXML -entry $_ }
    }
}

<#
.SYNOPSIS
    A short one-line action-based description, e.g. 'Tests if a function is valid'
.DESCRIPTION
    A longer description of the function, its purpose, common use cases, etc.
.NOTES
    Command is available only over Powershell 7.0 Version
.LINK
    Specify a URI to a help page, this will show when Get-Help -Online is used.
.EXAMPLE
    Test-MyTestFunction -Verbose
    Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
#>

function Show-PtcContent {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [int]$issueID,

        [Parameter()]
        [string[]]$fields,

        [Parameter()]
        [ArgumentCompleter({get-date -Format "\'MMM dd, yyyy h:mm:ss tt\'"})]
        [DateTime]$asOf,

        [Parameter()]
        [ValidateSet([Server])]
        [string]$Server = $env:PTCSERVER,

        [Parameter()]
        [ValidateSet([Port])]
        [int]$Port = $env:PTCPORT
    )

    begin {
      $cred = Use-Credential -IntegrityAdministrator -asPlainText
      $fieldString = $fields -join ","

        Write-Debug "Fields:$fieldString"
        Write-Debug "Date asOf:$asof"
    }

    process {
      $finalObject = [PSCustomObject]@{}

        $content =  switch ($true) {
          { $null -eq $asOf -and $null -eq $fields } {
            mksAPIViewer.exe --iplocal --xml im issues --user $($cred.username) --password $($cred.password) --hostname  $Server --port $Port $issueID
            ; break
          }
          { $null -ne $fields -and $null -ne $asOf } {
            $date = $asOf | Get-Date -Format "MMM dd, yyyy h:mm:ss tt"
            mksAPIViewer.exe --iplocal --xml im issues --user $($cred.username) --password $($cred.password) --asOf $date --fields $fieldString --hostname  $Server --port $Port $issueID
            ; break
          }
          { $null -ne $asOf} {
            $date = $asOf | Get-Date -Format "MMM dd, yyyy h:mm:ss tt"
            mksAPIViewer.exe --iplocal --xml im issues --user $($cred.username) --password $($cred.password) --asOf $date --hostname  $Server --port $Port $issueID
            ; break
          }
         Default { "Unknown Problem" }
       }

        $final = Convert-PTCCommand $content

        switch ($final.Response.ExitCode) {
            { $_ -ne 0 } {
                Write-PTCError $final
                ; break
            }
            0 {
                $finalObject = $final.REsponse.Workitems.WorkItem | ForEach-Object { ConvertFrom-PTCXML -entry $_ }
                $finalObject | Add-Member -NotePropertyName AsOf -NotePropertyValue  $date
                ; break
            }
        }
      Write-Output $finalObject
    }
    
}
<#
.SYNOPSIS
    Call Windchill RV&S Query
.DESCRIPTION
    Invoke-PtcQuery is ussing Windchill RV&S Command im issues --hostname host --port port --user userID --query query
    by using Mksapiviewer.exe --iplocal --xml
.NOTES
    Command is available only over Powershell 7.0 Version
.EXAMPLE
    Invoke-PtcQuery -query yourQuery -Server Integrity
 
    ID Type State Summary
    -- ---- ----- -------
    12 Project Active Project1
    23 Project Active Project2
    432 Project Cancelled Project3
    2435 Project Cancelled Project4
.EXAMPLE
    You can specify fields which you want to dislpay. By default if -fields is not specify commands return "ID,Type,State,Summary".
 
    Invoke-PtcQuery -query yourQuery -Server Integrity -fields id,'assigned user'
 
    ID Assigned User
    -- -------------
    12 colleagueID1
    23 colleagueID2
    432 colleagueID3
    2435 colleagueID3
 
#>

function Invoke-PtcQuery {
    param (
        [Parameter(Mandatory=$true)]
        [ValidateSet([Query])]
        [string]$query,

        [Parameter()]
        [string[]]$fields,

        [Parameter()]
        [ValidateSet([Server])]
        [String]$Server = $env:PTCSERVER,

        [Parameter()]
        [ValidateSet([Port])]
        [int]$port = $env:PTCPORT,

        [Parameter()]
        [switch]$asAdmin
    )

    $cred = $asAdmin.IsPresent ? (Use-Credential -IntegrityAdministrator -asPlainText) : (Use-Credential -IntegrityProduction -asPlainText)

    $fieldString = $fields -join ","

        Write-Debug "Server:$($server)"
        Write-Debug "Port:$($port)"
        Write-Debug "Fields:$($fieldString)"

    $content = switch ($true) {
        {$null -eq $fields} {
            mksAPIViewer.exe --iplocal --xml  im issues --user $($cred.username) --password $($cred.password) --hostname $Server --port $Port --query=$query
            ; break
        }
        { $null -ne $fields } {
            mksAPIViewer.exe --iplocal --xml  im issues --user $($cred.username) --password $($cred.password) --fields $fieldString --hostname  $Server --port $Port --query=$query
            ; break
        }
        Default { "Unknown Problem" }
    }
    $final = Convert-PTCCommand $content
    switch ($final.Response.ExitCode) {
        { $_ -ne 0 } {
            Write-PTCError $final
            ; break
        }
        0 {
            $final.REsponse.Workitems.WorkItem | ForEach-Object { ConvertFrom-PTCXML -entry $_ }
            ; break
        }
    }
}
<#
.SYNOPSIS
    A short one-line action-based description, e.g. 'Tests if a function is valid'
.DESCRIPTION
    A longer description of the function, its purpose, common use cases, etc.
.NOTES
    Command is available only over Powershell 7.0 Version
.LINK
    Specify a URI to a help page, this will show when Get-Help -Online is used.
.EXAMPLE
    Test-MyTestFunction -Verbose
    Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
#>

function Get-PTCItem {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, Position=0,ValueFromPipeline=$true)]
        [ValidateNotNullOrEmpty()]
        [int]$Item,

        [Parameter()]
        [ValidateSet([Server])]
        [String]$Server = $env:PTCSERVER,

        [Parameter()]
        [ValidateSet([Port])]
        [int]$port = $env:PTCPORT

    )
    process{
        $cred = Use-Credential -IntegrityAdministrator -asPlainText
        $ItemObj = [PSCustomObject]@{}
        $ItemView = mksAPIViewer --iplocal --xml im viewissue --showDecorators --user=$($cred.UserName) --password=$($cred.Password) --hostname=$server --port=$port $Item
        $Itemraw = $ItemView.replace('<?xml version="1.1" ?>','<?xml version="1.0" ?>')
        [xml]$Itemxml = $Itemraw

        switch ($Itemxml.Response.ExitCode) {
            {$_ -eq 0} {
                $Itemxml.Response.WorkItems.WorkItem.Field  | ForEach-Object {
                $propertyValue = ($null -eq $_.Item.ID -or $_.Item.ID -eq "" ) ? ($_.Value.InnerText) : $_.Item.ID
                if($null -ne $_.List.item.displayId){
                    $propertyValue = $_.List.item.displayId
                }
                $ItemObj | Add-Member -MemberType NoteProperty -Name $($_.Name) -Value $($propertyValue)
                }
            return $ItemObj
            }
          Default { Write-Error -Message $ItemView.Response.Exception.Message}
        }
    }
}
<#
.SYNOPSIS
    A short one-line action-based description, e.g. 'Tests if a function is valid'
.DESCRIPTION
    A longer description of the function, its purpose, common use cases, etc.
.NOTES
    Command is available only over Powershell 7.0 Version
.LINK
    Specify a URI to a help page, this will show when Get-Help -Online is used.
.EXAMPLE
    Test-MyTestFunction -Verbose
    Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
#>

function Use-Credential {
    param (
        [Parameter(ParameterSetName = "WindowsAdministrator")]
        [switch]$WindowsAdministrator,

        [Parameter(ParameterSetName = "IntegrityAdministrator")]
        [switch]$IntegrityAdministrator,

        [Parameter(ParameterSetName = "IntegrityProduction")]
        [switch]$IntegrityProduction,

        [Parameter()]
        [switch]$asPlainText
    )

    $secretVoult = Join-Path -Path $home -ChildPath secretVault.json

        Write-Debug "Voult:$($secretVoult)"

    $credentials = Switch ((Test-Path -Path $secretVoult) -eq $false ) {
        $true {
            Get-Credential -Message "please Enter Credentials"
            ; break
        }
        $False {
            switch ($true) {
                {
                    $WindowsAdministrator.IsPresent
                } {
                    $credentials = Get-Content -Path $secretVoult
                    | ConvertFrom-Json | Select-Object -ExpandProperty WindowsAdministrator
                    | Select-Object UserName, @{name = "Password" ; Expression = { Convertto-SecureString $_.Password } }
                    New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $credentials.UserName, $credentials.Password
                    ; break
                }
                {
                    $IntegrityAdministrator.IsPresent
                } {
                    Get-Content -Path $secretVoult
                    | ConvertFrom-Json | Select-Object -ExpandProperty IntegrityAdministrator
                    | Select-Object UserName, @{name = "Password"; Expression = { Convertto-SecureString $_.Password } }
                    ; break
                }
                {
                    $integrityProduction.IsPresent
                } {
                    Get-Content -Path $secretVoult | ConvertFrom-Json | Select-Object -ExpandProperty IntegrityProduction
                    | Select-Object UserName, @{name = "Password"; Expression = { Convertto-SecureString $_.Password } }
                    ; break
                }
                Default { "Unknown1 Problem" }
            }
        }
        Default { "Unknown2 Problem" }
    }
    return $asPlainText.IsPresent ? ($credentials | Select-Object UserName, @{Name = "Password"; Expression = { ConvertFrom-SecureString $_.Password -AsPlainText } }) : $credentials
}
<#
.SYNOPSIS
    Register-SecretVault stores user ID and password in encrypted Voult
.DESCRIPTION
    Register-SecretVault Allows to store your user ID and password in encrypted Voult. It's optional to use.
    WorkfloAndDocuments commands without voult will alywas ask for your user ID and password.
.NOTES
    Command is available only over Powershell 7.0 Version
#>

function Register-PTCSecretVault {

    $info = Get-Credential -Message "Integrity Production"
    $production = $info | Select-Object UserName,@{name="Password";Expression={ ConvertFrom-SecureString $_.Password }}

    $info = Get-Credential -Message "Integrity Administrator"
    $Administration = $info | Select-Object UserName,@{name="Password";Expression={ ConvertFrom-SecureString $_.Password }}

    $info = Get-Credential -Message "Windows Administrator"
    $Windows = $info | Select-Object UserName,@{name="Password";Expression={ ConvertFrom-SecureString $_.Password}}

    $voult = @{
        "IntegrityProduction"=$production
        "IntegrityAdministrator"=$Administration
        "WindowsAdministrator"=$Windows
    }
    $voult | ConvertTo-Json | Out-File -FilePath $home\secretVault.json

    Write-Output "Voult registered under $($home)\secretVault.json"
}
# /////////////////////////////// Help Functions ////////////////////////////

<#
.SYNOPSIS
    Help-function to tranform XML retrived from windchill command into pscustomeobject
.DESCRIPTION
    Output of any Windchill RV&S command by mksapiviewer.exe
.NOTES
    Command is available only over Powershell 7.0 Version
.INPUTS
 System.Xml.XmlElement
.OUTPUTS
 PScustomObject
.EXAMPLE
    Test-MyTestFunction -Verbose
    Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
#>

function ConvertFrom-PTCXML {
    param(
        [Parameter()]
        [System.Xml.XmlElement]$entry
    )
    $EntryObject = [PSCustomObject]@{}
    $entry.Field | ForEach-Object {

        switch ($_) {
            { $_.psobject.Properties.name -contains "List"} {

                if($null -eq $_.List.Item.ID) {
                    $EntryObject | Add-Member -NotePropertyName $_.name -NotePropertyValue $_.List.value.innertext
                } else {
                    $EntryObject | Add-Member -NotePropertyName $_.name -NotePropertyValue $_.List.Item.displayID
                }
                ;break
            }
            { $_.psobject.Properties.name  -notcontains "List" } {
                if($_.InnerText -eq '') {

                    if($null -eq $_.List.Item.ID) {
                        $EntryObject | Add-Member -NotePropertyName $_.name -NotePropertyValue $_.Item.displayID
                    } else {
                        $EntryObject | Add-Member -NotePropertyName $_.name -NotePropertyValue $_.List.Item.displayID
                    }

                } else {

                    if($_.Item -is [System.Xml.XmlLinkedNode]) {
                        $EntryObject | Add-Member -NotePropertyName $_.name -NotePropertyValue $_.item.displayId
                    } else {
                        $EntryObject | Add-Member -NotePropertyName $_.name -NotePropertyValue $_.innertext
                    }
                }
                ;break
            }
        }
    }
    Write-Output $EntryObject
}
<#
.SYNOPSIS
    Register-PTCDefaultServerInfo allows you to save in server.Json file your default Server hostname,port,queries
.DESCRIPTION
    This Function is required to run as admin prior to use any other Command from WorkflowandDocuments module to save your hostname, port and optional queries for command Invoke-PTCquery
.NOTES
    Command is available only over Powershell 7.0 Version
#>

function Register-PTCDefaultServerInfo {

    switch ([bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match "S-1-5-32-544")) {

        $true {
            $server = Read-Host -Prompt "Please Enter Default Windchill RV&S Server"

            $port = Read-Host -Prompt "Please Enter Default Windchill RV&S Port"

            $allServers = Read-Host -Prompt "Enter List of Servers you would like to use. Server1,Server2"

            $allports = Read-Host -Prompt "Enter List of ports you would like to use. 1,2,3"

            if(Test-Path -Path $HOME\server.json) {
                [System.Environment]::SetEnvironmentVariable("ServerList","$home\server.json",'Machine')
                [System.Environment]::SetEnvironmentVariable("PTCSERVER",$server,'Machine')
                [System.Environment]::SetEnvironmentVariable("PTCPORT",$port,'Machine')
                $DefaultFile = Get-Content -Path $HOME\server.json | ConvertFrom-Json
                $DefaultFile.DefaultServer = $server
                $DefaultFile.DefaultPort = $port
                $DefaultFile.ServerList += $allServers -split ","
                $DefaultFile.ServerList += $server
                $DefaultFile.PortList += $allports
                $DefaultFile.PortList += $port
                $DefaultFile | ConvertTo-Json | Out-File -FilePath $HOME\server.json
                Write-Output "Your Server.Json has been updated"
                Get-Content -Path $HOME\server.json
            } else {
                [System.Environment]::SetEnvironmentVariable("ServerList","$home\server.json",'Machine')
                [System.Environment]::SetEnvironmentVariable("PTCSERVER",$server,'Machine')
                [System.Environment]::SetEnvironmentVariable("PTCPORT",$port,'Machine')
                New-Item -Path $HOME\ -Name server.json -ItemType File
                $DefaultFile = [PSCustomObject]@{
                DefaultServer = $server
                DefaultPort = $port
                ServerList = $allServers -split ","
                PortList = $allports -split ","
                MyQueries = @()
                }
                $DefaultFile.ServerList += $server
                $DefaultFile.PortList += $port
                $DefaultFile | ConvertTo-Json | Out-File -FilePath $HOME\server.json
                Write-Output "Your Server.Json has been created"
                Get-Content -Path $HOME\server.json
            }
            ; break
        }

        $false {  Write-Output "Please Start Powershell as local Admin to set Enviroment Varables required in this module"
            ; break
        }
        Default {"Unknown Problem"}
    }
}
<#
.SYNOPSIS
    Small help fucntion to transform Windchill RV&S command into XML
.DESCRIPTION
    A longer description of the function, its purpose, common use cases, etc.
.NOTES
    Command is available only over Powershell 7.0 Version
.INPUTS
    System.Array
.OUTPUTS
    XmlDocument
.LINK
    Specify a URI to a help page, this will show when Get-Help -Online is used.
.EXAMPLE
    $info = mksAPIViewer.exe --iplocal --xml im viewissue --hostname hostname --port port --user userID --showhistory IssueID
    $final = convert-PTCCommand $info
    $final
 
    xml Response
    --- --------
    version="1.0" Response
#>

function Convert-PTCCommand {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, Position=0)]
        [ValidateNotNullOrEmpty()]
        [array]$Command
    )
      $info = $Command.replace('<?xml version="1.1" ?>','<?xml version="1.0" ?>')
      return [xml]$info = $info
}
<#
.SYNOPSIS
    Writes Error from Windchill RV&S command
.DESCRIPTION
    Writes Error transforms Windchill RV&S xml into pscustomobject with message & exit code
.NOTES
    Command is available only over Powershell 7.0 Version
.EXAMPLE
    $info = mksapiviewer.exe --iplocal --xml im queriesx --user userID
    $final = convert-PTCCommand $info
    Write-PTCError $final
 
    Write-PTCError: @{Message=im: Invalid command: queriesx; ErrorType=UnknownCommandException; ExitCode=1}
#>

function Write-PTCError {
    [CmdletBinding()]
    param (
        [xml]$Final
    )
    $errorObject = [PSCustomObject]@{}
    $errorObject | Add-Member -MemberType NoteProperty -name Message -Value ($Final.Response.WorkItems.WorkItem.Exception.Message ?? $Final.Response.Exception.Message)
    $errorObject | Add-Member -MemberType NoteProperty -name ErrorType -Value ($Final.Response.WorkItems.WorkItem.Exception.class ?? $Final.Response.Exception.Class)
    $errorObject | Add-Member -MemberType NoteProperty -name ExitCode -Value $Final.Response.ExitCode

    Write-error $errorObject
}

Export-ModuleMember -Function ConvertFrom-PtcXML,Invoke-PtcQuery,Show-PtcDocument,Show-PtcContent,Show-PtcItemHistory,Register-PtcDefaultServerInfo,Convert-PtcCommand,Use-Credential,
Get-PtcItem,Register-PtcSecretVault,Write-PTCError