Knowledge Base

Preserving for the future: Shell scripts, AoC, and more

jiralib.ps1 Powershell library

# File: jiralib.ps1
# Startdate: 2020-06-23 08:59
# Dependencies:
#    credlib.ps1

. "scripts\Functions\credlib.ps1"


$jiraCloudCred = Get-Shared-Credential -User "bgstack15@example.com" -PasswordCategory "jira-cloud-api-token-bgstack15"
${global:CloudHeaders} = Get-Basic-Base64-Auth-Headers -Credential $jiraCloudCred

Function List-Attachments-On-Issue {
    [CmdletBinding(SupportsShouldProcess)]
    Param(
        [Parameter(Mandatory = $False)][string]$Server = "example.atlassian.net",
        [Parameter(Mandatory = $True) ][string]$Issue,
        [Parameter(Mandatory = $False)][Hashtable]$cloudHeaders = ${global:cloudHeaders}
    )

    Process {
        $result = Invoke-RestMethod -Headers $cloudHeaders -Uri "https://${Server}/rest/api/3/issue/${Issue}" -ContentType "application/json"
        $results = $result.fields.attachment
        $results
    }
}

# Usage: List-Attachments-On-Issue -Issue "PROJ-2" | Download-Attachments -OutDirectory "C:\Output"
Function Download-Attachments {
    [CmdletBinding(SupportsShouldProcess)]
    Param(
        [Parameter(Mandatory = $False,ValueFromPipeLine = $true,ValueFromPipelineByPropertyName = $true)]$Attachments,
        [Parameter(Mandatory = $False) ][string]$OutDirectory = ".",
        [Parameter(Mandatory = $False)][Hashtable]$cloudHeaders = ${global:cloudHeaders},
        [Parameter(Mandatory = $False)][Boolean]$ShowCount = $False
    )
    Begin {
        $count = 0
    }
    Process {
        # for each item from List-Attachments-On-Issue
        $OutFile = "$($OutDirectory)\$($_.filename)" -Replace "\\\\","\"
        # Assumes that directory already exists.
        #Write-Host "Save $($_.content) as file $($OutFile)"
        If ($PsCmdlet.ShouldProcess("$($_.content)")) {
            Invoke-WebRequest -Headers $cloudHeaders -uri $_.content -OutFile $OutFile -Verbose:$false
            $count += 1
        }
    }
    End {
        If ($ShowCount) { Write-Verbose "Saved $count items" }
    }
}

Function Retrieve-Attachments-To-Subdir {
    [CmdletBinding(SupportsShouldProcess)]
    Param(
        [Parameter(Mandatory = $False)][string]$Server = "example.atlassian.net",
        [Parameter(Mandatory = $True) ][string]$Issue,
        [Parameter(Mandatory = $False)][string]$ParentDirectory = ".",
        [Parameter(Mandatory = $False)][Hashtable]$cloudHeaders = ${global:cloudHeaders},
        [Parameter(Mandatory = $False)][switch]$LongForm = $False
    )
    Process {
        $OutDir = "$($ParentDirectory)\$($Issue)" -Replace "\\\\","\"
        If ($LongForm) {
            $Name = ($Issue -Split "-")[0]
            $Number = ($Issue -Split "-")[-1]
            # the "{0:d4}" -f $Number syntax did not work when in a script for some reason.
            # but this does: https://devblogs.microsoft.com/scripting/understanding-powershell-and-basic-string-formatting/
            # using floor https://stackoverflow.com/questions/5863772/powershell-round-down-to-nearest-whole-number/5864061#5864061
            $n5 = [string]::Format("{0:d5}",[int]$Number)
            $n3 = [string]::Format("{0:d5}",[int]([math]::floor([decimal]($n5/100)))*100)
            $n2 = [string]::Format("{0:d5}",[int]([math]::floor([decimal]($n5/1000)))*1000)
            $n1 = [string]::Format("{0:d5}",[int]([math]::floor([decimal]($n5/10000)))*10000)
            $OutDir = "${Name}\${n1}\${n2}\${n3}\${n5}"
        }
        If ($ParentDirectory -ne ".") {
            $Prompt = "Save $($Issue) to $($ParentDirectory)\$($OutDir)"
        } Else {
            $Prompt = "Save $($Issue) to $($OutDir)"
        }
        If ($PsCmdlet.ShouldProcess($Prompt)) {
            If (!(Test-Path "$($ParentDirectory)\$($OutDir)")) { New-Item -ItemType Directory -Path "$($ParentDirectory)\$($OutDir)" | Out-Null ; }
            List-Attachments-On-Issue -Server $Server -Issue $Issue -cloudHeaders $cloudHeaders -Verbose:$False | Download-Attachments -OutDirectory "$($ParentDirectory)\$($OutDir)" -cloudHeaders $cloudHeaders -Confirm:$False -ShowCount $True
            # and now clean up any empty dirs
            # from https://stackoverflow.com/questions/28631419/how-to-recursively-remove-all-empty-folders-in-powershell/28631669#28631669
            Do {
                $dirs = Get-ChildItem $ParentDirectory -Directory -Recurse | ? { (Get-ChildItem $_.fullName -Force).count -eq 0 } | Select -ExpandProperty FullName
                $dirs | Foreach-Object { Remove-Item $_ -Confirm:$false -Verbose:$False }
            } While ($dirs.count -gt 0)
        }
    }
}

# This is the main function for pulling all attachments for tickets listed in a csv in one column, named nothing in particular
# Import-CSV "tickets.csv" | Retrieve-All-Attachments -ParentDirectory "C:\Output"
Function Retrieve-All-Attachments {
    [CmdletBinding(SupportsShouldProcess)]
    Param(
        [Parameter(Mandatory = $False)][string]$Server = "example.atlassian.net",
        [Parameter(Mandatory = $False,ValueFromPipeLine = $true,ValueFromPipelineByPropertyName = $true)]$Issues,
        [Parameter(Mandatory = $False)][string]$ParentDirectory = ".",
        [Parameter(Mandatory = $False)][Hashtable]$cloudHeaders = ${global:cloudHeaders}
    )
    Process {
        # CSV only needs one column, so a plaintext file would do, if Import-Csv works.
        # learn column name
        $ColumnName = ($_ | gm -Type NoteProperty).Name
        $_ | % {
            $Ticket = "$($_.$($ColumnName))"
            #Write-Host "Do something with $($Ticket)"
            #Write-Host "Retrieve-Attachments-To-Subdir -Server $Server -Issue $Ticket -ParentDirectory $ParentDirectory -LongForm -cloudHeaders $cloudHeaders"
            Retrieve-Attachments-To-Subdir -Server $Server -Issue $Ticket -ParentDirectory $ParentDirectory -LongForm -cloudHeaders $cloudHeaders -Verbose
        }
    }
}

I was writing a process to download Jira issue attachments and upload them to Sharepoint in a directory structure.

Comments