Hello,
I am trying to get comfortable with ScriptRunner and therefore I want to automate a Script that changes the Summary of issues of an certain issuetype everytime the issue is created or updated. I already have written a Automation Rule with Jira Automation Plugin which works. Now I want the same result using ScriptRunner.
The following figure shows the Rule which I want to "copy" with ScriptRunner.
To get there I used a Behaviour and added the following Script:
import com.atlassian.jira.ComponentAccessor
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.Issue
def field2ChangeValue = getFieldByName('summary')
def StudiengangF =
ComponentAccessor.getCustomFieldManager().getCustomFieldObject("Studiengang")
def StudiengangValue =
underlyingIssue?.getCustomFieldValue(StudiengangF)
def ModulkennungF =
ComponentAccessor.getCustomFieldManager().getCustomFieldObject("Modulkennung")
def ModulkennungValue =
underlyingIssue?.getCustomFieldValue(ModulkennungF)
field2ChangeValue.setFormValue("${StudiengangValue}-${ModulkennungValue}")
It seems like the script itself is fine but I dont have any idea how to automate this. To literally say Jira: Do this Scirpt everytime an issue of the issuetype XY is created or updated.
Do I have to set an initialiser for that and what would it be?
Thanks @Hana Kučerová for your support.
I found some ispiration and tried my best but it did not worked out. Could you please help me with my code?
import com.atlassian.jira.ComponentAccessor
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
// Define which issuetypes
def issue = event.issue
issue.projectObject.key == 'SUD'
issue.issueType.name == 'SUD Modul Basic'
def customFieldManager = ComponentAccessor.customFieldManager
def StudiengangF =
ComponentAccessor.getCustomFieldManager().getCustomFieldObject("Studiengang")
def StudiengangValue = getCustomFieldValue(StudiengangF)
def ModulkennungF =
ComponentAccessor.getCustomFieldManager().getCustomFieldObject("Modulkennung")
def ModulkennungValue = getCustomFieldValue(ModulkennungF)
// the name of the custom field to update
final customFieldName = 'Summary'
// the new value of the field
final newValue = '${StudiengangValue}-${ModulkennungValue}'
def customField = customFieldManager.getCustomFieldObjects(issue).findByName(customFieldName)
assert customField : "Could not find custom field with name $customFieldName"
customField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(customField), newValue), new DefaultIssueChangeHolder())
Best
Daniel
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @dD ,
please try something like this.
Appropriate ids for you custom fields needs to be set
Project needs to be set in the configuration of listener.
import com.atlassian.jira.bc.issue.IssueService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueInputParameters
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.user.ApplicationUser
Long CUSTOM_FIELD_STUDIENGANG_ID = 12345
Long CUSTOM_FIELD_MODULKENNUNG_ID = 23456
Issue issue = event.getIssue()
CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager()
IssueService issueService = ComponentAccessor.issueService
ApplicationUser loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
IssueInputParameters issueInputParameters = issueService.newIssueInputParameters()
CustomField studiengangCustomField = customFieldManager.getCustomFieldObject(CUSTOM_FIELD_STUDIENGANG_ID)
CustomField modulkennungCustomField = customFieldManager.getCustomFieldObject(CUSTOM_FIELD_MODULKENNUNG_ID)
String studiengang = issue.getCustomFieldValue(studiengangCustomField) as String
String modulkennung = issue.getCustomFieldValue(modulkennungCustomField) as String
issueInputParameters.setSummary(studiengang + "-" + modulkennung)
IssueService.UpdateValidationResult updateValidationResult = issueService.validateUpdate(loggedInUser, issue.getId(), issueInputParameters)
assert updateValidationResult.isValid() : updateValidationResult.errorCollection
IssueService.IssueResult issueResult = issueService.update(loggedInUser, updateValidationResult)
assert issueResult.isValid() : issueResult.errorCollection
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Best thanks to you it really worked !! :) I really appreciate your help!
Can I also ask you to briefly explain me some coding fragments (I would like to really understand it?
E.g. what does "long" do and why is it neccesairy to relate to a field by the field_ids and not by the name?
I am also wondering why sometimes words need to be written twice like " IssueService issueService =" ?
How exactly does the "IssueInputParameters"-function work?
and lastly what happens in that codeblock?
IssueService.UpdateValidationResult updateValidationResult = issueService.validateUpdate(loggedInUser, issue.getId(), issueInputParameters)
assert updateValidationResult.isValid() : updateValidationResult.errorCollection
IssueService.IssueResult issueResult = issueService.update(loggedInUser, updateValidationResult)
assert issueResult.isValid() : issueResult.errorCollection
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
E.g. what does "long" do and why is it neccesairy to relate to a field by the field_ids and not by the name?
It is not necessary, but for me it is a good practise to use ids instead of names, as you can have more custom fields with the same name, but id is an unique identifier of the custom field.
I am also wondering why sometimes words need to be written twice like " IssueService issueService =" ?
You can also do
def issueService = ComponentAccessor.issueService
I just prefer to define exact variable types, so that all sort of suggestion and check functions work in my IDE.
How exactly does the "IssueInputParameters"-function work?
and lastly what happens in that codeblock?
Through IssueInputParameters you prepare all sort of changes and then try to update the issue (validateUpdate) under the account of actually logged in user. If everything is ok and there are no errors, then really update the issue (update).
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Regarding your last answer:
Through IssueInputParameters you prepare all sort of changes and then try to update the issue (validateUpdate) under the account of actually logged in user. If everything is ok and there are no errors, then really update the issue (update).
Is it necessairly to update the issue under the account of actually logged in user? Or is it possible to avoid this and change the summary without touching an user account just alone by the system?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@dD it is not necessary to use logged in user (which in this context means - the user, who created / updated the issue), it can be any user (e.g. some technical account), who has the appropriate permissions (to see the issue and update it). If the user doesn't have appropriate permissions, the proces of validation fails and update is not performed.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Alright that makes sense.
As I went on further testing I noted that this Script is still not working perfectly. We overlooked that by now every new issue within that project is adressed. So when I create a new Issue from a different issuetype, automatically the summary gets changed. But this automation rule should only adress one specific issuetype within the project, how could i make this clear?
Issue issue.issueType.name == 'SUD Modul Basic'
Issue issue = event.getIssue()
I tried this but it doesnt work
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You can add the check on the top of the script, something like:
String ISSUE_TYPE_ID = "12345"
Issue issue = event.getIssue()
if (issue.getIssueTypeId() != ISSUE_TYPE_ID) {
return
}
12345 needs to be replaced with your allowed issue type id
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Yes !! Nice !! This works :)
If I would want to adress to more than one Issuetype, would I create a list like
String ISSUE_TYPE_ID = ["12345","234562]
or would I need to define a Variable for each issuetype? like
String ISSUE_TYPE_ID_1
String ISSUE_TYPE_ID_2
String ISSUE_TYPE_ID_3
or which is the smartest way? :D
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I would go for list of strings, something like
List<String> ISSUE_TYPE_IDS = ["12345", "23456"]
Issue issue = event.getIssue()
if (!ISSUE_TYPE_IDS.contains(issue.getIssueTypeId())) {
return
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
This is really helpful! Thank you @Hana Kučerová :)
Also, is it right that "event.getIssue()" only defines the issues for listeners? What would I need to write if I would like to run a scheduled job? Because then I retrieve an error, if I use event.getIssue().
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Listeners are called, whenever events occur in Jira. Scheduled Jobs are a mechanism for running code at specific times, so they are not connected to any events in Jira and "event.getIssue()" cannot work.
You need to obtain the issues differently, for example search for them using JQL or get them directly using their key.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
// Get the Issue via JQL Search
final jqlSearch = "issuetype = 'SUD Modul' and 'Sommersemester aktiv?' = aktiv"
// Step1: some components
def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def searchService = ComponentAccessor.getComponentOfType(SearchService)
// Step2: Parse the query
def parseResult = searchService.parseQuery(user, jqlSearch)
if (!parseResult.valid) {
log.error('Invalid query')
return null}
try {
// Step3: Perform the query to get the issues
def results = searchService.search(user, parseResult.query, PagerFilter.unlimitedFilter)
def issues = results.results
issues.each {
log.info(it.key)}
def assignee = issue.getAsignee()
// Defining the subject and body of the email
def emailSubject = "Verantwortlichkeit im Modul ${issue.modulname}"
def emailBody = "Sehr geehrte/r ${assignee.displayName},\n derzeit sind Sie als Lehrkraft im Modul {{issue.modulname}} eingetragen. \n Wenn Sie diese Modul im kommenden Sommersemester (SoSe ${now.format("yyyy")}) nicht unterrichten können, dann melden Sie dies bitte umgehend ihrer Studiengangsassistenz.\n Ansonsten tun Sie nichts und ihre Modulverantwortlichkeit bleibt bestehen.\n\n(Dies ist eine automatisch generierte Mail)"
def emailAddr = issue.getAssignee().getEmailAdress
String sendEmail(String emailAddr, String subject, String body) {
def logger = Logger.getLogger(getClass())
logger.setLevel(Level.DEBUG)
// Stop emails being sent if the outgoing mail server gets disabled (useful if you start a script sending emails and need to stop it)
def mailSettings = ComponentAccessor.getComponent(MailSettings)
if (mailSettings?.send()?.disabled) {
return 'Your outgoing mail server has been disabled'
}
def mailServer = ComponentAccessor.mailServerManager.defaultSMTPMailServer
if (!mailServer) {
logger.debug('Your mail server Object is Null, make sure to set the SMTP Mail Server Settings Correctly on your Server')
return 'Failed to Send Mail. No SMTP Mail Server Defined'
}
def email = new Email(emailAddr)
email.setMimeType('text/html')
email.setSubject(subject)
email.setBody(body)
try {
// This is needed to avoid the exception about IMAPProvider
ContextClassLoaderSwitchingUtil.runInContext(SMTPMailServer.classLoader) {
mailServer.send(email)
}
logger.debug('Mail sent')
'Success'
} catch (MailException e) {
logger.debug("Send mail failed with error: ${e.message}")
'Failed to Send Mail, Check Logs for error'
}
}
sendEmail(emailAddr, emailSubject, emailBody)
@Hana Kučerová I tried this code, but it is wrong. What do I have to change?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.