The main workflow rules types are the stateless rule, the scheduled rule and the state-machine rule.
Code
Description
rule Up priority
when State.becomes({Open}) || Fix versions.changed {
Priority = {Critical};
}
The rule raises 'Priority' when the 'State' field is changed to the 'Open'.
Every field has the .becomes() method and the .changed property:
field .becomes({Value}) is true if the current change is setting the value to {Value}
field .change is true, if the field's value is just changing in the current transaction.
You can also refer for more detailed description here
schedule rule Project admin report
daily at 10:00:00 [issue.Priority == {Show-stopper}] {
project.leader.notify("Urgent!", "Look at the " + getId());
}
Each day at 10:00, this rule sends emails to a project's administrator about all 'Show-stoppers'.
The scheduled rule is triggered by a timer that can be set to go off every minute, hourly,
daily, weekly, monthly, yearly or by an arbitrary cronexpression.
You can also refer for more detailed description here
statemachine state machine for field State {
initial state Open {
on fix[always] do {
Fix versions.required("Please set the 'Fix versions'");
} transit to Fixed
exit {
message("You're leaving the state 'Open'");
}
}
state Fixed {
enter {
message("You're entering into the state 'Fixed'");
}
on reopen[always] do {<definestatements>} transit to Open
in 1 day[Priority == {Critical}] do {<definestatements>} transit to Open
}
}
A simple state machine that defines allowed transitions between issue states.
State machine is based on states provided by the state machine's specified field and by the transitions between these states. More detailed description is available here.
In particular, there are :
initial state<State> — it's the start state
on'event name'.do {<statements>} transit to<State> — define the custom transition
enter {} — executed on entering to the state
exit {} — executed on exiting from the state
intime[condition] do {<statements>} transit to<State> — in the time period, if the condition is satisfied, do something and transit to the target state
As any respective programming language, the workflow language has keywords, variables, collections and iterators, and standard statements. Also there are specific to the workflow language statements, controls, methods and customs working with dates, strings and more.
All rules are worked on context of issue. Workflow cannot handle bundles changes, user registration/modification, report building, tag or saved search modifications and so on.
Variable is declared with a keyword var followed by variable name. Optionally, initial value can be specified after '=' sign, like in JavaScript language.
Code
Description
var state = State;
The simple variable assignment.
var versions; versions = Fix versions;
The simple split variable assignment.
var oldAssignee = Assignee.oldValue;
Any field has the oldValue parameter, which is the reference to the previous value of the field; the value that the field had prior the current change.
Workflow language supports two main iterators for each and while and the following predefined collections: issues, comments, tags, users, issue links, enum elements, versions, builds, ownedFields, groups, states, bundle static elements, strings.
Code
Description
for each version in Fix versions {
if (version.releaseDate < now){project.leader.notify("Overdueversion!","Theissue"+getId()+"hasoverduefixversion.");}}
Send notification to a project leader about the overdue 'Fix version'.
Any collection has the fields first, last, isEmpty, isNotEmpty and the following methods:
added — a collection of items that were added during the current transaction
removed — a collection of items that were removed during the current transaction
contains(<element>) — check whether a collection contains the specified element or not
The tags, enum[], *state[], *version[], *builds[], *groups[], *user[]* have also the add() and clear() methods.
var n = 0;
var pi = 0;
while (n < 100){if(n%2 ==0){pi =pi+1/(2*n+1);}else{pi =pi-1/(2*n+1);}n++;}pi =pi*4;
The while loop has the standard behaviour of iteration by the condition.
The example provided here shows how you can calculate the Pi number within workflow rule.
The workflow language provides several specific operators: assert, message and, of cause, the 'branch' operator.
Code
Description
assert Assignee != loggedInUser: "Oops! Only the Assignee can make changes.";
The assert statement stops the rule execution if condition is false and roll-backs all changes to the initial issue(s) state.
Workflows can be invoked by the chain. For example, one workflow rule changes issue attributes in such way that the issue starts to match another workflow rule conditions, and so on. In such case all changes made by these workflows will be roll-backed.
if (now > created + 3 days && Assignee == null) {
message("This issue has been created 3 days ago but still unassigned!");
}
The if operator has the standard branch operator behavior.
if (Type == {Feature}) {
project.leader.notify("Voted feature",
"The feature " + getId() + " has " + votes + " votes!");
} else if (Type == {Bug}) {
project.leader.notify("Duplicated bug",
"There are " + duplicatesNumber + " duplicates of the bug " + getId());
} else {
project.leader.notify("Another type",
"The " + Type.name + getId() + " just need your attention.");
}
Also there are additional else { } and else if { } operators.
There are more interesting and important issue-specific methods in the workflow language: applyCommand(), addComment(), addTag(), removeTag(), clearAttachment(), isReported(), hasTag().
Code
Description
applyCommand("for me Critical");
The method applyCommand(<command>) applies the specified by text command to the source issue.
addComment("+1!!!");
The method addComment(<comment>) adds the new comment provided by the text parameter.
addTag("todo");
The method addTag(<tag>) adds the tag provided by the text parameter.
clearAttachments();
The method clearAttachments() deletes all issue attachments.
removeTag("waiting for reply");
The method removeTag(<tag>) removes the tag provided by the text parameter.
if (!isReported() && description == null) {
description = "Welcome to YouTrack!
\nPlease describe reproduce steps below and attach screenshots if possible.";
}
isReported() is true for existing issues. The draft context is defined by !isReported() method.
var amIWatchingResolved = hasTag("Star") && isResolved();
The method hasTag(<tag>) answer whether issue has specified tag or not.
The isResolved() method is true, if all state-type fields are resolved. It there is at least one state-field in unresolved state, this method returns false.
sendMail(Reporter email, "[YouTrack, Commented]", "New comment was added: " + comments.added.first.text));
The method sendMail(<email>, <subject>, <body>) sends the letter by the specified email.
It's allowing to reference some entity statically by the construction {category: static_entity_name}, {group: group_name}, {issue: issueID}, {project: project_shortName}, {savedSearch: savedSearchName}, {tag: tagName}, {user: username}.
Code
Description
{group: QA Engineers}.notifyAllUsers("Test failed!", "Please look at the failed test " + getId())
All member of the group 'QA Engineers' will be notified.
assert {issue: IDEA-99999}.Type == {Feature}: "It should be the cool feature about the new look and feel!";
The issue 'IDEA-99999' must have the type "Feature".
for each currency in {project: Business Trips}.valuesFor(Currency) {
if (currency.name == {Dollar}) {
assert currency.colorIndex == 17: "Dollar should be green!";
}
}
The 'Currency' field values are taken for the project "Business Trips".
for each notMyIssue in loggedInUser.getIssues({savedSearch: Reported by me}, "for: -me") {
notMyIssue.Assignee = loggedInUser;
}
The loop by 'reported by current user but not assigned for it' issues.
rule Regression
when State.becomes({Reopened}) {
tags.add({tag: regression});
}
The tag 'regression' is added onto the reopening issue.
There are a lot of methods to working with strings, e.g. startsWith(), substring(), indexOf() etc. Look details on the Apache StringUtils reference.
Code
Description
var operators = "+-*/%"; var expr = "a * b + c"; var variable = ""; for each term in expr.split(" ", opts) { if (!operators.contains(term, opts)) { variable = term; } }
This piece of code parses the expression to terms.
var extractedUsername = comments.added.first.text.substringBetween("@", " "); project.getUser(extractedUsername).notify("[Youtrack, Comment notice]", "You were mentioned in the comment of issue " + "", true);
This rule extracts mentioned in comment user login from the "@username" substing and sends the notification to it.
There are number of methods which are specific to entity types project, comment, tag, group, enum filed, state, owned field, version, build, integer, period.
One can access the project properties shortName, leader, name, issues, fields, description, createdBy. The methods getUser(<name>) and valuesFor(<field>) are available as well.
Code
Description
var newIssue = project.getUser("admin").createNewIssue(project.shortName); newIssue.Assignee = project.leader; newIssue.summary = "Issue is created in the project " + project.name;
The simple way to create new issue on behalf of given user in the selected project.
for each version in project.valuesFor(Fix versions) { if (version.releaseDate > now && Fix versions.contains(version)) { message("On of the 'Fix versions' is overdue."); } }
The rule shows up the message if at least one of 'Fix versions' is overdue.
The group has properties addNewUser, allUsersGroup, description, name and methods getUsers(), notifyAllUsers().
Code
Description
var users = Assignees group.getUsers();
Gets all the group 'Assignees group' users.
permittedGroup.oldValue.notifyAllUsers("Visibity has been changed", "The visibility group for the issue " + "<a href=\"" + issue.getUrl() + "\">" + issue.getId() + "</a> has been changed to " + permittedGroup.name);
The rule notifies all the members of the issue permitted group before its changing.
The standard fields 'Priority', 'Type' have the type 'enum'. Enum fields has the properties name, description, ordinal, colorIndex and method getPresentation().
Code
Description
if (Priority.colorIndex == 19) { var priorityPresentation = "The priority '" + Priority.name + "' (" + Priority.description + ") is red."; }
The rule constructs the 'Property' field presentation text.
if (!Type.name.eq(Type.getPresentation(), opts)) { message("The " + Type.getPresentation() + " has been changed."); }
Show the message if getPresentation() is differed from the name. It seems it shouldn't appears ever :)
The field of type 'Version' has the properties archived, released, releasedDate, description, name, ordinal and getSprint(), getPresentation() methods.
The sprint cab be accessed by the version.getSprint(<project>) and has two properties start and finish.
Code
Description
var version = Fix versions.added.first; if (!version.archived && !version.released && version.releaseDate > now) { message("Current sprint start date: " + version.getSprint(project).start + "; finish date: " + version.getSprint(project).finish); }
This rule shows up the current sprint start and finish dates.
A field of the 'Integer' type inherits all standard properties as any other issue field but also behaves as the primitive type, and thus supports the following standard operations: +, -, &, /, %, comparison operations: <, <=, >, >=+, increment/decrement i++, i--, ++i, --i.
Generally, we assume that users will use text strings (notifications, messages, etc.) in one same language. That is, we assume that users create not multi-lingual workflows.
If you create a workflow rule without specific tag for localization, then text strings in such rules will be shown in the same language and will not be auto-translated with the change of the system language. For example, let's say you use Spanish localization, and you create a workflow with texts in Spanish. In this case, if you choose to switch the system language back to English, your custom workflow will still have texts in Spanish.
However, there is a way to create a multi-lingual workflow that will be auto-translated along with the UI texts and default workflows when you switch to another system language.
To implement such workflow, you should use l10n construction for your texts. Please see the example below.
Code
Description
Due Date.required("S'il vous plaît préciser la date");
This workflow shows the French text independently of the chosen system language.
l10n ( The work item automatically added by the timer. )
There is a way to localize you own workflow on different languages:
Copy any l10n( text ) entity from any of the default workflows and past it wherever needed.
Standing on the l10n open the 'Inspector panel' in the right bottom corner of Workflow Editor (Alt+2)
Create new unique id for the string and replace the existing one.
Add this key with the text string to all of the translation files that you need. For more details, please refer to the reference
In YouTrack, generally, all workflow rules are applied in the context of an issue. However, in some cases it would have been useful to apply rules in a more global context - in the context of a project, which would provide us opportunity to implement, for example, project-based reports. Luckily, we have a trick allowing us to invoke stateless/schedule rule in the project context.
The example below shows how you can implement a weekly report about unassigned issues in a project. Such report is sent on Mondays, at noon to the project's Leader.
Code
Description
schedule rule weekly report weekly on Monday at 12:00:00 [issue == {issue: A-1}] { var msg = ""; var myIssues = project.leader.getIssues({savedSearch: Unassigned in A}, ""); for each anIssue in myIssues { if (msg.isNotEmpty) { msg = msg + ", "; } msg = msg + "<a href=\"" + anIssue.getUrl() + "\">" + anIssue.getId() + "</a>"; } msg = msg + "<p style=\"color: gray;font-size: 12px;margin-top: 1em;border-top: 1px solid #D4D5D6\">Sincerely yours, YouTrack</p>"; debug(msg); project.leader.notify("Report", msg, true); }
A-1 — is a random issue that virtually implement rule's context and is not actually used in the rule. This workflow sends the weekly report containing list of all unassigned issues in the project A.