Integrating Confluence with Jira is a way to ensure that your Atlassian systems are as interconnected as possible and it can help the way your team works. For instance, pick a situation where team members fill out a form in Confluence, and a Jira issue is automatically created based on their input. This setup can streamline workflows, reduce manual data entry and also ensure that tasks and issues are promptly recorded.
But before diving into the “how,” let me briefly explore the “why.” By automating the creation of Jira issues from Confluence forms, you can:
- Enhance Efficiency: Minimize manual data entry and potential errors.
- Ensure Consistency: Standardize the way issues are logged and tracked.
- Improve Traceability: Maintain a clear audit trail from form submission to issue resolution.
Tools and Requirements
To set up this integration, you’ll need:
- Confluence and Jira.
- The “Forms for Confluence” plugin to create forms in Confluence.
- ScriptRunner for Jira to handle the automation.
Step-by-Step Integration Guide
Step 1: Create a Form in Confluence
First, set up a form in Confluence where team members can submit their requests using the “Forms for Confluence” plugin.
- Install Forms for Confluence: If you haven’t already, install the “Forms for Confluence” add-on from the Atlassian Marketplace.
- Create a Form: In a Confluence page, insert the form with the necessary fields (e.g., issue type, summary, description, priority).
- Set Up Field Mapping: Ensure that each form field is mapped to a corresponding Jira field.
Step 2: Configure ScriptRunner for Jira
Next, set up ScriptRunner for Jira to handle the form submissions and create Jira issues. Ensure that ScriptRunner is installed and that you have administrative rights in Jira to configure REST endpoints.
- Create a Scripted REST Endpoint: In ScriptRunner, create a new custom REST endpoint to handle the form submissions.
groovyCopy codeimport com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.AttachmentError
import com.atlassian.jira.issue.IssueInputParametersImpl
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.user.ApplicationUser
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.json.JsonSlurper
import groovy.transform.BaseScript
import org.apache.log4j.Level
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
import com.atlassian.jira.issue.attachment.CreateAttachmentParamsBean
@BaseScript CustomEndpointDelegate delegate
// Set log level to INFO (default for REST endpoints is WARN)
log.setLevel(Level.INFO)
def projectManager = ComponentAccessor.projectManager
def userManager = ComponentAccessor.userManager
def issueService = ComponentAccessor.issueService
def constantsManager = ComponentAccessor.constantsManager
// The requesting user must be in one of these groups
final allowedGroups = ['jira-administrators']
receiveFormWithAttachments(httpMethod: "POST", groups: allowedGroups) { MultivaluedMap queryParams, String body ->
def form = new JsonSlurper().parseText(body) as Map<String, List>
// Gets the project from the key submitted in the form
def project = projectManager.getProjectObjByKey(form.project?.getAt(0) as String)
// Choose a user who has permissions to create issues
final username = 'admin'
def user = userManager.getUserByKey(username)
// The issue type to create
final issueTypeName = 'Task'
def params = new IssueInputParametersImpl()
params.with {
setProjectId(project?.id)
setReporterId(user?.name)
// Sets summary and description from the form data
setSummary(form.summary?.getAt(0) as String)
setDescription(form.description?.getAt(0) as String)
setIssueTypeId(constantsManager.allIssueTypeObjects.findByName(issueTypeName).id)
}
def createValidationResult = issueService.validateCreate(user, params)
if (createValidationResult.errorCollection.hasAnyErrors()) {
log.error("Couldn't create issue: ${createValidationResult.errorCollection}")
return Response.serverError().type(MediaType.APPLICATION_JSON).entity([errors: createValidationResult.errorCollection.errors]).build()
}
def issue = issueService.create(user, createValidationResult).issue
log.info "Created issue: ${issue.key}"
// Adds attachments if present
def warnings = form.attachments ? createAttachments(issue, user, form.attachments) : []
// Generate an HTML list of potential warnings (if any)
def warningsHtmlList = warnings ? "<ul> ${ warnings.collect { "<li>${it}</li>" }.join('') } </ul>" : ''
log.info("[TEST] The warning HTML list ${warningsHtmlList}")
Response.ok("Issue created. ${warningsHtmlList}".toString()).type(MediaType.TEXT_HTML).build()
}
List<String> createAttachments(Issue issue, ApplicationUser user, List<Map> attachments) {
def attachmentManager = ComponentAccessor.attachmentManager
// Check if attachments are enabled in the instance
if (!attachmentManager.attachmentsEnabled()) {
def warning = "Warning! Attachments are disabled on the Jira instance and couldn't be created"
log.warn(warning)
return [warning]
}
def tempDir = new File(System.getProperty('java.io.tmpdir'))
def warnings = attachments.findResults { Map<String, String> attachment ->
// Create the attachment file in the temp dir
def attachmentFile
try {
attachmentFile = new File(tempDir, attachment.filename as String)
attachmentFile.createNewFile()
attachmentFile.bytes = (attachment.content as String).decodeBase64()
} catch (IOException e) {
def warning = "Warning! Couldn't create attachment with name '${attachment.filename}'"
log.warn("${warning}: ${e.message}")
return warning
}
// Create the attachment in Jira
def attachmentBean = new CreateAttachmentParamsBean.Builder(attachmentFile, attachment.filename as String, attachment.contentType as String, user, issue).build()
def result = attachmentManager.tryCreateAttachment(attachmentBean)
if (result.isLeft()) {
def attachmentError = result.left().get() as AttachmentError
def warning = "Warning! Couldn't create attachment with name '${attachmentBean.filename}'"
log.warn("${warning}: : '${attachmentError.logMessage}'")
return warning
}
log.info("Attachment with name '${attachmentBean.filename}' created!")
}
warnings
}
- Configure the Endpoint: Ensure that the endpoint is properly configured to accept form submissions from Confluence.
Step 3: Link the Confluence Form to the ScriptRunner Endpoint
Set up the form in Confluence to submit data to the ScriptRunner endpoint.
- Form Action: Configure the form action to point to the ScriptRunner REST endpoint URL.
- Field Mappings: Ensure that the field names in the form match the expected parameter names in the ScriptRunner script.
Step 4: Test the Integration
Before rolling out the form to the entire team, test the integration to ensure it works as expected.
- Submit a Test Form: Fill out the form in Confluence with sample data and submit it.
- Verify Issue Creation: Check Jira to confirm that a new issue has been created with the correct details.
- Checkingย ScriptRunner logs: Check the ScriptRunner logsย in Jira for any issues or errors as it will help in troubleshooting faster.
Step 5: Roll Out to Your Team
Once you’ve confirmed that the integration works smoothly, you can share the Confluence form with your team. Provide clear instructions on how to use the form and what information is required for each field.
Best Practices for Maintaining the Integration
- Regularly Review Field Mappings: Ensure that any changes in Jira field configurations are reflected in the Confluence form.
- Monitor Automation Logs: Check logs periodically to catch and resolve any issues promptly.
- Gather Feedback: Encourage team members to provide feedback on the form and the integration process to identify any areas for improvement.
Parting Thoughts
By following the steps outlined above and using the provided ScriptRunner script, you can set up a process that reduces manual work and ensures consistency in issue tracking. Use this integration to enhance your team’s efficiency and keep your projects on track. It’s useful to mention periodicย retestingย whenever there are Jira or plugin updates, as this can sometimes cause unexpected issues.
Stay Clouding!