Public/Projects.ps1


## Projects

<#
.SYNOPSIS
    Gets the projects from a TM instance.
 
.DESCRIPTION
    The `Get-TMProject` function gets projects from a TM instance specified in a TMSession object.
 
    By default it calls the REST endpoint of a TM server.
    A value of "$false" can be passed to the -UseAPI parameter to call the Web Service endpoint instead.
 
.PARAMETER TMSession
    Specifies the name of the TMSession whose projects will be fetched.
 
.PARAMETER CurrentProject
    Gets the TMSession.UserContext.Project property of a TMSession object and returns a corresponding TMProject object.
 
.PARAMETER UseAPI
    If a value of $false is passed to this parameter, the function uses web service calls to get the project data.
 
    NOTE:Currently, the REST Endpoint and the Web Services endpoint return different set of properties.
        For more information check the TMProject class source code.
 
.PARAMETER Project
Gets a project where the Project Name or Project Code matches the specified value.
 
.PARAMETER ResetIDs
 
.NOTES
    An existing TMSession needs to be created before Get-TMProject can be used.
 
.INPUTS
    You can pipe a TMSession object or a string with the name of an existing TMSession.
 
.OUTPUTS
    [TMProject]
        This function outputs a TMProject class object.
 
.EXAMPLE
    Get the current project, as specified in the TMSession.UserContext.Project property of the TMSession
    Get-TM-Project -Current
 
.EXAMPLE
    Get a list of projects from a TMSession named 'Default'
    Get-TMProject -TMSession 'Default'
 
.EXAMPLE
    Get a list of projects from a TMSession named 'Default', using web service calls instead of a REST endpoint.
    Get-TMProject -TMSession 'Default'
#>

Function Get-TMProject {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)][PSObject]$TMSession = 'Default',
        [Alias("Current")][Parameter(Mandatory = $false)][Switch]$CurrentProject,
        [Parameter(Mandatory = $false)][Bool]$UseAPI = $true,
        [Alias("Name", "ProjectCode", "ProjectName")][Parameter(Mandatory = $false, Position = 0)][String]$Project,
        [Parameter(Mandatory = $false)][Switch]$ResetIDs

    )
    begin {
        $PSBoundParameters.Remove("UseWebServices") | Out-Null
    }
    process {
        try {
            if ($UseAPI) {
                Get-TMProjectRestApi @PSBoundParameters
            } else {
                Get-TMProjectWebService @PSBoundParameters
            }
        } catch {
            throw $_
        }

    }
    end {
    }
}
Function Enter-TMProject {
    param(
        [Parameter(Mandatory = $false)][PSObject]$TMSession = 'Default',
        [Parameter(Mandatory = $false, ParameterSetName = "ByProjectId", Position = 0)][Int]$ProjectID,
        [Alias("Project")][Parameter(
            Mandatory = $false,
            HelpMessage = 'Enter the Project Name or Project Code',
            ParameterSetName = "ByProjectNameOrCode",
            Position = 0)][String]$ProjectNameOrCode
    )

    ## Get Session Configuration
    $TMSession = Get-TMSession $TMSession
    if (-not $TMSession) {
        Write-Host 'TMSession: [' -NoNewline
        Write-Host $TMSession -ForegroundColor Cyan
        Write-Host '] was not Found. Please use the New-TMSession command.'
        Throw "TM Session Not Found. Use New-TMSession command before using features."
    }

    #Honor SSL Settings
    $TMCertSettings = @{SkipCertificateCheck = $TMSession.AllowInsecureSSL }

    ## If either the Project ID or Project Code were known, they could be used. However, support for changing based just on the project name is possible
    if (-not $ProjectID) {
        ## Get the Project by Name or Code
        $Project = Get-TMProject -Project $ProjectNameOrCode -TMSession $TMSession
        $ProjectID = $Project.id
        if (!$ProjectID) {
            throw "Project [" + $ProjectNameOrCode + "] does not exist. Please create it and run the script again."
        }
    }

    ## 4.4, 4.5, 4.6
    if ($TMSession.TMVersion -in @('4.4.3', '4.5.9' , '4.6.3')  ) {
        $uri = "https://"
        $uri += $TMSession.TMServer
        $uri += '/tdstm/project/addUserPreference/' + $ProjectID
    }

    ## 4.7.0+
    else {
        $uri = "https://"
        $uri += $TMSession.TMServer
        $uri += '/tdstm/ws/project/viewEditProject/' + $ProjectID
    }

    try {
        $response = Invoke-WebRequest -Method Get -Uri $uri -WebSession $TMSession.TMWebSession @TMCertSettings
    } catch {
        return $_
    }

    if ($response.StatusCode -in @(200, 204)) {
        $newProject = ($Response.Content | ConvertFrom-Json).data.projectInstance
        $TMSession.UserContext.project.id = $newProject.id
        $TMSession.UserContext.project.name = $newProject.name

        ## The code property has not been consistent in builds between 4.7 -> 6.0-alpha. Simply force it
        Add-Member -InputObject $TMSession.UserContext.Project -NotePropertyName 'code' -NotePropertyValue $newProject.projectCode -Force

        if ($VerbosePreference -eq 'Continue') {
            Write-Host 'Project has been changed to: ' -NoNewline
            Write-Host $ProjectName -ForegroundColor Cyan
        }
    } else {
        throw "Unable to Set Projects."
    }
}

Function New-TMProject {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,

        [Parameter(Mandatory = $true)]
        [String]$Company,

        [Parameter(Mandatory = $false)]
        [ValidateLength(1, 20)]
        [String]$Code,

        [Parameter(Mandatory = $false)]
        [PSObject]$TMSession = "Default",

        [Parameter(Mandatory = $false)]
        [DateTime]$StartDate,

        [Parameter(Mandatory = $false)]
        [DateTime]$EndDate,

        [Parameter(Mandatory = $false)]
        [Switch]$Passthru

    )

    ## Get Session Configuration
    $TMSession = Get-TMSession $TMSession
    if (-not $TMSession) {
        Write-Host 'TMSession: [' -NoNewline
        Write-Host $TMSession -ForegroundColor Cyan
        Write-Host '] was not Found. Please use the New-TMSession command.'
        throw "TM Session Not Found. Use New-TMSession command before using features."
    }

    #Honor SSL Settings
    $TMCertSettings = @{ SkipCertificateCheck = $TMSession.AllowInsecureSSL }

    # Check if the project already exists
    $ExistingProject = Get-TMProject -Name $Name -TMSession $TMSession
    if ($ExistingProject) {
        if ($Passthru) {
            return $ExistingProject
        } else {
            return
        }
    }

    # Make sure the Provider exists
    $Client = Get-TMCompany -Name $Company -TMSession $TMSession
    if (!$Client) {
        throw "The company '$Company' does not exist."
    }


    $instance = $TMSession.TMServer.Replace('/tdstm', '').Replace('https://', '').Replace('http://', '')
    $uri = "https://$instance/tdstm/ws/project/saveProject/"

    Set-TMHeaderContentType -ContentType 'JSON' -TMSession $TMSession

    if (!$StartDate) {
        $StartDate = (Get-Date -AsUTC).ToString('yyyy-MM-ddTHH:mm:ssZ')
    }

    if (!$EndDate) {
        $EndDate = (Get-Date -AsUTC).AddDays(365).ToString('yyyy-MM-ddTHH:mm:ssZ')
    }

    if (!$Code) {
        $FormattedCode = $Name.Trim().Replace(' - ', '-').Replace('- ', '-').Replace(' ', '-')
        $Code = $FormattedCode.Length -gt 20 ?  $FormattedCode.Substring(0, 20) : $FormattedCode
    }

    # Format the body of the request
    $Project = [PSCustomObject]@{
        clientId          = $Client.id
        startDate         = $StartDate
        completionDate    = $EndDate
        projectCode       = $Code
        defaultBundleName = "TBD"
        description       = ""
        collectMetrics    = $true
        timeZone          = "GMT"
        projectManagerId  = 0
        comment           = ""
        projectType       = "Standard"
    }

    # Format the name property based on the TM version
    $NameKey = $TMSession.TMVersion -like '4.*' ? 'projectName' : 'name'
    $Project | Add-Member -Name $NameKey -Value $Name -MemberType NoteProperty

    $PostJson = $Project | ConvertTo-Json

    try {
        $response = Invoke-WebRequest -Method Post -Uri $uri -WebSession $TMSession.TMWebSession -Body $PostJson @TMCertSettings
    } catch {
        return $_
    }

    if ($response.StatusCode -in @(200, 204)) {
        if ($Passthru) {
            return (Get-TMProject -Name $Name -TMSession $TMSession)
        }
    } else {
        throw "Unable to create Project."
    }
}

function Get-TMProjectWebService {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)][PSObject]$TMSession = 'Default',
        [Alias("Name", "ProjectCode", "ProjectName")][Parameter(Mandatory = $false, Position = 0)][String]$Project,
        [Parameter(Mandatory = $false)][Switch]$ResetIDs
    )

    begin {
        ## Get Session Configuration
        $TMSession = Get-TMSession $TMSession
        if (-not $TMSession) {
            Write-Host 'TMSession: [' -NoNewline
            Write-Host $TMSession -ForegroundColor Cyan
            Write-Host '] was not Found. Please use the New-TMSession command.'
            Throw "TM Session Not Found. Use New-TMSession command before using features."
        }
        #Honor SSL Settings
        $TMCertSettings = @{SkipCertificateCheck = $TMSession.AllowInsecureSSL }
        # Format the uri
        $instance = $instance = $TMSession.TMServer.Replace('/tdstm', '').Replace('https://', '').Replace('http://', '')
        $uri = "https://$instance/tdstm/ws/projects"

    }

    process {

        try {
            $response = Invoke-WebRequest -Method Get -Uri $uri -WebSession $TMSession.TMWebSession @TMCertSettings
        } catch {
            throw $_
        }

        if ($response.StatusCode -in 200, 204) {
            $Results = ($response.Content | ConvertFrom-Json).data | ForEach-Object { [TMProject]::New($_) }
        } else {
            throw "Unable to collect Projects."
        }

        if ($ResetIDs) {

            ## Version 4.6.3 is legacy and has a different data structure. All forward versions are the same
            if ($TMSession.TMVersion -eq '4.6.3') {

                ## Clear All projects (No nesting here)
                for ($i = 0; $i -lt $Results.Count; $i++) {
                    $Results[$i].id = $null
                }

                # 4.7.1+
            } else {

                ## Clear Active Projects
                for ($i = 0; $i -lt $Results.Count; $i++) {
                    $Results[$i].id = $null
                }
            }
        }

        if ($Project) {
            $NameKey = $TMSession.TMVersion -like '4.*' ? 'projectName' : 'name'
            $CodeKey = "projectCode"
            $Results = $Results | Where-Object {
                $_.$NameKey -eq $Project -or $_.$CodeKey -eq $Project
            }
        }

        return $Results
    }

    end {

    }
}
function Get-TMProjectRestApi {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)][PSObject]$TMSession = 'Default',
        [Alias("Current")][Parameter(Mandatory = $false)][Switch]$CurrentProject,
        [Alias("Name", "ProjectCode", "ProjectName")][Parameter(Mandatory = $false, Position = 0)][String]$Project,
        [Parameter(Mandatory = $false)][Switch]$ResetIDs
    )

    begin {
        ## Get Session Configuration
        $TMSession = Get-TMSession $TMSession
        if (-not $TMSession) {
            Write-Host 'TMSession: [' -NoNewline
            Write-Host $TMSession -ForegroundColor Cyan
            Write-Host '] was not Found. Please use the New-TMSession command.'
            Throw "TM Session Not Found. Use New-TMSession command before using features."
        }

        # Format the uri
        $Instance = $TMSession.TMServer

        $RestParams = @{
            SkipCertificateCheck = $TMSession.AllowInsecureSSL
            Headers              = $TMSession.TMRestSession.Headers
            Uri                  = "https://$Instance/tdstm/api/project"
            Method               = "Get"
            StatusCodeVariable   = "ResponseStatusCode"
        }
        if ($CurrentProject) {
            $RestParams.Uri = $RestParams.Uri.Replace('project', "project/$($TMSession.userContext.Project.id)")
        }
    }

    process {

        try {
            $Response = Invoke-RestMethod @RestParams
        } catch {
            throw $_
        }


        if ($ResponseStatusCode -in 200, 204) {
            $Results = $Response | ForEach-Object { [TMProject]::New($_) }
        } else {
            return "Unable to collect Projects."
        }

        if ($ResetIDs) {

            ## Version 4.6.3 is legacy and has a different data structure. All forward versions are the same
            if ($TMSession.TMVersion -eq '4.6.3') {

                ## Clear All projects (No nesting here)
                for ($i = 0; $i -lt $Results.Count; $i++) {
                    $Results[$i].id = $null
                }

                # 4.7.1+
            } else {

                ## Clear Active Projects
                for ($i = 0; $i -lt $Results.Count; $i++) {
                    $Results[$i].id = $null
                }
            }
        }

        if ($Project) {
            $NameKey = $TMSession.TMVersion -like '4.*' ? 'projectName' : 'name'
            $CodeKey = "projectCode"
            $Results = $Results | Where-Object {
                $_.$NameKey -eq $Project -or $_.$CodeKey -eq $Project
            }
        }

        return $Results
    }

    end {

    }
}