Hi Community,
I created a listener script to display an error if a ticket already has the start and end date and someone tries to use the same date and time. There are two custom dates/times and fields. When users create a ticket, they will fill both fields. For example, if a ticket has a start date/time of 03/07/2024 at 6:00 pm and an end date/time of 03/07/2024 at 10:00 pm when another user tries to use the exact date and time for a new ticket, it should display an error. For some reason, the script is not working. I created multiple tickets using the same date and time. It's not displaying an error, but when I check the log, I see the error below. I have been trying to figure out and fix the error for the last couple of days. I am unable to fix it. Any idea what's wrong with it?
Error
2024-03-07 14:37:43,078 ERROR [runner.AbstractScriptListener]: ************************************************************************************* 2024-03-07 14:37:43,079 ERROR [runner.AbstractScriptListener]: Script function failed on event: com.atlassian.jira.event.issue.IssueEvent, file: null groovy.lang.MissingMethodException: No signature of method: com.atlassian.jira.issue.search.providers.LuceneSearchProvider.search() is applicable for argument types: (com.atlassian.query.QueryImpl, com.atlassian.jira.user.DelegatingApplicationUser...) values: [{project = "CCB"} AND ( {start_date_field <= "2024-03-07 14:30:00.0"} AND {end_date_field >= "2024-03-07 14:30:00.0"} OR {start_date_field <= "2024-03-08 14:30:00.0"} AND {end_date_field >= "2024-03-08 14:30:00.0"} ), ...] Possible solutions: search(com.atlassian.jira.issue.search.SearchQuery, com.atlassian.jira.web.bean.PagerFilter), search(com.atlassian.jira.issue.search.SearchQuery, com.atlassian.jira.web.bean.PagerFilter, java.util.Set), search(com.atlassian.jira.issue.search.SearchQuery, org.apache.lucene.search.Collector), each(groovy.lang.Closure) at Script3.findOverlappingIssues(Script3.groovy:42) at Script3.run(Script3.groovy:20)
Script
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.issue.search.SearchException
import com.atlassian.jira.issue.search.SearchQuery
// Get custom fields
def customFieldManager = ComponentAccessor.customFieldManager
def startDateField = customFieldManager.getCustomFieldObjectByName("Start Date/Time")
def endDateField = customFieldManager.getCustomFieldObjectByName("End Date/Time")
// Get the selected values
def startDate = issue.getCustomFieldValue(startDateField) as Date
def endDate = issue.getCustomFieldValue(endDateField) as Date
// Get existing issues that overlap with the new date range
def overlappingIssues = findOverlappingIssues(startDate, endDate, issue.key)
// Display an error message if there are overlapping issues
if (overlappingIssues) {
def overlapMessage = "There are existing tickets reserved for the selected date and time range. Please choose a different range."
issueInputParameters.addError("Start Date/Time", overlapMessage)
issueInputParameters.addError("End Date/Time", overlapMessage)
}
// Function to find issues that overlap with the given date range
def findOverlappingIssues(Date startDate, Date endDate, String currentIssueKey) {
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def user = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def issueManager = ComponentAccessor.issueManager
// Use the JqlQueryParser to parse the JQL query
def jqlQuery = "project = 'CCB' AND ((start_date_field <= '$startDate' AND end_date_field >= '$startDate') OR (start_date_field <= '$endDate' AND end_date_field >= '$endDate'))"
def query = jqlQueryParser.parseQuery(jqlQuery)
try {
// Exclude the current issue if it's being edited
def currentIssue = issueManager.getIssueObject(currentIssueKey)
def searchResults = ComponentAccessor.getComponent(SearchProvider).search(query, user, PagerFilter.getUnlimitedFilter())
def overlappingIssues = searchResults.results.findAll { it.issue.key != currentIssueKey }
return overlappingIssues
} catch (SearchException e) {
log.error("Error occurred while searching for overlapping issues: ${e.message}", e)
return []
}
}
Thank you,
Hi Shah
I think there is a fundamental error in your thinking.
Listeners can't prevent issue creation or raise errors to the users and prevent them from continuing.
Listeners happen AFTER the event is complete (i.e. issue creation or issue update).
You need to either use a workflow transition validator or a behaviour (if you need this to work during issue edits too).
As for your actual error, I would recommend you replace the SearchProvide-related code with HAPI search methods.
Here is how I would achieve your requirement using a Behaviour Config
def startDateFieldName = 'Start Date/Time'
def endDateFieldName = 'End Date/Time'
def startDateFld = getFieldByName(startDateFieldName)
def endDateFld = getFieldByName(endDateFieldName)
startDateFld.clearError()
endDateFld.clearError()
def startDate = startDateFld.value as Date
def endDate = endDateFld.value as Date
if(!startDate){
startDateFld.setError("Start Date is required: $startDate")
return
}
if(!endDate){
endDateFld.setError('Start Date is required')
return
}
def currentIssueCriteria = (underlyingIssue) ? "AND Key != $underlyingIssue.key" : '' //exclude the current issue if we're in edit mode
def jqlDateFormat = 'yyyy/MM/dd HH:mm'
def startDateJqlFormat = startDate.format(jqlDateFormat)
def endDateJqlFormat = endDate.format(jqlDateFormat)
def jql = """project = CCB $currentIssueCriteria AND
(($startDateFld.fieldId <= "$startDateJqlFormat" AND $endDateFld.fieldId >= "$startDateJqlFormat" )
OR ($startDateFld.fieldId <= "$endDateJqlFormat" AND $endDateFld.fieldId >= "$endDateJqlFormat" ))"""
def overLappingIssuesFound = Issues.count(jql) > 0
if(overLappingIssuesFound){
def overlapMessage = "There are existing tickets reserved for the selected date and time range. Please choose a different range."
startDateFld.setError(overlapMessage)
endDateFld.setError(overlapMessage)
}
Hi @Peter-Dave Sheehan thank you for the suggestion and behaviour script. I copied and pasted your script to both start and end date/time field. I just included these four lines at the top. However I am getting an error.
import com.onresolve.jira.groovy.user.FieldBehaviours
import groovy.transform.BaseScript
import java.text.SimpleDateFormat
@BaseScript FieldBehaviours behaviours
2024-03-08 16:59:34,134 ERROR [behaviours.BehaviourManagerImpl]: *************************************************************************************
2024-03-08 16:59:34,136 ERROR [behaviours.BehaviourManagerImpl]: Script function failed on issue: (create issue) project/issuetype: CCB/Task, user: jirauser, fieldId: customfield_1400, file: <inline script>, edit behaviour: https://ourstance.com:8443/plugins/servlet/scriptrunner/admin/behaviours/edit/aaaaaaaaa-1111-44444-bbbb-ccc4444444444
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '' with class 'java.lang.String' to class 'java.util.Date'
at e5a0d01d5d68b6163dc3d2854a785195.run(e5a0d01d5d68b6163dc3d2854a785195.groovy:15)
Thank you,
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Did you adjust the first 2 lines with the actual names of your fields?
You can try removing the "as Date" on lines 8 and 9 (my numbering, I don't know which lines that corresponds on your final version). I added those to help with static type checking.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
No, since the field names are the same. My custom start field name is "Start Date/Time," and the end field name is "End Date/Time."
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Peter-Dave Sheehan I removed as Date part from those two lines. The error went away. However, it does not show the below message when I select the same start/end date/time for another ticket, even if there is an unresolved ticket in the project.
"There are existing tickets reserved for the selected date and time range. Please choose a different range."
Thanks
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You can try to add something like this near the end for debugging purposes:
startDateFld.setHelpText("Found $overLappingIssuesFound based on JQL: $jql")
And see if the start date field will show the details. Then copy the JQL and confirm it was formatted correctly by pasting it in an advanced issue search.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Peter-Dave Sheehan the below messaged got displayed after adding the piece of debug code.
Found false based on JQL: project = CCB AND ((customfield_1400 <= "07/Mar/24 2:30 PM" AND customfield_1401 >= "07/Mar/24 2:30 PM" ) OR (customfield_1400 <= "08/Mar/24 2:30 PM" AND customfield_1401 >= "08/Mar/24 2:30 PM" ))
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Ah yeah... I forgot JQL takes a different form for custom fields.
Change this line:
def jql = """project = CCB $currentIssueCriteria AND
(($startDateFld.fieldId <= "$startDateJqlFormat" AND $endDateFld.fieldId >= "$startDateJqlFormat" )
OR ($startDateFld.fieldId <= "$endDateJqlFormat" AND $endDateFld.fieldId >= "$endDateJqlFormat" ))"""
To
def startCf = startDateFld.customFieldManager.getCustomFieldObject(startDateFld.fieldId)
def endCf = endDateFld.customFieldManager.getCustomFieldObject(endDateFld.fieldId)
def jql = """project = CCB $currentIssueCriteria AND
((cf[$startCf.idAsLong] <= "$startDateJqlFormat" AND cf[$endCf.idAsLong] >= "$startDateJqlFormat" )
OR (cf[$startCf.idAsLong] <= "$endDateJqlFormat" AND cf[$endCf.idAsLong] >= "$endDateJqlFormat" ))"""
Then confirm the JQL works and tweak as necessary.
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.