Validating Attachments/Links In Transitions

Writing a validator that needs to check for newly-added linked issues or attachments in the current transition is a special case. Unfortunately due to deficiencies in the Jira API you need to jump through some additional hoops. This code only applies where you have the Attachments or Issue Links field on the screen. If you can avoid this, you should try to do so. That is, require that the attachments or links are created through the standard operations before the transition is done. It will make your code simpler.

Typically you will get attachments and linked issues from AttachmentManager or IssueLinkManager…​ however these will only give you newly-added attachments/links once the current transaction has been committed. In a validator it has not as yet.

This in itself makes sense, the API deficiency is that there’s no way to get these things from the issue that is available to validators.

Validating Attachments Added

The following example details how to find properties of attachments added this transition or on creation, for example the file name. The Attachments field must be on the transition screen to get attachments like this.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.attachment.TemporaryWebAttachmentManager
import com.atlassian.jira.issue.fields.AttachmentSystemField
import webwork.action.ActionContext

def temporaryAttachmentManager = ComponentAccessor.getComponent(TemporaryWebAttachmentManager)
def temporaryAttachmentIds = ActionContext.getRequest()?.getParameterValues(AttachmentSystemField.FILETOCONVERT)

temporaryAttachmentIds.each { String attachmentId ->
    def attachment = temporaryAttachmentManager.getTemporaryWebAttachment(attachmentId).getOrNull()
    if (attachment) {
        log.debug "Uploaded attachment name: ${attachment.filename}"
    }
}

Via the REST API, it’s not currently possible to create an issue with attachments, or add attachments on a transition. The form token above is not available in REST contexts, but because of this limitation it doesn’t matter.

Blocking Files by Extension

You can block files with certain extensions being added using the following script in conjunction with a Simple Scripted Validator:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.attachment.TemporaryWebAttachmentManager
import com.atlassian.jira.issue.fields.AttachmentSystemField
import com.google.common.io.Files
import webwork.action.ActionContext

def extensionsToBlock = ['exe', 'reg']

def temporaryAttachmentManager = ComponentAccessor.getComponent(TemporaryWebAttachmentManager)
def temporaryAttachmentIds = ActionContext.getRequest()?.getParameterValues(AttachmentSystemField.FILETOCONVERT)

!temporaryAttachmentIds.any { String attachmentId ->
    def attachment = temporaryAttachmentManager.getTemporaryWebAttachment(attachmentId).getOrNull()
    attachment && Files.getFileExtension(attachment.filename).toLowerCase() in extensionsToBlock
}

You cannot select the Attachments field as the field to show the error on. Leave the error field blank, and the error message will be shown at the top of the form.

Reading Attachments Added this Transition

If you need to read the contents of newly-added attachments, for in-depth validation, you can get the data like this:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.IssueFieldConstants
import com.atlassian.jira.issue.attachment.FileSystemAttachmentDirectoryAccessor

def attachmentDirectoryAccessor = ComponentAccessor.getComponent(FileSystemAttachmentDirectoryAccessor)
def temporaryAttachmentDirectory = attachmentDirectoryAccessor.getTemporaryAttachmentDirectory()

def newAttachmentNames = issue.modifiedFields.get(IssueFieldConstants.ATTACHMENT)?.newValue
newAttachmentNames.each { String filename ->
    log.debug "File text:" + new File(temporaryAttachmentDirectory, filename).text
}

Validating Links Added this Transition

The following example shows how to read links that have been added on the screen during the transition, and in this case, stops the action unless at least one blocks link has been added.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.IssueFieldConstants
import com.atlassian.jira.issue.fields.IssueLinksSystemField
import com.opensymphony.workflow.InvalidInputException
import webwork.action.ActionContext

def fieldManager = ComponentAccessor.getFieldManager()
def linksSystemField = fieldManager.getField("issuelinks") as IssueLinksSystemField

def request = ActionContext.getRequest()
if (request) {
    def params = request.getParameterMap()
    def issueLinkingValue = linksSystemField.getRelevantParams(params) as IssueLinksSystemField.IssueLinkingValue

    if (!(issueLinkingValue.linkDescription == "blocks" && issueLinkingValue.linkedIssues.size() > 0)) {
        throw new InvalidInputException(IssueFieldConstants.ISSUE_LINKS,
            "You must link a blocker issue at this transition")
    }
}

You should also allow for that the correct link may have been added previous to the transition, using the examples described here.

On this page