PSFront.psm1
#region Private function InvokeFrontRestMethod { [CmdletBinding()] param( [Parameter(Mandatory)] [Microsoft.PowerShell.Commands.WebRequestMethod]$Method, [Parameter()] [String]$URL = "https://api2.frontapp.com", [Parameter(Mandatory)] [String]$Endpoint, [Parameter()] [String]$Path, [Parameter()] [hashtable]$Body, [Parameter()] [System.Collections.Specialized.NameValueCollection]$Query, [Parameter(Mandatory)] [SecureString]$ApiKey ) if (-not ("System.Web.HttpUtility" -as [Type])) { Add-Type -AssemblyName "System.Web" -ErrorAction "Stop" } $Params = @{ Method = $Method URI = "{0}/{1}" -f $URL, $Endpoint Headers = @{ "Authorization" = "Bearer {0}" -f [PSCredential]::new("none", $ApiKey).GetNetworkCredential().Password } ContentType = "application/json" ErrorAction = "Stop" ErrorVariable = "InvokeRestMethodError" } if ($PSBoundParameters.ContainsKey("Path")) { $Params["URI"] = "{0}/{1}" -f $Params["URI"], $Path } if ($PSBoundParameters.ContainsKey("Query")) { $Params["URI"] = "{0}?{1}" -f $Params["URI"], $Query.ToString() } if ($PSBoundParameters.ContainsKey("Body")) { $Params["Body"] = $Body | ConvertTo-Json } $Params["URI"] = [System.Web.HttpUtility]::UrlDecode($Params["URI"]) do { Write-Verbose "Calling Front API" Write-Verbose ("URI: '{0}'" -f $Params["URI"]) Write-Verbose ("Body: '{0}'" -f $Params["Body"]) try { $Data = Invoke-RestMethod @Params } catch { # The web exception class is different for Core vs Windows if ($InvokeRestMethodError.ErrorRecord.Exception.GetType().FullName -match "HttpResponseException|WebException") { $ExceptionMessage = $InvokeRestMethodError.Message | ConvertFrom-Json | Select-Object -ExpandProperty "_error" | Select-Object -ExpandProperty "message" $ErrorId = "{0}{1}" -f [Int][System.Net.HttpStatusCode]$InvokeRestMethodError.ErrorRecord.Exception.Response.StatusCode, [String][System.Net.HttpStatusCode]$InvokeRestMethodError.ErrorRecord.Exception.Response.StatusCode switch -Regex ($InvokeRestMethodError.ErrorRecord.Exception.Response.StatusCode) { "Unauthorized" { $Exception = [System.UnauthorizedAccessException]::new($ExceptionMessage) $ErrorRecord = [System.Management.Automation.ErrorRecord]::new( $Exception, $ErrorId, [System.Management.Automation.ErrorCategory]::AuthenticationError, $Params['Uri'] ) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } "BadRequest|Conflict" { $Exception = [System.ArgumentException]::new($ExceptionMessage) $ErrorRecord = [System.Management.Automation.ErrorRecord]::new( $Exception, $ErrorId, [System.Management.Automation.ErrorCategory]::InvalidArgument, $Params['Uri'] ) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } "NotFound" { $Exception = [System.Management.Automation.ItemNotFoundException]::new($ExceptionMessage) $ErrorRecord = [System.Management.Automation.ErrorRecord]::new( $Exception, $ErrorId, [System.Management.Automation.ErrorCategory]::ObjectNotFound, $Params['Uri'] ) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } "ServiceUnavailable" { $Exception = [System.InvalidOperationException]::new($ExceptionMessage) $ErrorRecord = [System.Management.Automation.ErrorRecord]::new( $Exception, $ErrorId, [System.Management.Automation.ErrorCategory]::ResourceUnavailable, $Params['Uri'] ) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } "TooManyRequests" { [int]$Seconds = [int]$InvokeRestMethodError.InnerException.Response.Headers.GetValues("Retry-After")[0] + 1 Write-Verbose ("Exceeded number of requests allowed, will wait {0} second(s) until retrying" -f $Seconds) Start-Sleep -Seconds $Seconds $Params = @{ Method = $Method Endpoint = $Endpoint ApiKey = $ApiKey } if ($PSBoundParameters.ContainsKey("Path")) { $Params["Path"] = $Path } if ($PSBoundParameters.ContainsKey("Query")) { $Params["Query"] = $Query } if ($PSBoundParameters.ContainsKey("Body")) { $Params["Body"] = $Body } return InvokeFrontRestMethod @Params } default { $Exception = [System.InvalidOperationException]::new($ExceptionMessage) $ErrorRecord = [System.Management.Automation.ErrorRecord]::new( $Exception, $ErrorId, [System.Management.Automation.ErrorCategory]::InvalidOperation, $Params['Uri'] ) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } } } else { $PSCmdlet.ThrowTerminatingError($_) } } if ($Params["URI"] -ne $Data._pagination.next) { # This could be null, depending if pagination is needed or not # Update the URI just in case the loop isn't finished yet $Params["URI"] = $Data._pagination.next } else { $Exception = [System.InvalidOperationException]::new("Pagination failure with Front API (the same URL was given for next page)") $ErrorRecord = [System.Management.Automation.ErrorRecord]::new( $Exception, $ErrorId, [System.Management.Automation.ErrorCategory]::OperationStopped, $Params['Uri'] ) $PSCmdlet.ThrowTerminatingError($ErrorRecord) } if ($Data._results) { Write-Output $Data._results } else { Write-Output $Data } } until ([String]::IsNullOrWhiteSpace($Data._pagination.next)) } #endregion #region Public function Invoke-FrontRestMethod { <# .SYNOPSIS Arbitrarily interact with Front's Core API. .DESCRIPTION Arbitrarily interact with Front's Core API. Since this function is for no particular endpoint in Front's API, this can be useful for occasions when Front have added a new endpoint, query or body parameter to their API specification and one of the other dedicated functions of PSFront have not yet been updated. .EXAMPLE PS C:\> Invoke-FrontRestMethod -Method GET -Endpoint tags -ApiKey $secret Lists all tags in Front: https://dev.frontapp.com/reference/tags-1#get_tags .EXAMPLE PS C:\> Invoke-FrontRestMethod -Method GET -Endpoint teams -Path "tim_2daq7/tags" -ApiKey $secret Lists all tags for the team with id tim_2daq7 in Front: https://dev.frontapp.com/reference/tags-1#get_teams-team-id-tags-1 .INPUTS This function does not accept pipeline input. .OUTPUTS System.Management.Automation.PSObject #> [CmdletBinding()] param( [Parameter(Mandatory)] [Microsoft.PowerShell.Commands.WebRequestMethod]$Method, [Parameter(Mandatory)] [String]$Endpoint, [Parameter()] [String]$Path, [Parameter()] [hashtable]$Body, [Parameter()] [hashtable]$Query, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = $Method Endpoint = $Endpoint ApiKey = $ApiKey ErrorAction = "Stop" } switch ($PSBoundParameters.Keys) { "Path" { $Params["Path"] = $Path } "Body" { $Params["Body"] = $Body } "Query" { $QueryString = [System.Web.HttpUtility]::ParseQueryString('') foreach ($item in $Query.GetEnumerator()) { $QueryString.Add($item.Key, $item.Value) } $Params["Query"] = $QueryString } } try { InvokeFrontRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function Add-FrontConversationTag { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")] param ( [Parameter(Mandatory)] [String]$ConversationId, [Parameter(Mandatory)] [String[]]$TagId, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = "POST" Endpoint = "conversations" Path = "{0}/tags" -f $ConversationId Body = @{ "tag_ids" = @($TagId) } ApiKey = $ApiKey } try { if ($PSCmdlet.ShouldProcess( ('Would add tag ID(s) "{0}" to conversation "{1}"' -f [String]::Join('", "', $TagId), $ConversationId), "Are you sure you want to continue?", ('Adding tag ID(s) "{0}" to conversation "{1}"' -f [String]::Join('", "', $TagId), $ConversationId))) { InvokeFrontRestMethod @Params } } catch { Write-Error -ErrorRecord $_ } } #TODO Finish implementing the other search criteria in the query parameter function Find-FrontConversation { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) .NOTES General notes #> param ( [Parameter()] [String[]]$InboxId, [String[]]$Keyword, [Parameter()] [String[]]$Recipient, [Parameter()] [String[]]$From, [Parameter()] [String[]]$To, [Parameter()] [String[]]$CC, [Parameter()] [String[]]$BCC, [Parameter()] [String[]]$TagId, [Parameter()] [String[]]$TopicId, [Parameter()] [String[]]$Contact, [Parameter()] [String[]]$Participant, [Parameter()] [String[]]$Assignee, [Parameter()] [String[]]$Author, [Parameter()] [String[]]$Mention, [Parameter()] [String[]]$Commenter, [Parameter()] [String[]]$Status, [Parameter()] [datetime]$Before, [Parameter()] [datetime]$After, [Parameter(Mandatory)] [SecureString]$ApiKey ) [System.Collections.Generic.List[String]]$Query = @() switch ($PSBoundParameters.Keys) { 'Keyword' { foreach ($item in $Keyword) { $Query.add('"{0}"' -f $item) } } 'InboxId' { foreach ($item in $InboxId) { $Query.add('inbox:{0}' -f $item) } } 'TagId' { foreach ($item in $TagId) { $Query.add('tag:{0}' -f $item) } } 'TopicId' { foreach ($item in $TopicId) { $Query.add('topic:{0}' -f $item) } } 'Contact' { foreach ($item in $Contact) { $Query.add('contact:{0}' -f $item) } } 'Status' { foreach ($item in $Status) { $Query.add('is:{0}' -f $item) } } 'Recipient' { foreach ($item in $Recipient) { $Query.add('recipient:{0}' -f $item) } } 'From' { foreach ($item in $From) { $Query.add('from:{0}' -f $item) } } 'To' { foreach ($item in $To) { $Query.add('to:{0}' -f $item) } } 'CC' { foreach ($item in $CC) { $Query.add('cc:{0}' -f $item) } } 'BCC' { foreach ($item in $BCC) { $Query.add('bcc:{0}' -f $item) } } 'Before' { $Query.add('before:{0}' -f (Get-Date $Before -UFormat '%s')) } 'After' { $Query.add('after:{0}' -f (Get-Date $After -UFormat '%s')) } } $Params = @{ Method = 'GET' Endpoint = 'conversations' Path = 'search/{0}' -f [String]::Join(' ', $Query) ApiKey = $ApiKey } try { InvokeFrontRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function Get-FrontConversation { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) #> param ( [Parameter(Mandatory)] [String]$ConversationId, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = "GET" Endpoint = "conversations" Path = $ConversationId ApiKey = $ApiKey } try { InvokeFrontRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function Get-FrontConversationMessages { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) #> param ( [Parameter(Mandatory)] [String]$ConversationId, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = "GET" Endpoint = "conversations" Path = "{0}/messages" -f $ConversationId ApiKey = $ApiKey } try { InvokeFrontRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function Get-FrontInbox { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) #> param ( [Parameter()] [String]$Id, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = "GET" Endpoint = "inboxes" ApiKey = $ApiKey } if ($PSBoundParameters.ContainsKey("Id")) { $Params["Path"] = $Id } try { InvokeFrontRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function Remove-FrontConversationTag { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")] param ( [Parameter(Mandatory)] [String]$ConversationId, [Parameter(Mandatory)] [String[]]$TagId, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = "DELETE" Endpoint = "conversations" Path = "{0}/tags" -f $ConversationId Body = @{ "tag_ids" = @($TagId) } ApiKey = $ApiKey } try { if ($PSCmdlet.ShouldProcess( ('Would remove tag ID(s) "{0}" to conversation "{1}"' -f [String]::Join('", "', $TagId), $ConversationId), "Are you sure you want to continue?", ('Removing tag ID(s) "{0}" to conversation "{1}"' -f [String]::Join('", "', $TagId), $ConversationId))) { InvokeFrontRestMethod @Params } } catch { Write-Error -ErrorRecord $_ } } #TODO Need to do more here than just tags function Update-FrontConversation { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) #> param ( [Parameter(Mandatory)] [String]$ConversationId, [Parameter(Mandatory)] [String[]]$TagId, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = "PATCH" Endpoint = "conversations" Path = $ConversationId Body = @{ "tag_ids" = @($TagId) } ApiKey = $ApiKey } try { InvokeFrontRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function Get-FrontCompanyTags { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) #> param ( [Parameter()] [String]$Id, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = "GET" Endpoint = "company/tags" ApiKey = $ApiKey } try { InvokeFrontRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function Get-FrontTag { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) #> param ( [Parameter()] [String]$Id, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = "GET" Endpoint = "tags" ApiKey = $ApiKey } if ($PSBoundParameters.ContainsKey("Id")) { $Params["Path"] = $Id } try { InvokeFrontRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function New-FrontTag { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) #> param ( [Parameter(Mandatory)] [String]$Name, [Parameter()] [ValidateSet("Grey", "Pink", "Red", "Orange", "Yellow", "Green", "Light-Blue", "Blue", "Purple")] [String]$Highlight, [Parameter()] [Bool]$IsVisibleInConversationLists, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = "POST" Endpoint = "tags" Body = @{ name = $Name } ApiKey = $ApiKey } if ($PSBoundParameters.ContainsKey("Highlight")) { $Params["Body"]["highlight"] = $Highlight.ToLower() } if ($PSBoundParameters.ContainsKey("IsVisibleInConversationLists")) { $Params["Body"]["is_visible_in_conversation_lists"] = $IsVisibleInConversationLists } try { InvokeFrontRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function Remove-FrontTag { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) #> param ( [Parameter(Mandatory)] [String]$Id, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = "GET" Endpoint = "tags" Path = $Id ApiKey = $ApiKey } try { InvokeFrontRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function Get-FrontMessageTemplateFolder { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) #> param ( [Parameter()] [String]$Id, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = "GET" Endpoint = "message_template_folders" ApiKey = $ApiKey } if ($PSBoundParameters.ContainsKey("Id")) { $Params["Path"] = $Id } InvokeFrontRestMethod @Params } function New-FrontComment { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) .NOTES General notes #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")] param ( [Parameter(Mandatory)] [String]$ConversationId, [Parameter(Mandatory)] [String]$AuthorId, [Parameter(Mandatory)] [String]$Comment, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = "POST" Endpoint = "conversations" Path = "{0}/comments" -f $ConversationId Body = @{ author_id = $AuthorId body = $Comment } ApiKey = $ApiKey } try { if ($PSCmdlet.ShouldProcess( ('Would add commment "{0}" as author "{1}" to conversation "{2}"' -f $Comment, $AuthorId, $ConversationId), "Are you sure you want to continue?", ('Adding commment "{0}" as author "{1}" to conversation "{2}"' -f $Comment, $AuthorId, $ConversationId))) { InvokeFrontRestMethod @Params } } catch { Write-Error -ErrorRecord $_ } } function Get-FrontMessageTemplate { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) #> [CmdletBinding(DefaultParameterSetName="ById")] param ( [Parameter(Mandatory, ParameterSetName="ById")] [String]$Id, [Parameter(Mandatory, ParameterSetName="ByFolderId")] [String]$FolderId, [Parameter(ParameterSetName="All")] [Switch]$All, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = "GET" ApiKey = $ApiKey } switch ($PSCmdlet.ParameterSetName) { "ById" { $Params["Endpoint"] = "message_templates" $Params["Path"] = $id } "ByFolderId" { $Params["Endpoint"] = "message_template_folders" $Params["Path"] = "{0}/message_templates" -f $FolderId } "All" { $Params["Endpoint"] = "message_templates" } } InvokeFrontRestMethod @Params } function Update-FrontMessageTemplate { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE PS C:\> <example usage> Explanation of what the example does .INPUTS Inputs (if any) .OUTPUTS Output (if any) #> [CmdletBinding()] param ( [Parameter(Mandatory)] [String]$Id, [Parameter()] [String]$Name, [Parameter()] [String]$Subject, [Parameter()] [String]$Body, [Parameter()] [String]$FolderId, [Parameter()] [String[]]$InboxId, [Parameter(Mandatory)] [SecureString]$ApiKey ) $Params = @{ Method = "PATCH" ApiKey = $ApiKey Endpoint = "message_templates" Path = $id } $Body = @{} switch ($PSBoundParameters.Keys) { "Name" { $Params["Body"] = $Body["name"] = $Name } "Subject" { $Params["Body"] = $Body["subject"] = $Subject } "Body" { $Params["Body"] = $Body["body"] = $Body } "FolderId" { $Params["FolderId"] = $Body["folder_id"] = $FolderId } "InboxId" { $Params["InboxId"] = $Body["inbox_ids"] = @($InboxId) } } InvokeFrontRestMethod @Params } #endregion |