How to fix Groovy Listener Script Error?

Shah Baloch March 7, 2024

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,

1 answer

1 accepted

0 votes
Answer accepted
Peter-Dave Sheehan
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
March 7, 2024

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

  1.  Create a behaviour config mapped to your project/issue type
  2. Add both your start and end date fields to the behaviour
  3. Add server-side script to both fields
  4. Put the following script in both server-side scripts

 

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)
}

 

 

Shah Baloch March 8, 2024

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,

Peter-Dave Sheehan
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
March 8, 2024

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.

 

Shah Baloch March 8, 2024

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."

Screenshot 2024-03-08 180437.png

Shah Baloch March 8, 2024

@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

Peter-Dave Sheehan
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
March 8, 2024

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.

Like Shah Baloch likes this
Shah Baloch March 11, 2024

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" ))

Peter-Dave Sheehan
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
March 11, 2024

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.

Like Shah Baloch likes this
Shah Baloch March 11, 2024

It worked, thank you so much @Peter-Dave Sheehan

Suggest an answer

Log in or Sign up to answer
TAGS
AUG Leaders

Atlassian Community Events