diff --git a/JiraPS/Public/Get-JiraIssueAttachment.ps1 b/JiraPS/Public/Get-JiraIssueAttachment.ps1 index cf833b16..8efae370 100644 --- a/JiraPS/Public/Get-JiraIssueAttachment.ps1 +++ b/JiraPS/Public/Get-JiraIssueAttachment.ps1 @@ -59,14 +59,6 @@ function Get-JiraIssueAttachment { ConvertTo-JiraAttachment -InputObject $attachments } - else { - $errorMessage = @{ - Category = "ObjectNotFound" - CategoryActivity = "Searching for resource" - Message = "This issue does not have any attachments" - } - Write-Error @errorMessage - } } end { diff --git a/JiraPS/Public/Get-JiraIssueAttachmentFile.ps1 b/JiraPS/Public/Get-JiraIssueAttachmentFile.ps1 new file mode 100644 index 00000000..4021a752 --- /dev/null +++ b/JiraPS/Public/Get-JiraIssueAttachmentFile.ps1 @@ -0,0 +1,68 @@ +function Get-JiraIssueAttachmentFile { + # .ExternalHelp ..\JiraPS-help.xml + [CmdletBinding()] + [OutputType([Bool])] + param ( + [Parameter( Mandatory, ValueFromPipeline )] + [PSTypeName('JiraPS.Attachment')] + $Attachment, + + [ValidateScript( + { + if (-not (Test-Path $_)) { + $errorItem = [System.Management.Automation.ErrorRecord]::new( + ([System.ArgumentException]"Path not found"), + 'ParameterValue.FileNotFound', + [System.Management.Automation.ErrorCategory]::ObjectNotFound, + $_ + ) + $errorItem.ErrorDetails = "Invalid path '$_'." + $PSCmdlet.ThrowTerminatingError($errorItem) + } + else { + return $true + } + } + )] + [String] + $Path, + + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + begin { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" + } + + process { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] ParameterSetName: $($PsCmdlet.ParameterSetName)" + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] PSBoundParameters: $($PSBoundParameters | Out-String)" + + foreach ($_Attachment in $Attachment) { + if ($Path) { + $filename = Join-Path $Path $_Attachment.Filename + } + else { + $filename = $_Attachment.Filename + } + + $iwParameters = @{ + Uri = $_Attachment.Content + Method = 'Get' + Headers = @{"Accept" = $_Attachment.MediaType} + OutFile = $filename + Credential = $Credential + } + + $result = Invoke-JiraMethod @iwParameters + (-not $result) + } + } + + end { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function ended" + } +} diff --git a/Tests/Functions/Get-JiraIssueAttachmentFile.Unit.Tests.ps1 b/Tests/Functions/Get-JiraIssueAttachmentFile.Unit.Tests.ps1 new file mode 100644 index 00000000..eeb9863f --- /dev/null +++ b/Tests/Functions/Get-JiraIssueAttachmentFile.Unit.Tests.ps1 @@ -0,0 +1,154 @@ +#requires -modules BuildHelpers +#requires -modules @{ ModuleName = "Pester"; ModuleVersion = "4.4.0" } + +Describe "Get-JiraIssueAttachmentFile" -Tag 'Unit' { + + BeforeAll { + Remove-Item -Path Env:\BH* + $projectRoot = (Resolve-Path "$PSScriptRoot/../..").Path + if ($projectRoot -like "*Release") { + $projectRoot = (Resolve-Path "$projectRoot/..").Path + } + + Import-Module BuildHelpers + Set-BuildEnvironment -BuildOutput '$ProjectPath/Release' -Path $projectRoot -ErrorAction SilentlyContinue + + $env:BHManifestToTest = $env:BHPSModuleManifest + $script:isBuild = $PSScriptRoot -like "$env:BHBuildOutput*" + if ($script:isBuild) { + $Pattern = [regex]::Escape($env:BHProjectPath) + + $env:BHBuildModuleManifest = $env:BHPSModuleManifest -replace $Pattern, $env:BHBuildOutput + $env:BHManifestToTest = $env:BHBuildModuleManifest + } + + Import-Module "$env:BHProjectPath/Tools/BuildTools.psm1" + + Remove-Module $env:BHProjectName -ErrorAction SilentlyContinue + Import-Module $env:BHManifestToTest + } + AfterAll { + Remove-Module $env:BHProjectName -ErrorAction SilentlyContinue + Remove-Module BuildHelpers -ErrorAction SilentlyContinue + Remove-Item -Path Env:\BH* + } + + InModuleScope JiraPS { + + . "$PSScriptRoot/../Shared.ps1" + + $jiraServer = 'http://jiraserver.example.com' + $issueID = 41701 + $issueKey = 'IT-3676' + + $attachments = @" +[ + { + "self": "$jiraServer/rest/api/2/attachment/10013", + "id": "10013", + "filename": "foo.pdf", + "author": { + "self": "$jiraServer/rest/api/2/user?username=admin", + "name": "admin", + "key": "admin", + "accountId": "000000:000000-0000-0000-0000-ab899c878d00", + "emailAddress": "admin@example.com", + "avatarUrls": { }, + "displayName": "Admin", + "active": true, + "timeZone": "Europe/Berlin" + }, + "created": "2017-10-16T10:06:29.399+0200", + "size": 60444, + "mimeType": "application/pdf", + "content": "$jiraServer/secure/attachment/10013/foo.pdf" + }, + { + "self": "$jiraServer/rest/api/2/attachment/10010", + "id": "10010", + "filename": "bar.pdf", + "author": { + "self": "$jiraServer/rest/api/2/user?username=admin", + "name": "admin", + "key": "admin", + "accountId": "000000:000000-0000-0000-0000-ab899c878d00", + "emailAddress": "admin@example.com", + "avatarUrls": { }, + "displayName": "Admin", + "active": true, + "timeZone": "Europe/Berlin" + }, + "created": "2017-10-16T09:06:48.070+0200", + "size": 438098, + "mimeType": "'application/pdf'", + "content": "$jiraServer/secure/attachment/10010/bar.pdf" + } +] +"@ + + Mock Get-JiraIssueAttachment -ModuleName JiraPS { + $object = ConvertFrom-Json -InputObject $attachments + $object[0].PSObject.TypeNames.Insert(0, 'JiraPS.Attachment') + $object[1].PSObject.TypeNames.Insert(0, 'JiraPS.Attachment') + $object + } + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter { + $Method -eq 'Get' -and + $URI -like "$jiraServer/secure/attachment/*" + } { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri', 'OutFile' + } + + # Generic catch-all. This will throw an exception if we forgot to mock something. + Mock Invoke-JiraMethod -ModuleName JiraPS { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + throw "Unidentified call to Invoke-JiraMethod" + } + + ############# + # Tests + ############# + + It 'only accepts JiraPS.Attachment as input' { + { Get-JiraIssueAttachmentFile -Attachment (Get-Date) } | Should Throw + { Get-JiraIssueAttachmentFile -Attachment (Get-ChildItem) } | Should Throw + { Get-JiraIssueAttachmentFile -Attachment @('foo', 'bar') } | Should Throw + { Get-JiraIssueAttachmentFile -Attachment (Get-JiraIssueAttachment -Issue "Foo") } | Should Not Throw + } + + It 'takes the issue input over the pipeline' { + { Get-JiraIssueAttachment -Issue "Foo" | Get-JiraIssueAttachmentFile } | Should Not Throw + } + + It 'uses Invoke-JiraMethod for saving to disk' { + $script:ShowMockData = $true + Get-JiraIssueAttachment -Issue "Foo" | Get-JiraIssueAttachmentFile + Get-JiraIssueAttachment -Issue "Foo" | Get-JiraIssueAttachmentFile -Path "../" + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = "JiraPS" + ParameterFilter = { + $OutFile -in @("foo.pdf", "bar.pdf") + } + Exactly = $true + Times = 2 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = "JiraPS" + ParameterFilter = { + $OutFile -like "..*.pdf" + } + Exactly = $true + Times = 2 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat + } + } +} diff --git a/docs/en-US/commands/Get-JiraIssueAttachment.md b/docs/en-US/commands/Get-JiraIssueAttachment.md index 93b5d630..f0ae1905 100644 --- a/docs/en-US/commands/Get-JiraIssueAttachment.md +++ b/docs/en-US/commands/Get-JiraIssueAttachment.md @@ -126,6 +126,8 @@ If neither are supplied, this function will run with anonymous access to JIRA. ## RELATED LINKS +[Get-JiraAttachmentFile](../Get-JiraAttachmentFile/) + [Add-JiraIssueAttachment](../Add-JiraIssueAttachment/) [Get-JiraIssue](../Get-JiraIssue/) diff --git a/docs/en-US/commands/Get-JiraIssueAttachmentFile.md b/docs/en-US/commands/Get-JiraIssueAttachmentFile.md new file mode 100644 index 00000000..1be58aba --- /dev/null +++ b/docs/en-US/commands/Get-JiraIssueAttachmentFile.md @@ -0,0 +1,138 @@ +--- +external help file: JiraPS-help.xml +Module Name: JiraPS +online version: https://atlassianps.org/docs/JiraPS/commands/Get-JiraIssueAttachmentFile/ +locale: en-US +schema: 2.0.0 +layout: documentation +permalink: /docs/JiraPS/commands/Get-JiraIssueAttachmentFile/ +--- +# Get-JiraIssueAttachmentFile + +## SYNOPSIS + +Save an attachment to disk. + +## SYNTAX + +```powershell +Get-JiraIssueAttachmentFile [-Attachment] [[-Path] ]] + [[-Credential] ] [] +``` + +## DESCRIPTION + +This function downloads an attachment of an issue to the local disk. + +## EXAMPLES + +### EXAMPLE 1 + +```powershell +Get-JiraIssueAttachmentFile (Get-JiraIssueAttachment -Issue TEST-001) +``` + +This example downloads all attachments from issue TEST-001 to the current +working directory. + +### EXAMPLE 2 + +```powershell +Get-JiraIssue TEST-002 | Get-JiraIssueAttachment | Get-JiraIssueAttachmentFile +``` + +This example illustrates use of the pipeline to download all attachments from +issue TEST-002. + +### EXAMPLE 3 + +```powershell +Get-JiraIssue TEST-002 | + Get-JiraIssueAttachment -FileName "*.png" | + Get-JiraIssueAttachmentFile -Path "c:\temp +``` + +Download all attachments of issue TEST-002 where the filename ends in `.png` +to a specific location. + +## PARAMETERS + +### -Attachment + +Attachment which will be downloaded. + +```yaml +Type: JiraPS.Attachment +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Path + +Path in which to store to attachment. + +The name of the file will be appended to the Path provided. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Credential + +Credentials to use to connect to JIRA. +If not specified, this function will use anonymous access. + +```yaml +Type: PSCredential +Parameter Sets: (All) +Aliases: + +Required: False +Position: 3 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters + +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. +For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### [JiraPS.Attachment] + +## OUTPUTS + +### [Bool] + +## NOTES + +This function requires either the `-Credential` parameter to be passed or a persistent JIRA session. +See `New-JiraSession` for more details. +If neither are supplied, this function will run with anonymous access to JIRA. + +## RELATED LINKS + +[Get-JiraAttachment](../Get-JiraAttachmentFile/) + +[Add-JiraIssueAttachmentFile](../Add-JiraIssueAttachmentFile/) + +[Get-JiraIssue](../Get-JiraIssue/) + +[Remove-JiraIssueAttachmentFile](../Remove-JiraIssueAttachmentFile/)