When Jira Cloud was launched custom conditions and validators didn’t even exist. This is because they completely break the design of the cloud architecture. Performing an action in the cloud sets off a chain reaction of micro-services and events all at once, like knocking over multiple rows of dominos. Conditions and validators require a pause in those events while they determine if the domino should fall over or if it is allowed to fall over. It really doesn't work like that so instead of being able to use scripted conditions and validators Atlassian release the expressions language which is the only available way to create custom conditions and validators.
Jira expressions is like any problem “solved” by the government or some service provided by the government. Yes, it technically does the job but the experience usually leaves a lot to be desired. While you can use Jira expressions if you really want to, another solution may be to approach the problem from another direction. Instead executing the script that decides if the transition can/should be performed right at that moment in time like conditions and validators do, you could use scripts to pre-calculate the answer. Using listeners you could store the answer to that question in invisible issue fields called issue entity properties. Then, with expressions you could simply check the value of that property thus created a truly scripted solution and minimizing the need to use expressions.
Example
Below is an example of a validator that controls whether or not a user can transition an issue based on the status or resolution of linked issues.
// custom script validator import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.bc.issue.search.SearchService import com.atlassian.jira.web.bean.PagerFilter import com.atlassian.jira.jql.parser.JqlQueryParser import com.opensymphony.workflow.InvalidInputException def current_user = ComponentAccessor.jiraAuthenticationContext.loggedInUser def query = /assignee = currentUser() and project = "$issue.projectObject.name" and statusCategory = "In Progress"/ // searching for current user having in-progress issues in current project def jql = ComponentAccessor.getComponent(JqlQueryParser).parseQuery(query) def page_filter = new PagerFilter(1) // 1 = search only 1 issue def in_progress_issues = ComponentAccessor.getComponent(SearchService).search(current_user, jql, page_filter) if(in_progress_issues){ // exist at least one in-progress issue invalidInputException = new InvalidInputException("VALIDATION FAILED MESSAGE YOU WANT") }else{ true // true = validation is pass }
if (issue.customfield_1234.value != "Not Required") { return true; } else if (issue.project.key == 'PROJECT' ) { new Issue('PROJECT-1234').description.plainText.includes(issue.customfield_1235); } else if (issue.project.key == 'PROJECT2' ) { new Issue('TEST-1235').description.plainText.includes(issue.customfield_1235); } else { return false; }
JSON.stringify(issue.properties['swanly_timeline']).includes('465')