Grammar
This page describes the basic grammar, variable declarations, data types and literals that are used in the YouTrack workflow programming language.
Workflow Language Elements | ||||
---|---|---|---|---|
The workflow programming language borrows most of its syntax from JavaScript. If you are familiar with JavaScript, you will quickly recognize the structure that is used to write a workflow rule.
If you're not a programmer, you can find your way around with the code completion feature in the workflow editor. The workflow editor shows you a list of available options for every element in the code. The editor also displays default text that tells you what is expected for each line.
The workflow language is case-sensitive and uses the Unicode character set.
Workflow Rule Syntax
The syntax that is used for each rule is defined by the rule type. The workflow editor applies constraints when you write that help you follow the required syntax.
The syntax for each rule type is as follows:
Statelessrule [rule name]
when <issue created or updated> {
<define statements>
}
The initial when
statement determines whether the rule is triggered on issue creation or update.
schedule rule [rule name]
[schedule] [<expression>] {
<define statements>
}
The initial schedule
statement determines when the rule is triggered. The following intervals are supported:
every minute
daily at <hour>:<minute>:<second>
weekly on <day of week> at <hour>:<minute>:<second>
hourly at <minute>:<second>
monthly on <day of month> at <hour>:<minute>:<second>
yearly on <month>, <day of month> at <hour>:<minute>:<second>
cron: [cron expression]
The expression (set in square brackets) that follows the schedule
statement is a guard condition. The guard condition determines whether the subsequent statements are executed.
statemachine [rule name] for field [field name] }
initial state [field value] {
}
state [field value] {
}
}
For each state, you specify the conditions for applying an action. The following options are available:
Statement | Description |
---|---|
enter | The changes that are specified in a nested block statement are applied when the field is set to the specified value. |
exit | The changes that are specified in a nested block statement are applied when the field is changed from the specified value. |
on [action] [condition] do {statements} transit to [field value] | The changes that are specified in the {statements} block are applied when the action is applied with a command or the field is set to the specified value. The optional [condition] block specifies additional criteria that must be met to apply the specified changes. After the changes are applied, the issue transitions automatically to the specified value. |
in [time frame] [condition] do {statements} transit to [field value] | The changes that are specified in the {statements} block are applied after the specified time frame. The optional [condition] block specifies additional criteria that must be met to apply the specified changes. After the changes are applied, the issue transitions automatically to the specified value. |
Literals
The workflow programming language uses the following keywords to represent fixed values.
Keyword | Description | Example |
---|---|---|
loggedInUser | References the user who is currently logged in to YouTrack. | issue.Assignee = loggedInUser |
issue | References the current issue. | when issue.becomesReported() |
now | References the current date and time in UTC. | Due date = now |
true | A Boolean literal value. | var isHandled = true |
false | A Boolean literal value. | var hasChildren = false |
null | A reference literal value. | var number = null |
Use the following formats to reference literal values for specific data types.
Type | Format | Example |
---|---|---|
date | Written in the format | if (created < 2012-Dec-31 && !isResolved()) {
tags.add(project.getUser("root").getSharedTag("obsolete?"));
} |
time | Written in the format | daily at 08:00:00 [issue.resolved == null && issue.Priority == {Normal} && issue.created < now - 7 days]{
issue.Priority = {Major};
} |
integer | Unformatted numeric value. | var increaseNumber = Internal number + 1; |
period | Represented by an integer and the time period. The following period constants are supported:
| if (updated + 10 months < now) {
project.leader.watchIssue(issue);
} |
string | Set in quotation marks | if (now > created + 3 days && Assignee == null) {
message("Issue is three days old and is still unassigned!");
} |
You can reference literal values for entities with the construction {entity: static_entity_name}
.
Reference | Value |
---|---|
{group: [group_name]} | UserGroup literal. |
{issue: [issueID]} | Issue literal. |
{project: [project_shortName]} | Project literal. |
{savedSearch: [savedSearchName]} | SavedSearch literal. |
{tag: [tagName]} | IssueTag literal. |
{user: [username]} | User literal. |
{[customFieldName]} | CustomField literal. |
The following examples show you how to insert static references to different entities.
Code | Description |
---|---|
{group: QA Engineers}.notifyAllUsers("Test failed!", "Please look at the failed test " + getId()) | References the UserGroup |
assert {issue: IDEA-99999}.Type == {Feature}: "It should be the cool feature about the new look and feel!"; | References a single Issue |
for each currency in {project: Business Trips}.valuesFor(Currency) {
if (currency.name == {Dollar}) {
assert currency.colorIndex == 17: "Dollar should be green!";
}
} | References the Project |
for each notMyIssue in loggedInUser.getIssues({savedSearch: Reported by me}, "for: -me") {
notMyIssue.Assignee = loggedInUser;
} | References the SavedSearch |
rule Regression
when State.becomes({Reopened}) {
tags.add({tag: regression});
} | References the IssueTag |
Variables
Variables are declared with the keyword var
, followed by the name of the variable. You can specify the initial value with the =
sign, as with JavaScript.
var notInitializedVariable;
var initializedVariable = "Hello";
The data type of the variable is inferred from the type that is specified in the initial expression. When a variable has no initial value, its value is inferred from its usages.
Once you have declared a variable, you can refer to it by its name.
notInitializedVar = "World!";
notInitializedVar = initializedVar + ", " + notInitializedVar;
In the following example, the variable user
is used to set the assignee of an issue.
var user;
if (issue.Assignee == null) {
user = issue.project.leader;
} else {
user = issue.Assignee;
}
When you use the createNewIssue
method, an issue is created with the default (or empty) values for each field as configured for the project. To create an issue with specific values for fields, use variable declarations.
The following sample shows how to create an issue in the project with the ID RM
, specify values for custom fields, and add a depends on link for the current issue to the issue that is created by the workflow.
var i = loggedInUser.createNewIssue("RM");
i.Assignee = Marketing;
i.Due Date = Due Date - 1 month;
i.summary = "Write text for mailing list";
i.Version = Version;
i.Product = Product;
depends on.add(i);
State-machine Declarations
State-machine rules use a pre-defined set of declarations. These declarations identify the field that is managed by the state-machine rule and values that are assigned to each transition.
Declaration | Description |
---|---|
for field | Identifies the field that is managed by the state-machine rule. |
initial state | Sets the value that is assigned to new issues for the specified field. |
state | Specifies a value in the set of values for the custom field. Allowed transitions for the specified value are defined in the block statements that follow the declaration. |
transit to | Specifies the value that is set for the specified field. This declaration is used in an |
Comments
You can use comments to add notes to your workflow rules. This can help others understand what a rule does and how it can be implemented. You can also use comments to disable specific lines of code.
To insert a comment, use //
. All of the text that follows on the same line is commented out. For example:
for each subtask in parent.parent for {
//We can't use subtask.isResolved() here, as this method relies on issue.resolved
//property, which is updated after workflow rules are executed.
if (!subtask.State.isResolved) {
allSubtasksResolved = false;
break;
}
}