Methods and Operations
This page provides a list of the methods and operations that are supported the workflow programming language. The syntax for each method or operation is presented in the format: [name]([data type] parameter): [data type]
.
The methods and operations that are described on this page are grouped by the object type that they are related to.
Object | Methods and Operations |
---|---|
Issue | getId, getUrl, applyCommand, copy, getEditedComments, addComment, clearAttachments, hasTag, isStarred, addTag, removeTag, getDuplicateRoot, isReported, becomesReported, isResolved, becomesResolved, becomesUnresolved, getNotificationEmail, sendMail, wikify |
Field | becomes, canBeReadBy, canBeUpdatedBy, changed, oldValue, required |
CustomField | getPresentation |
ProjectField | getValuePresentation, getBackgroundColor, getForegroundColor |
IssueTag | |
SavedQuery | |
Sequence | add, added, clear, contains, first, isEmpty, isNotEmpty, last, remove, removed |
Project | |
User | getVisibleName, isBanned, hasRole, isInGroup, createNewIssue, watchIssue, unwatchIssue, getTag, getSharedTag, canVoteIssue, voteIssue, canUnvoteIssue, unvoteIssue, notify, sendMail, sendJabber, getIssues |
UserGroup |
To use a method or operation, simply add a dot after any field and write the name of the method or operation. You can continue to add methods and operations to the statement until it generates the desired condition. For example:
when comments.added.isNotEmpty
With code completion, the workflow editor displays a list of possible options that can be used with the preceding element.
Methods display the syntax and the object type they are associated with in the schema.
Operations are formatted in dark blue text in the editor and display a description of each available operation.
Issues
Changes that are applied to an issue are managed by a series of transactions. A transaction is a collection of current changes that are either saved to the database or discarded as a set. When a user edits an issue, a new transaction is started. The transaction is completed when the user clicks the Submit button. The single transaction includes all of the changes that were made to the issue.
Rules that are not executed according to a schedule are processed at the end of a transaction. Scheduled rules and scheduled blocks in state-machine rules are only processed for issues that are already reported or become reported in the current transaction.
The following methods are available for use with issues. See also Issue Fields.
getId(): string | |
---|---|
Description | Returns the issue ID. |
Example | user.notify("Issue is overdue", "Please, look at the issue: " + issue.getId()); |
getUrl(): string | |
---|---|
Description | Returns the issue URL. |
Example | user.notify("Issue is overdue", "Please, look at the issue: " + issue.getUrl()); |
applyCommand(string command, User runAs): void | ||
---|---|---|
Parameters | command | The command that is applied to the issue. |
runAs | Specifies the user by which the command is applied. If this parameter is not set, the command is applied on behalf of the current user. | |
Description | Applies a command to an issue. | |
Example | applyCommand("for me Critical") |
copy(): Issue | |
---|---|
Description | Creates a copy of the current issue. |
getEditedComments(): sequence<IssueComment> | |
---|---|
Description | Gets the comments that are edited in the current transaction. Comments that are added and removed are not considered as edited. These values are represented by the |
addComment(string text, User user): IssueComment | ||
---|---|---|
Parameters | text | The text to add to the issue as a comment. |
user | Optional. When used, adds the comment on behalf of the specified user. | |
Description | Adds a comment to the current issue. Sets the | |
Example | addComment("+1!!!"); |
clearAttachments(): void | ||
---|---|---|
Description | Remove all of the attachments from an issue. |
hasTag(string name): Boolean | ||
---|---|---|
Parameters | name | The name of the tag to search for. |
Description | Checks whether the specified tag is attached to an issue. | |
Example | hasTag("todo"); |
isStarred(): Boolean | |
---|---|
Description | Checks whether any user has added the star tag to an issue. |
addTag(string name): IssueTag | ||
---|---|---|
Parameters | name | The name of the tag to attach to the issue. |
Description | Add a tag with the specified name to an issue. YouTrack adds the first matching tag that is visible to the current user. If a match is not found, a new private tag is created for the current user. When you use this method to add the star tag to an issue, the current user is added to the list of watchers. To add a tag that is not visible to the current user, use the applyCommand method instead. Use | |
Example | addTag("todo"); |
removeTag(string name): IssueTag | ||
---|---|---|
Parameters | name | The name of the tag to remove from the issue. |
Description | Remove a tag with the specified name from an issue. If the specified tag is not attached to the issue, an exception is thrown. This method only works when the logged-in user is also the owner of the tag. To remove a tag that is owned by another user, use the applyCommand method instead. Use | |
Example | removeTag("waiting for reply"); |
getDuplicateRoot(): Issue | |
---|---|
Description | Returns the root issue in a tree of duplicates that are linked to the current issue. |
Example | when issue.duplicateCluster.changed || issue.duplicates.changed || issue.is duplicated by.changed {
info("Processing duplicate-cluster issue " + issue.getId());
var duplicateRoot = issue.getDuplicateRoot();
if (duplicateRoot != null) {
issue.duplicates.clear;
if (issue != duplicateRoot) {
issue.duplicates.add(duplicateRoot);
duplicateRoot.duplicates.clear;
}
}
} |
Issue Lifecycle
The following methods are related to the lifecycle of an issue. The lifecycle of an issue consists of the following stages:
Stage | Description |
---|---|
1. Issue is created in this transaction | The issue is still in draft form and does not have an issue ID. The default custom field values are set in this stage. Rules are not triggered during this stage. |
2. Issue is draft | Changes are made after creation but before the issue is reported. This stage includes all edits to the draft issue. Stateless and state-machine rules can be triggered during this stage. Scheduled rules and scheduled blocks in state-machine rules are not processed. |
3. Issue becomes reported in this transaction | The issue becomes reported. The issue is submitted and is assigned an ID. All rules including scheduled rules and scheduled blocks in state-machine rules are processed during this stage. |
4. Issue is reported | This stage includes all changes made to an issue after it has been reported. All rules including scheduled rules and scheduled blocks in state-machine rules are processed during this stage. |
5. Issue is deleted in this transaction | The issue is deleted and is not available after this transaction is complete. Rules are not triggered during this stage. |
isReported(): Boolean | |
---|---|
Description | Checks whether an issue is already reported or becomes reported in this transaction (stages 3 and 4). To apply changes to an issue draft (stage 2), use |
Example | for each dep in depends on {
if (dep.isReported()) {
assert dep.State.isResolved: l10n ( The issue has unresolved dependencies and thus cannot be set Fixed! );
}
} |
becomesReported(): Boolean | ||
---|---|---|
Description | Checks whether an issue becomes reported in this transaction (stage 3). | |
Example | when Assignee == null && (((Subsystem.changed || project.changed) && isReported()) || becomesReported()) {
if (issue.Subsystem != null) {
issue.Assignee = issue.Subsystem.owner;
}
} |
The following methods are related to the issue.resolved
property. This property is set based on the values that are stored in a custom field with a state
data type. Each value that can be stored in this field has a Resolved property. This property determines whether the issue is considered to be resolved when it assigned a value for this field.
The resolved
property is associated with the issue, which means that these methods do not contain references to the custom field.
isResolved(): Boolean | |
---|---|
Description | Checks whether an issue is assigned a state that is considered resolved. |
Example | when isResolved() {
assert !votes.changed: l10n ( Voting for a resolved issue is not allowed. );
} |
becomesResolved(): Boolean | |
---|---|
Description | Checks whether an issue is assigned a state that is considered resolved in the current transaction. |
Example | when issue.isReported() && issue.becomesResolved() && issue.subtask of.isNotEmpty {
var parent = issue.subtask of.first;
while (parent != null && !parent.isResolved()){
var allSubtasksResolved = true;
for each subtask in parent.parent for {
if (!subtask.State.isResolved) {
allSubtasksResolved = false;
break;
}
}
if (allSubtasksResolved) {
parent.State = {Done};
message(l10n ( Automatically set{parent.getId()} as Done));
}
parent = parent.subtask of.first;
}
} |
becomesUnresolved(): Boolean | |
---|---|
Description | Checks whether an issue is assigned a state that is considered unresolved in the current transaction. |
Example | when issue.becomesUnresolved() && issue.subtask of.isNotEmpty {
var parent = issue.subtask of.first;
while (parent != null && !(parent.project.isArchived()) && parent.isResolved()) {
parent.State = {Open};
if (parent.Type != null) {
message(l10n ( Automatically reopen {parent.Type} {parent.getId()} ));
} else {
message(l10n ( Automatically reopen {parent.getId()} ));
}
parent = parent.subtask of.first;
}
} |
Notifications
These methods can be used to send notifications to unregistered users with an email address. To send notifications to existing users, use the notify, notifyAllUsers, sendMail, and sendJabber methods.
If overused, sending email messages to unregistered users can slow down the performance of your YouTrack server, which is not designed for use as a bulk email service. You can configure a system property to set a daily email message limit. For more information, see Configuration Parameters.
getNotificationEmail(): string | |
---|---|
Description | Returns the email address that is used to send notifications for the project. If a From address is not set for the project, the From address for the YouTrack server is returned. |
Example | when issue.becomesReported() {
if (Last message related emails.isNotEmpty) {
for each email in Last message related emails.split(" ", preserveAllTokens) {
if (email.isNotBlank && !(email.eq(getNotificationEmail(), ignoreCase))) {
if (All related emails.isEmpty) {
All related emails = email;
} else if (!(All related emails.split(" ", preserveAllTokens).contains(email))) {
All related emails = All related emails + " " + email;
}
}
}
Last message related emails = null;
}
} |
sendMail(string fromPersonal, string fromEmail, string email, string cc, string subject, string body): void | ||
---|---|---|
Parameters | fromPersonal | The sender of the email message. If this parameter is not set, the project From address is used. If a From address is not set for the project, the From address for the YouTrack server is used. |
fromEmail | ||
email | The email address of the primary recipient. | |
cc | The email addresses of additional recipients who receive a copy of the message. Multiple email addresses are delimited with commas. If this parameter is not set, the message is sent to a single recipient. | |
subject | The subject line of the email message | |
body | The email message text. | |
Description | Sends an email message with the specified parameters. | |
Example | when comments.added.isNotEmpty {
if (Reporter email != null) {
sendMail(Reporter email, "[YouTrack, Commented]", "New comment was added: " + comments.added.first.text));
}
} |
wikify(string text): string | ||
---|---|---|
Parameters | text | The string of text to convert to HTML. |
Description | Converts text with wiki markup to HTML. Use this method to send "pretty" notifications to unregistered users. | |
Example | when comments.added.isNotEmpty {
for each comment in comments.added {
sendMail("myuser@example.com", "Issue is commented", wikify(comment.text));
}
} |
Fields
The following operations are available for all fields.
becomes([field] value): Boolean | ||
---|---|---|
Parameters | value | The value to check for the specified field. |
Description | Checks whether a specific value is set for a field in the current transaction. | |
Example | when State == {Verified} && !State.becomes({Verified}) {
assert comments.added.isEmpty: l10n ( Commenting for fixed and verified issues is disabled. );
} |
canBeReadBy(User user): Boolean | ||
---|---|---|
Parameters | user | The user for whom the permission to read the field is checked. |
Description | Checks whether a specific user has permission to read the field. |
canBeUpdatedBy(User user): Boolean | ||
---|---|---|
Parameters | user | The user for whom the permission to update the field is checked. |
Description | Checks whether a specific user has permission to update the field. | |
Example | when duplicates.added.isNotEmpty && State != {Duplicate} {
if (State.canBeUpdatedBy(loggedInUser)) {
State = {Duplicate};
}
} |
changed(): Boolean | ||
---|---|---|
Description | Checks whether a the value of the field is changed in the current transaction. | |
Example | for each comment in comments.added {
text = text + " " + comment.text;
}
if (description.changed || becomesReported()) {
text = text + " " + description;
}
if (summary.changed || becomesReported()) {
text = text + " " + summary;
} |
oldValue(): [field type] | ||
---|---|---|
Description | Returns the previous value of the specified field before an update was applied. | |
Example | when State == {Verified} && !State.becomes({Verified}) {
assert comments.added.isEmpty: l10n ( Commenting for fixed and verified issues is disabled. );
} |
required(string message): void | ||
---|---|---|
Parameters | message | The message that is displayed to the user that describes the field requirement. |
Description | Asserts that a value is set for a field. If a value for the required field is not set, the field is highlighted in the issue and the specified message is displayed. | |
Example | state Approved {
enter {
Assignee.required("Please select an assignee");
} |
CustomFields
The following method is available for CustomField
types. See also CustomField Fields.
getPresentation(): string | |
---|---|
Description | Returns the presentation of the value for a custom field. |
Example | if (!Type.name.eq(Type.getPresentation(), opts)) {
message("The " + Type.getPresentation() + " has been changed.");
} |
ProjectFields
The following methods are available for ProjectField
types. See also ProjectField Fields.
getValuePresentation(Issue issue): string | ||
---|---|---|
Parameters | issue | The issue for which the value presentation is returned. |
Description | Returns the string presentation of the value that is used for this field in the specified issue. |
getBackgroundColor(Issue issue): string | ||
---|---|---|
Parameters | issue | The issue for which the background color is returned. |
Description | Returns the background color that is used for this field value in the specified issue. Can return |
getForegroundColor(Issue issue): string | ||
---|---|---|
Parameters | issue | The issue for which the foreground color is returned. |
Description | Returns the foreground color that is used for this field value in the specified issue. Can return |
IssueTag
The following methods are available for IssueTag types. You can specify a tag with the literal reference {tag: [tagName]}
. See also IssueTag Fields.
getShareGroup(): UserGroup | |
---|---|
Description | Returns the |
Example | var visible = tag.getShareGroup(); |
getUpdateShareGroup(): UserGroup | |
---|---|
Description | Returns the |
Example | var updatable = tag.getUpdateShareGroup(); |
SavedQuery
The following methods are available for SavedQuery types. You can specify a saved search with the literal reference {savedSearch: [savedSearchName]}
. See also IssueTag Fields.
getOwner(): User | |
---|---|
Description | Returns the |
Example | var owner = {savedSearch: IDEA Backlog}.getOwner(); |
getShareGroup(): UserGroup | |
---|---|
Description | Returns the |
Example | var visible = {savedSearch: IDEA Backlog}.getShareGroup(); |
getUpdateShareGroup(): UserGroup | |
---|---|
Description | Returns the |
Example | var updatable = {savedSearch: IDEA Backlog}.getUpdateShareGroup(); |
Sequences
The workflow programming language contains a set of predefined sequences. These sequences include issues, comments, tags, users, issue links, enumerated custom fields, versions, builds, ownedFields, groups, states, static elements for sets of values, and strings. The workflow language does not let you define your own sequence of elements.
When you reference a sequence, you can look up the values that are stored in a specific field for each element. For example, issue.comments
returns a sequence of IssueComment
elements. You can find the first comment that was added to the issue with the operations added
and first
. You can then access fields for the first IssueComment
, such as text
or author
. A statement that returns this field would look something like this:
if (comments.added.isNotEmpty) {
text = comments.added.first.text;
You can also iterate over the elements in a sequence with a for each
statement. For example:
for each version in Fix versions {
if (version.releaseDate < now) {
project.leader.notify("Overdue version!", "The issue " + getId()+ " has overdue fix version.");
}
}
The following operations are available for use with all sequence types.
add([sequence type] element): [element type] | ||
---|---|---|
Parameters | element | The element to add to the sequence. |
Description | Adds an element to the specified sequence. | |
Example | Fix versions.add({Backlog}); |
added(): sequence<[element type]> | |
---|---|
Description | Returns a sequence of elements that were added to the sequence during the current transaction. |
Example | when State == {Verified} && !State.becomes({Verified}) {
assert comments.added.isEmpty: l10n ( Commenting for fixed and verified issues is disabled. );
} |
clear(): void | ||
---|---|---|
Description | Removes all of the elements in the sequence. | |
Example | relates to.clear; |
contains([sequence type] element): Boolean | ||
---|---|---|
Parameters | element | The element to check for in the sequence. |
Description | Checks whether a sequence contains a specific element. | |
Example | if (Fix versions.contains({2017.2})){
Priority = {Critical};
} |
first(): [element type] | |
---|---|
Description | The first element of the sequence. If the sequence is empty, a null value is returned. |
Example | when comments.added.isNotEmpty {
if (All related emails.isNotEmpty){
var myComment = comments.added.first; |
isEmpty(): Boolelan | |
---|---|
Description | True if the sequence is empty. |
Example | when State == {Duplicate} && duplicates.removed.isNotEmpty {
if (duplicates.isEmpty) {
State = {Open};
}
} |
isNotEmpty(): Boolelan | |
---|---|
Description | True if the sequence is not empty. |
Example | when parent for.added.isNotEmpty {
for each subtask in parent for.added {
if (subtask.Assignee == null) {
subtask.Assignee = Assignee;
}
}
} |
last(): [element type] | |
---|---|
Description | The last element of the sequence. If the sequence is empty, a null value is returned. |
Example | if (curNumber.is(numeric)) {
referringIssue = loggedInUser.getIssues(project, "#" + issueID);
if (referringIssue.isNotEmpty && referringIssue.first == referringIssue.last) {
refers to.add(referringIssue.first); |
remove([sequence type] element): [element type] | ||
---|---|---|
Parameters | element | The element to remove from the sequence. |
Description | Removes an element from the specified sequence. | |
Example | Fix versions.remove({Next Release}); |
removed() sequence<[element type]> | ||
---|---|---|
Description | Returns a sequence of elements that were removed from the sequence during the current transaction. | |
Example | var tag = {tag:confirmed};
tags.remove(tag);
if (tags.removed.contains(tag)) {
message(l10n ( Tag ' {tag.name} ' is removed.)); |
Projects
The following methods are available for use with projects. See also Project Fields.
getUser(string login): User | ||
---|---|---|
Parameters | login | The username of the user. |
Description | Returns a | |
Example | if (created < 2012-12-31 && !isResolved()) {
tags.add(project.getUser("root").getSharedTag("obsolete?"));
} |
isArchived(): Boolean | |
---|---|
Description | Checks the current status of a project. |
Example | when Assignee.changed {
for each subtask in parent for {
if (subtask.project.isArchived()) {
continue;
}
if (subtask.Assignee == Assignee.oldValue) {
subtask.Assignee = Assignee;
}
}
} |
The following operation is available for use with projects.
valuesFor(CustomField field): sequence<[field type]> | ||
---|---|---|
Parameters | field | The name of the custom field for which values are returned. |
Description | Returns a read-only sequence of values that are used by the custom field in this project. | |
Example | when parent for.added.isNotEmpty {
for each subtask in parent for.added {
if (project != subtask.project && issue.project.valuesFor(Fix versions).first != subtask.project.valuesFor(Fix versions).first) {
continue;
}
if (!subtask.isResolved()) {
subtask.Fix versions.clear;
for each version in Fix versions {
subtask.Fix versions.add(version);
}
}
}
} |
Users
The following methods are available for use with User
objects. See also User Fields.
getVisibleName(): string | |
---|---|
Description | Returns the full name of the specified user. If the full name is not set, the username is returned. |
isBanned(): Boolean | |
---|---|
Description | Checks whether the user account is banned. |
Example | if (Assignee.isBanned()) {
Assignee = null;
} |
hasRole(string role): Boolean | ||
---|---|---|
Parameters | role | The name of role you want to check for. |
Description | Checks whether the user has the specified role in any project. | |
Example | if (loggedInUser.hasRole("Developer")) {
Assignee = loggedInUser;
} |
isInGroup(): string | |
---|---|
Description | Checks whether the user is a member of the specified group. |
Example | when Assignee.changed && Assignee != null {
if (permittedGroup != null && !Assignee.isInGroup(permittedGroup.name)) {
message(l10n ( Please take into account that new assignee ' {Assignee.fullName} ' isn't included into the visibility group ' {permittedGroup.name} '!));
}
} |
createNewIssue(string projectShortName): Issue | ||
---|---|---|
Parameters | projectShortName | The ID of the project in which the issue is created. |
Description | Creates a new issue in the specified project. | |
Example | var todoIssue = loggedInUser.createNewIssue(project.shortName);
todoIssue.summary = "iPhone :" + issue.summary;
todoIssue.tags.add(iOSTag);
tags.add(iOSTag); |
watchIssue(Issue issue): void | ||
---|---|---|
Parameters | issue | The issue to which the user is added as a watcher. |
Description | Adds the current user to the issue as a watcher (add | |
Example | if (loggedInUser.isInGroup("developers") && loggedInUser.hasRole("Developer")) {
loggedInUser.watchIssue(issue);
} else {
loggedInUser.unwatchIssue(issue);
} |
unwatchIssue(Issue issue): void | ||
---|---|---|
Parameters | issue | The issue to from which the user is removed as a watcher. |
Description | Removes the current user from the list of watchers for the issue (remove | |
Example | if (loggedInUser.isInGroup("developers") && loggedInUser.hasRole("Developer")) {
loggedInUser.watchIssue(issue);
} else {
loggedInUser.unwatchIssue(issue);
} |
getTag(string tagName, Boolean createIfNotExists): IssueTag | ||
---|---|---|
Parameters | tagName | The name of the tag. |
createIfNotExists | If | |
Description | Returns a tag that is visible to the user. | |
Example | var createIfNotExist = loggedInUser.getSharedTag("iOS") == null;
var iOSTag = loggedInUser.getTag("iOS", createIfNotExist); |
getSharedTag(string tagName): IssueTag | ||
---|---|---|
Parameters | tagName | The name of the tag. |
Description | Returns a tag with the specified name that is shared with but not owned by the user. If such a tag does not exist, a null value is returned. | |
Example | var createIfNotExist = loggedInUser.getSharedTag("iOS") == null;
var iOSTag = loggedInUser.getTag("iOS", createIfNotExist); |
canVoteIssue(Issue issue): Boolean | |
---|---|
Description | Checks whether the user is able to vote for the specified issue. |
Example | var user = project.getUser("user");
if (user.canVoteIssue(issue)) {
user.voteIssue(issue);
} |
voteIssue(Issue issue): void | ||
---|---|---|
Parameters | issue | The issue to which the vote is added. |
Description | Adds a vote on behalf of the user to the issue, if allowed. | |
Example | >var user = project.getUser("user");
if (user.canVoteIssue(issue)) {
user.voteIssue(issue);
} |
canUnvoteIssue(Issue issue): Boolean | |
---|---|
Description | Checks whether the user is able to remove their vote from the specified issue. |
unvoteIssue(Issue issue): void | ||
---|---|---|
Parameters | issue | The issue from which the vote is removed. |
Description | Removes a vote on behalf of the user from the issue, if allowed |
notify(string subject, string body, Boolean ignoreNotifyOnOwnChangesSetting) | ||
---|---|---|
Parameters | subject | The subject line of the email notification. |
body | The message text of the email notification. | |
ignoreNotifyOnOwnChangesSetting | If | |
Description | Sends an email notification to the email address that is set in the user profile. | |
Example | var projectLeader = project.leader;
projectLeader.notify("Attention","Please pay attention to " + getId());
projectLeader.sendJabber("Please look at " + getId()); |
sendMail(string subject, string body): void | ||
---|---|---|
Parameters | subject | The subject line text of the email notification. |
body | The message text of the email notification. | |
Description | An alias for | |
Example | sendMail(Reporter email, "[YouTrack, Commented]", "New comment was added: " + comments.added.first.text)); |
sendJabber(string text): void | ||
---|---|---|
Parameters | text | The message text for the Jabber notification. |
Description | Sends a notification message over Jabber. Similar to | |
Example | project.leader.sendJabber("Please pay attention to " + getId()); |
The following operation is available for use with User
objects.
getIssues(SavedQuery context, string query): sequence<Issue> | ||
---|---|---|
Parameters | context | The name of the saved search to use as the context for the search query. The default context is |
query | The search query. Can be an empty string. | |
Description | Returns a list of issues based on the specified context and search query. | |
Example | for each notMyIssue in loggedInUser.getIssues({savedSearch: Reported by me}, "for: -me") {
notMyIssue.Assignee = loggedInUser;
} | |
Variations | var issues = project.leader.getIssues(Everything, "");
var issues = project.leader.getIssues({savedSearch: Unassigned in A}, "");
var issues = project.leader.getIssues({savedSearch: Unassigned in A}, "Priority: Critical"); |
UserGroups
The following methods are available for use with UserGroups. See also UserGroup Fields.
Description | Returns all of the users who are members of the specified group. |
Example | var users = Assignees group.getUsers(); |
notifyAllUsers(string subject, string body): void | ||
---|---|---|
Parameters | subject | The subject line of the email notification. |
body | The message text of the email notification. | |
Description | Sends an email notification to all of the users who are members of the group. | |
Example | permittedGroup.oldValue.notifyAllUsers("Visibility has been changed",
"The visibility group for the issue "
+ "<a href=\"" + issue.getUrl() + "\">" + issue.getId()
+ "</a> has been changed to " + permittedGroup.name); |