diff --git a/PwshZendesk.psd1 b/PwshZendesk.psd1 index dfa3413..e607f07 100644 --- a/PwshZendesk.psd1 +++ b/PwshZendesk.psd1 @@ -35,7 +35,7 @@ Description = 'Wrapper for the Zendesk Rest API' # Minimum version of the PowerShell engine required by this module - PowerShellVersion = '5.0' + PowerShellVersion = '5.1' # Name of the PowerShell host required by this module # PowerShellHostName = '' @@ -96,6 +96,8 @@ 'Get-OrganizationRelated', 'Get-ZendeskOrganizationRelated' 'Get-Problem', 'Get-ZendeskProblem' 'Get-SearchCount', 'Get-ZendeskSearchCount' + 'Get-SharingAgreement', 'Get-ZendeskSharingAgreement' + 'Get-SharingAgreement', 'Get-ZendeskSharingAgreement' 'Get-SuspendedTicket', 'Get-ZendeskSuspendedTicket' 'Get-Tag', 'Get-ZendeskTag' 'Get-Ticket', 'Get-ZendeskTicket' @@ -118,6 +120,7 @@ 'New-GroupMembership', 'New-ZendeskGroupMembership' 'New-Organization', 'New-ZendeskOrganization' 'New-OrganizationMembership', 'New-ZendeskOrganizationMembership' + 'New-SharingAgreement', 'New-ZendeskSharingAgreement' 'New-Ticket', 'New-ZendeskTicket' 'New-UserIdentity', 'New-ZendeskUserIdentity' 'Remove-Attachment', 'Remove-ZendeskAttachment' @@ -125,6 +128,7 @@ 'Remove-GroupMembership', 'Remove-ZendeskGroupMembership' 'Remove-Organization', 'Remove-ZendeskOrganization' 'Remove-OrganizationMembership', 'Remove-ZendeskOrganizationMembership' + 'Remove-SharingAgreement', 'Remove-ZendeskSharingAgreement' 'Remove-SuspendedTicket', 'Remove-ZendeskSuspendedTicket' 'Remove-Tag', 'Remove-ZendeskTag' 'Remove-Ticket', 'Remove-ZendeskTicket' @@ -146,6 +150,7 @@ 'Test-Connection', 'Test-ZendeskConnection' 'Update-Group', 'Update-ZendeskGroup' 'Update-Organization', 'Update-ZendeskOrganization' + 'Update-SharingAgreement', 'Update-ZendeskSharingAgreement' 'Update-Ticket', 'Update-ZendeskTicket' 'Update-User', 'Update-ZendeskUser' 'Update-UserIdentity', 'Update-ZendeskUserIdentity' diff --git a/functions/Get-SharingAgreement.ps1 b/functions/Get-SharingAgreement.ps1 new file mode 100644 index 0000000..cb5bda4 --- /dev/null +++ b/functions/Get-SharingAgreement.ps1 @@ -0,0 +1,45 @@ +function Get-SharingAgreement { + <# + .SYNOPSIS + Returns a sharing agreement for your account. + .DESCRIPTION + Returns a sharing agreement for your account. + .EXAMPLE + PS C:\> Get-ZendeskSharingAgreement + + Lists all sharing agreements + .EXAMPLE + PS C:\> Get-ZendeskSharingAgreement -Id 1 + + Gets the details of the sharing agreement with id 1 + #> + [OutputType([PSCustomObject])] + [CmdletBinding(DefaultParameterSetName = 'Default')] + Param ( + + # Unique Id of the group to retrieve + [Parameter(Mandatory = $false)] + [ValidateRange(1, [Int64]::MaxValue)] + [Int64] + $Id, + + # Zendesk Connection Context from `Get-ZendeskConnection` + [Parameter(Mandatory = $false)] + [PSTypeName('ZendeskContext')] + [PSCustomObject] + $Context = $null + ) + + Assert-IsAgent -Context $Context + + if ($PSBoundParameters.ContainsKey('Id')) { + $path = "/api/v2/sharing_agreements/$Id.json" + $key = 'sharing_agreement' + } else { + $path = '/api/v2/sharing_agreements.json' + $key = 'sharing_agreements' + } + + $result = Invoke-Method -Context $Context -Path $path -Verbose:$VerbosePreference + $result | Select-Object -Expand $key +} diff --git a/functions/New-SharingAgreement.ps1 b/functions/New-SharingAgreement.ps1 new file mode 100644 index 0000000..21a46a6 --- /dev/null +++ b/functions/New-SharingAgreement.ps1 @@ -0,0 +1,86 @@ + +function New-SharingAgreement { + <# + .SYNOPSIS + Creates a sharing agreement. + .DESCRIPTION + Creatas a sharing agreement. Requires sharing to be enabled on the Zendesk instance. For more information see: https://support.zendesk.com/hc/en-us/articles/203661466-Sharing-tickets-with-other-Zendesk-Support-accounts + .EXAMPLE + PS C:\> New-ZendeskSharingAgreement -RemoteSubdomain 'Foo' + + Creates a new sharing agreement with the Foo Zendesk Support instance. + #> + [OutputType([PSCustomObject])] + [CMDletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + Param ( + # Name of this sharing agreement + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [String] + $Name, + + # The direction of the agreement + [Parameter(Mandatory = $false)] + [ValidateSet('inbound', 'outbound')] + [String] + $Type, + + # The status of the agreement + [Parameter(Mandatory = $false)] + [ValidateSet('accepted', 'declined', 'pending', 'inactive')] + [String] + $Status, + + # The Partner System + [Parameter(Mandatory = $false)] + [ValidateSet('jira')] + [String] + $PartnerName, + + # Subdomain of the remote account + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [String] + $RemoteSubdomain, + + # Zendesk Connection Context from `Get-ZendeskConnection` + [Parameter(Mandatory = $false)] + [PSTypeName('ZendeskContext')] + [PSCustomObject] + $Context = $null + ) + + Assert-IsAdmin -Context $Context + + $path = '/api/v2/sharing_agreements.json' + $body = @{ + sharing_agreement = @{ + } + } + + if ($PSBoundParameters.ContainsKey('Name')) { + $body.sharing_agreement['name'] = $Name + } + + if ($PSBoundParameters.ContainsKey('Type')) { + $body.sharing_agreement['type'] = $Type + } + + if ($PSBoundParameters.ContainsKey('Status')) { + $body.sharing_agreement['status'] = $Status + } + + if ($PSBoundParameters.ContainsKey('PartnerName')) { + $body.sharing_agreement['partner_name'] = $PartnerName + } + + if ($PSBoundParameters.ContainsKey('RemoteSubdomain')) { + $body.sharing_agreement['remote_subdomain'] = $RemoteSubdomain + } + + if ($PSCmdlet.ShouldProcess($Name, 'Create Group')) { + $result = Invoke-Method -Context $Context -Method 'Post' -Path $path -Body $body -Verbose:$VerbosePreference + $result + } + +} diff --git a/functions/Remove-SharingAgreement.ps1 b/functions/Remove-SharingAgreement.ps1 new file mode 100644 index 0000000..42e7a4b --- /dev/null +++ b/functions/Remove-SharingAgreement.ps1 @@ -0,0 +1,38 @@ + +function Remove-SharingAgreement { + <# + .SYNOPSIS + Deletes a sharing agreement. + .DESCRIPTION + Deletes a sharing agreement. + .EXAMPLE + PS C:\> Remove-ZendeskSharingAgreement -Id 1 + + Deletes the sharing agreement with Id 1 + #> + [OutputType([PSCustomObject])] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + Param ( + # Unique Id of group to delete + [Parameter(Mandatory = $true)] + [ValidateRange(1, [Int64]::MaxValue)] + [Int64] + $Id, + + # Zendesk Connection Context from `Get-ZendeskConnection` + [Parameter(Mandatory = $false)] + [PSTypeName('ZendeskContext')] + [PSCustomObject] + $Context = $null + ) + + Assert-IsAdmin -Context $Context + + $path = "/api/v2/sharing_agreements/$Id.json" + + if ($PSCmdlet.ShouldProcess($Id, "Delete Sharing Agreement")) { + $result = Invoke-Method -Context $Context -Method 'Delete' -Path $path -Verbose:$VerbosePreference + $result + } + +} diff --git a/functions/Update-SharingAgreement.ps1 b/functions/Update-SharingAgreement.ps1 new file mode 100644 index 0000000..80e76eb --- /dev/null +++ b/functions/Update-SharingAgreement.ps1 @@ -0,0 +1,50 @@ +function Update-SharingAgreement { + <# + .SYNOPSIS + Updates the status of a sharing agreement + .DESCRIPTION + Updates the status of a sharing agreement + .EXAMPLE + PS C:\> Update-SharingAgreement -Id 1 -Status 'accepted' + + Accepts the sharing agreement with id 1. + .EXAMPLE + PS C:\> Update-SharingAgreement -Id 1 -Status 'declined' + + Declines the sharing agreement with id 1. + #> + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + Param ( + # Unique Id of the group to retrieve + [Parameter(Mandatory = $false)] + [ValidateRange(1, [Int64]::MaxValue)] + [Int64] + $Id, + + # Unique Id of the group to retrieve + [Parameter(Mandatory = $false)] + [ValidateSet('accepted', 'declined', 'pending', 'inactive')] + [String] + $Status, + + # Zendesk Connection Context from `Get-ZendeskConnection` + [Parameter(Mandatory = $false)] + [PSTypeName('ZendeskContext')] + [PSCustomObject] + $Context = $null + ) + + Assert-IsAdmin -Context $Context + + $path = "/api/v2/sharing_agreements/$Id.json" + $body = @{ + sharing_agreement = @{ + status = $Status + } + } + + if ($PSCmdlet.ShouldProcess($Id, 'Update Group Name.')) { + $result = Invoke-Method -Context $Context -Method 'Put' -Path $path -Body $body -Verbose:$VerbosePreference + $result + } +} diff --git a/tests/Routes-SharingAgreement.tests.ps1 b/tests/Routes-SharingAgreement.tests.ps1 new file mode 100644 index 0000000..71396ba --- /dev/null +++ b/tests/Routes-SharingAgreement.tests.ps1 @@ -0,0 +1,178 @@ +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +Param() + +Import-Module "$PSScriptRoot/../PwshZendesk.psm1" -Force + +Describe 'Groups Routes' { + + InModuleScope PwshZendesk { + + $IsInteractive = [Environment]::GetCommandLineArgs() -join ' ' -notmatch '-NonI' + + $context = @{ + Organization = 'company' + BaseUrl = 'https://company.testdesk.com' + Credential = [System.Management.Automation.PSCredential]::New('email', ('api-key' | ConvertTo-SecureString -AsPlainText -Force)) + User = [PSCustomObject]@{ role = '' } + } + $context | Add-Member -TypeName 'ZendeskContext' + + Mock -ModuleName PwshZendesk Invoke-RestMethod { [PSCustomObject]@{ sharing_agreement = $null; sharing_agreements = $null } } + + Context 'List Sharing Agreements' { + It 'Matches the endpoint' { + if ($IsInteractive) { + throw 'Please run test in non-interactive mode' + } + + $context.User.role = 'admin' + + { Get-SharingAgreement -Context $context } | Should -Not -Throw + Assert-MockCalled Invoke-RestMethod -Exactly 1 -ParameterFilter { $Method -eq 'Get' -and $Uri -match '/api/v2/sharing_agreements\.json' } -Scope It + } + + It 'Does not allow end users to call' { + $context.User.role = 'end-user' + + { Get-SharingAgreement -Context $context } | Should -Throw 'Authenticated user must have role' + } + + It 'Allows agents to call' { + $context.User.role = 'agent' + + { Get-SharingAgreement -Context $context } | Should -Not -Throw + } + + It 'Allows admins to call' { + $context.User.role = 'admin' + + { Get-SharingAgreement -Context $context } | Should -Not -Throw + } + } + + Context 'Show a Sharing Agreement' { + It 'Matches the endpoint' { + if ($IsInteractive) { + throw 'Please run test in non-interactive mode' + } + + $context.User.role = 'admin' + + { Get-SharingAgreement -Context $context -Id 1 } | Should -Not -Throw + Assert-MockCalled Invoke-RestMethod -Exactly 1 -ParameterFilter { $Method -eq 'Get' -and $Uri -match '/api/v2/sharing_agreements/\d+\.json' } -Scope It + } + + It 'Does not allow end users to call' { + $context.User.role = 'end-user' + + { Get-SharingAgreement -Context $context -Id 1 } | Should -Throw 'Authenticated user must have role' + } + + It 'Allows agents to call' { + $context.User.role = 'agent' + + { Get-SharingAgreement -Context $context -Id 1 } | Should -Not -Throw + } + + It 'Allows admins to call' { + $context.User.role = 'admin' + + { Get-SharingAgreement -Context $context -Id 1 } | Should -Not -Throw + } + } + + Context 'Create Sharing Agreement' { + It 'Matches the endpoint' { + if ($IsInteractive) { + throw 'Please run test in non-interactive mode' + } + + $context.User.role = 'admin' + + { New-SharingAgreement -Context $context -Name 'Company @ Zendesk' -Type 'inbound' -RemoteSubdomain 'company' -Confirm:$false } | Should -Not -Throw + Assert-MockCalled Invoke-RestMethod -Exactly 1 -ParameterFilter { $Method -eq 'Post' -and $Uri -match '/api/v2/sharing_agreements\.json' } -Scope It + } + + It 'Does not allow end users to call' { + $context.User.role = 'end-user' + + { New-SharingAgreement -Context $context -Name 'Company @ Zendesk' -Type 'inbound' -RemoteSubdomain 'company' -Confirm:$false } | Should -Throw 'Authenticated user must have role' + } + + It 'Does not allow agents to call' { + $context.User.role = 'agent' + + { New-SharingAgreement -Context $context -Name 'Company @ Zendesk' -Type 'inbound' -RemoteSubdomain 'company' -Confirm:$false } | Should -Throw 'Authenticated user must have role' + } + + It 'Allows admins to call' { + $context.User.role = 'admin' + + { New-SharingAgreement -Context $context -Name 'Company @ Zendesk' -Type 'inbound' -RemoteSubdomain 'company' -Confirm:$false } | Should -Not -Throw + } + } + + Context 'Update a Sharing Agreement' { + It 'Matches the endpoint' { + if ($IsInteractive) { + throw 'Please run test in non-interactive mode' + } + + $context.User.role = 'admin' + + { Update-SharingAgreement -Context $context -Id 1 -Status 'accepted' -Confirm:$false } | Should -Not -Throw + Assert-MockCalled Invoke-RestMethod -Exactly 1 -ParameterFilter { $Method -eq 'Put' -and $Uri -match '/api/v2/sharing_agreements/\d+\.json' } -Scope It + } + + It 'Does not allow end users to call' { + $context.User.role = 'end-user' + + { Update-SharingAgreement -Context $context -Id 1 -Status 'accepted' -Confirm:$false } | Should -Throw 'Authenticated user must have role' + } + + It 'Does not allow agents to call' { + $context.User.role = 'agent' + + { Update-SharingAgreement -Context $context -Id 1 -Status 'accepted' -Confirm:$false } | Should -Throw 'Authenticated user must have role' + } + + It 'Allows admins to call' { + $context.User.role = 'admin' + + { Update-SharingAgreement -Context $context -Id 1 -Status 'accepted' -Confirm:$false } | Should -Not -Throw + } + } + + Context 'Delete a Sharing Agreement' { + It 'Matches the endpoint' { + if ($IsInteractive) { + throw 'Please run test in non-interactive mode' + } + + $context.User.role = 'admin' + + { Remove-SharingAgreement -Context $context -Id 1 -Confirm:$false } | Should -Not -Throw + Assert-MockCalled Invoke-RestMethod -Exactly 1 -ParameterFilter { $Method -eq 'Delete' -and $Uri -match '/api/v2/sharing_agreements/\d+\.json' } -Scope It + } + + It 'Does not allow end users to call' { + $context.User.role = 'end-user' + + { Remove-SharingAgreement -Context $context -Id 1 -Confirm:$false } | Should -Throw 'Authenticated user must have role' + } + + It 'Does not allow agents to call' { + $context.User.role = 'agent' + + { Remove-SharingAgreement -Context $context -Id 1 -Confirm:$false } | Should -Throw 'Authenticated user must have role' + } + + It 'Allows admins to call' { + $context.User.role = 'admin' + + { Remove-SharingAgreement -Context $context -Id 1 -Confirm:$false } | Should -Not -Throw + } + } + } + +}