|
Key
This line was removed.
This word was removed. This word was added.
This line was added.
|
Comment:
Changes (3)
View Page Historyh2. Workflow Repository
{table:class=confluenceTable|width=100%}
{table:class=confluenceTable|width=100%}
{th:class=confluenceTh|width=50%}Description{th}
{th:class=confluenceTh|width=50%}Code{th}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}
h4. Votes and watchers {tip}[^jetbrains-youtrack-comments.zip]{tip}{td}
{th:class=confluenceTh|width=50%}Code{th}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}
h4. Votes and watchers {tip}[^jetbrains-youtrack-comments.zip]{tip}{td}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{anchor:Votes and watchers} *Votes and watchers* [^jetbrains-youtrack-comments.zip]{td}
{tr}
{td:class=confluenceTd}Adds a user to an issue watchers list, depending on the number of its votes.{td}
{td:class=confluenceTd}{code}rule watch if lots of votes
when votes == 10 {
project.leader.watchIssue(issue);
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Default visibility group*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Sets default visibility group to all new issues in project.{td}
{td:class=confluenceTd}{code}rule set permitted group for new issues
when !isReported() && reporter.isInGroup("Reporters") {
permittedGroup = {developers};
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Notify about no comments added in a time period*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}If issue is not commented by a any member of a certain group (developers-team) during specific time period, send a notification to a specific user (support team member). {td}
{td:class=confluenceTd}{code}schedule rule Notify on no comments during time period
daily at 12 : 00 : 00 [!issue.State.isResolved] {
if (comments.last != null && !comments.last.author.isInGroup("developers-team")) {
project.leader.notify("[Youtrack, Notifier] Issue " +
getId() + " need attention",
"Issue " + summary + " has last comment not from developers-team group member.");
}
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Reopen if no duplicates*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Reopens an issue if all duplicate links are removed.{td}
{td:class=confluenceTd}{code}rule Reopen issue on removing last duplicates link
when duplicates.changed && duplicates.isEmpty {
State = {Open};
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Clear fix for version on reopen*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Clears 'Fix for version' field when an issue is reopened.{td}
{td:class=confluenceTd}{code}rule Clear fix for version on issue reopen
when State.oldValue != null && State.oldValue.isResolved && !State.isResolved {
Fix versions.clear;
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}*'Unblocked' state when no blockers*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Provide an ability to have the issue unblocked (denoted with a "is blocked by" - "blocks" relationship) when single blocker is resolved and issue has no remaining blockers. \(?){td}
{td:class=confluenceTd}{code}rule unblock on blockers resolving
when issue.State.isResolved && !issue.State.oldValue.isResolved {
for each dependant in issue.blocks {
if (dependant.is blocked by.first == dependant.is blocked by.last) { //e.g. it's source issue
dependant.State = {Unblocked};
}
}
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Semi-automatic 'Wait for reply' state management*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}When a comment from an external user is required, internal user sets issue state to 'Wait for reply' manually. When comment is added, issue is automatically reopened. Assignee gets a notification, if comment is not added within 5 days. {td}
{td:class=confluenceTd}Statemachine with 'Waiting for reply' state: {code}statemachine States for field State {
initial state Submitted {
on wait[always] do {<define statements>} transit to Wait for Reply
...
}
state Open {
on wait[always] do {<define statements>} transit to Wait for Reply
...
}
state Wait for Reply {
on fix[always] do {<define statements>} transit to Fixed
on open[always] do {<define statements>} transit to Open
on in progress[always] do {<define statements>} transit to In Progress
...
}
}{code}
Changes issue state from 'Wait for reply' to 'Open' when comment is added by an external user:
{expand:title=Click to see code}
{code}
rule Wait for reply: reopen on answer
when State == {Wait for Reply} && comments.added != null && comments.added.isNotEmpty &&
comments.added.last != null && !comments.added.last.author.isInGroup("developers") {
State = {Open};
}
{code}
{expand}
Notify assignee if no answer is received within 5 days:
{expand:title=Click to see code}
{code}
schedule rule Wait for reply reminder
daily at 12 : 05 : 00 [issue.State == {Wait for Reply}] {
var lastComment = comments.last;
if (lastComment != null && lastComment.author.isInGroup("developers")
&& lastComment.created + 5 days < now) {
if (Assignee != null) {
Assignee.notify("[Youtrack, 'Wait for reply' reminder] Unanswered comment within 5 days",
"Hi, " + Assignee.fullName + "! <br><br> Issue <a href=\"" + getUrl() + "\">" + getId()
+ " " + summary + "</a>" + " is in state \"Wait for reply\" more than 5 days."
+ " It has unanswered comment:<br><b>" + lastComment.text
+ "</b><br><br> Please answer or resolved issue.<br><br>"
+ "<p style=\"color: gray;font-size: 12px;margin-top: 1em;border-top: 1px solid #D4D5D6\">"
+ "Sincerely yours, YouTrack" + "</p>");
}
}
}
{code}
{expand}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Remind mentioned in comment user about pending comment*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Send notification to a user, mentioned in comment.{td}
{td:class=confluenceTd}{code}schedule rule Set day of week
daily at 12 : 00 : 00 [for all issues] {
var mentionedUser;
for each comment in comments {
if (comment.text.contains("Remind", opts) && comment.created + 1 day > now) {
// best way to extract target username. All extended string method are available
}
}
for each user in permittedGroup.getUsers() { *concrete group iteration
if (user.login == mentionedUser) {
user.notify("Reminder", "You are reminded because of "+ getId());
}
}
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Reopen on stop duplicate*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Reopen an issue when the last link to duplicate issue is removed. {td}
{td:class=confluenceTd}{code}rule Reopen issue on removing last duplicates link
when duplicates.changed && duplicates.isEmpty {
State = {Open};
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Default visibility group*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Set default visibility group for all new issues in a project.{td}
{td:class=confluenceTd}{code}rule set permitted group for new issues
when !isReported() && reporter.isInGroup("Reporters") {
permittedGroup = {mygroup};
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Manage tag 'Waiting for reply'*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd} Replace 'waiting for reply' tag with 'answered' when comment from developer is added.{td}
{td:class=confluenceTd}{code}rule Set tag waiting for reply (answered)
when comments.added.isNotEmpty {
if (!comments.added.last.author.isInGroup("developers")) {
removeTag("waiting for reply");
addTag("waiting for reply (answered?)");
}
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Display the last submitted date for duplicated exceptions*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd} Show the last submitted date for the main issue, when a new 'duplicated by' link is added. \(?) {td}
{td:class=confluenceTd}{code}rule Display last submitted date for duplicated exceptions
when issue.is duplicated by.added.isNotEmpty {
var lastDuplicate = issue.Last duplicate;
for each source in issue.is duplicated by.added {
var addedDuplicate = source.created;
if (lastDuplicate < addedDuplicate) {
lastDuplicate = addedDuplicate;
issue.Last duplicate = lastDuplicate;
}
}
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Restrictions on field changing*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd} Make some fields editable for issue assignee only. Display an error message to any other user on attempt to modify the fields. {td}
{td:class=confluenceTd}{code}rule assert assignee can change field
when Priority.changed || Fix versions.changed || Fixed in build.changed || Resolution.changed {
assert loggedInUser == Assignee: "Only an Issue Assignee can set with field";
}
{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Duplicated issues visibility group*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Prohibit to duplicate issues with different visibility groups{td}
{td:class=confluenceTd}{code}rule Different duplicates issues visibility
when issue.duplicates.added.isNotEmpty {
for each duplicate in issue.duplicates.added {
assert permittedGroup == duplicate.permittedGroup:
"Try to duplicate issue with different visibility";
}
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Set assignee on open issue*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Open issue on specifying Assignee{td}
{td:class=confluenceTd}{code}rule Open issue on set Assignee
when Assignee != null && Assignee.changed && Assignee.oldValue == null {
State = {Open};
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Testers process*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Introduces new:
\\
* field 'Verify by' user of type 'user'
* field 'Verified in build' of type 'build'
* value 'W/O verification in bundle 'State'
\\
Workflows notifies user if it's set as 'Verify by' user, requires verified in build on change state to 'Verified', automatically sets 'Verified in user', requires comments on change state to 'W/O verification'
\\{td}
{td:class=confluenceTd}{code}rule Notify on set Verify by user
when isReported() && Verified by.changed {
if (Verified by != null && loggedInUser != Verified by) {
Verified by.notify("[Youtrack, Verify by] You became verified by user of issue "
+ issue.getId() + " " + issue.summary, "Hi, " + Verified by.fullName +
"! <br><br> You became the verified by user of issue <a href=\"" + issue.getUrl()
+ "\">" + issue.getId() + "</a>" + " " + " <a href=\"" + issue.getUrl() + "\">"
+ issue.summary + "</a>" + "<br> Please verify it or close as \"W/O verification\".<br><br>"
+ "<p style=\"color: gray;font-size: 12px;margin-top: 1em;border-top: 1px solid #D4D5D6\">"
+ "Sincerely yours, YouTrack" + "</p>");
}
}{code}
Set verified in build.
{expand:title=Click to see code}
{code}rule Set Verified State on specifying Verified In Build
when Verified in build != null && State != {Verified} {
State = {Verified};
}{code}
{expand}
Set verified by user on specifying Verified in build:
{expand:title=Click to see code}
{code}
rule Set verified by user on specifying Verified in build
when Verified in build != null && Verified in build.changed {
Verified by = loggedInUser;
}{code}
{expand}
States machine:
{expand:title=Click to see code}
{code}
...
state W/O verification {
enter {
Verified by = loggedInUser;
}
on reopen[always] do {<define statements>} transit to Open
}
state Verified {
on reopen[always] do {<define statements>} transit to Open
enter {
Verified in build.required("Specify verified in build");
Verified by = loggedInUser;
}
exit {
Verified in build = null;
}
}
...
{code}
{expand}
Require adding a new comment when changing an issue state.
{expand:title=Click to see code}
{code}rule Require comment on w/o verification
when State.becomes({W/O verification}) {
assert comments.added.isNotEmpty: "Add a comment about the missing details";
}{code}
{expand}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Advanced states machine*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Manage states transitions.{td}
{td:class=confluenceTd}
{expand:title=Click to see code}
{code}statemachine States for field State {
initial state Submitted {
in 3 days[State == {Submitted}] do {
var user = Assignee;
if (user == null) {
user = project.leader;
}
user.notify("[Youtrack, State reminder] Issue " + issue.getId()
+ " is still Submitted: " + issue.summary, "Hi, " + user.fullName
+ "! <br><br> Issue <a href=\"" + issue.getUrl() + "\">" + issue.getId()
+ "</a>" + " is still in Submitted state:" + " <a href=\"" + issue.getUrl()
+ "\">" + issue.summary + "</a>"
+ "<br> Please start working on it or specify other accurate state.<br><br>"
+ "<p style=\"color: gray;font-size: 12px;margin-top: 1em;border-top: 1px solid #D4D5D6\">"
+ "Sincerely yours, YouTrack" + "</p>");
}
on fix[always] do {<define statements>} transit to Fixed
on open[always] do {<define statements>} transit to Open
on in progress[always] do {<define statements>} transit to In Progress
on discuss[always] do {<define statements>} transit to To be discussed
on can't reproduce[always] do {<define statements>} transit to Can't Reproduce
on obsolete[always] do {<define statements>} transit to Obsolete
on duplicate[always] do {<define statements>} transit to Duplicate
on as designed[always] do {<define statements>} transit to As designed
on invalidate[always] do {<define statements>} transit to Invalid
}
state Open {
on in progress[always] do {<define statements>} transit to In Progress
on discuss[always] do {<define statements>} transit to To be discussed
on fix[always] do {<define statements>} transit to Fixed
on obsolete[always] do {<define statements>} transit to Obsolete
on duplicate[always] do {<define statements>} transit to Duplicate
on can't reproduce[always] do {<define statements>} transit to Can't Reproduce
on as designed[always] do {<define statements>} transit to As designed
on invalid[always] do {<define statements>} transit to Invalid
on wait[always] do {<define statements>} transit to Wait for Reply
}
state Reopened {
on open[always] do {<define statements>} transit to Open
}
state Obsolete {
on reopen[always] do {<define statements>} transit to Open
}
state Duplicate {
on reopen[always] do {<define statements>} transit to Open
}
state In Progress {
on reopen[always] do {<define statements>} transit to Open
on fix[always] do {<define statements>} transit to Fixed
on can't reproduce[always] do {<define statements>} transit to Can't Reproduce
on obsolete[always] do {<define statements>} transit to Obsolete
on as designed[always] do {<define statements>} transit to As designed
}
state To be discussed {
on in progress[always] do {<define statements>} transit to In Progress
on duplicate[always] do {<define statements>} transit to Duplicate
on obsolete[always] do {<define statements>} transit to Obsolete
}
state Can't Reproduce {
on reopen[always] do {<define statements>} transit to Open
}
state As designed {
on reopen[always] do {<define statements>} transit to Open
}
state Won't fix {
on reopen[always] do {<define statements>} transit to Open
}
state Invalid {
on reopen[always] do {<define statements>} transit to Open
}
state Incomplete {
on reopen[always] do {<define statements>} transit to Open
}
state Fixed {
on reopen[always] do {<define statements>} transit to Open
on verify[always] do {<define statements>} transit to Verified
on can't verify[always] do {<define statements>} transit to W/O verification
}
state W/O verification {
enter {
Verified by = loggedInUser;
}
on reopen[always] do {<define statements>} transit to Open
}
state Verified {
on reopen[always] do {<define statements>} transit to Open
enter {
Verified in build.required("Specify verified in build");
Verified by = loggedInUser;
}
exit {
Verified in build = null;
}
}
state Wait for Reply {
on fix[always] do {<define statements>} transit to Fixed
on open[always] do {<define statements>} transit to Open
on in progress[always] do {<define statements>} transit to In Progress
on discuss[always] do {<define statements>} transit to To be discussed
on can't reproduce[always] do {<define statements>} transit to Can't Reproduce
on obsolete[always] do {<define statements>} transit to Obsolete
on duplicate[always] do {<define statements>} transit to Duplicate
on as designed[always] do {<define statements>} transit to As designed
on invalidate[always] do {<define statements>} transit to Invalid
}
}{code}
{expand}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Duty*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Manage duties. Requires a separate project to store duties, requires states:'On duty, 'Active', 'On a rest', new 'integer' field 'day of week', duty is store in 'Assignee' field.
\\
Chain of duties is implemented as linked by 'is required for' linkage issues.
\\ {td}
{td:class=confluenceTd}
{expand:title=Click to see code}
{code}schedule rule Set Today's Duty Based on is required for
daily at 10 : 00 : 00 [issue.State == {On duty} && issue.Day of week != 5 && issue.Day of week != 6] {
if (updated + 10 seconds < now) {
// set new duty
var nextDuty = is required for.first;
var stop = 0;
if (nextDuty == null || nextDuty.Assignee == null) {
project.leader.notify("Duty",
"Duties chain should be provided by 'is required for' link and 'Duty' store field must be non-empty.",
true);
}
while (stop != -1 && stop < 50) {
if (nextDuty.State == {Active}) {
nextDuty.Day of week = Day of week;
nextDuty.State = {On duty};
Assignee.notify("Duty: You are no longer on duty", Assignee.fullName + ", "
+ " you just came off duty.<br><br>The new man-on-duty is "
+ nextDuty.Assignee.fullName + ".", true);
State = {Active};
debug("Duty changed to: " + nextDuty.Assignee.fullName);
stop = -1;
} else if (nextDuty.State == {On a rest}) {
nextDuty = nextDuty.is required for.first;
stop = stop + 1;
} else {
project.leader.notify("Duty", "States should be only 'Active' or 'On a rest'", true);
stop = -1;
}
}
nextDuty.Assignee.notify("Duty: You are on duty", nextDuty.Assignee.fullName + ", "
+ "you are on duty today.<br><br> Don't forget to handle "
+ "<a href=\"http://youtrack.jetbrains.net/issues/JT?"
+ "q=%23Unassigned+%23Unresolved+created%3A+Yesterday%2C+Today+sort+by%3A+Priority+\">"
+ "new issues in Youtrack." + "</a>" + "<br>"
+ "<p style=\"color: gray;font-size: 12px;margin-top: 1em;border-top: 1px solid #D4D5D6\">"
+ "Sincerely yours, YouTrack" + "</p>", true);
var curIssue = nextDuty;
var preventInfinite = 0;
while (curIssue != issue && preventInfinite < 50) {
if (curIssue.State == {Active}) {
assert curIssue.Assignee != null: "Assignee must me not null!";
curIssue.Assignee.notify("Duty: Man-on-duty was changed", "Today's man-on-duty is "
+ nextDuty.Assignee.fullName + ".", true);
}
curIssue = curIssue.is required for.first;
preventInfinite = preventInfinite + 1;
}
}
}{code}
{expand}
Manage day of week:
{code}schedule rule Set day of week
daily at 00 : 00 : 00 [issue.State == {On duty}] {
Day of week = (Day of week + 1) % 7;
}{code}
Prohibit to drop Assignee:
{code}rule Assert Assignee not null
when Assignee.changed {
assert Assignee != null: "Assignee can't be null!";
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Warn on issue visibility changed*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Warn on change issue visibility{td}
{td:class=confluenceTd}{code}rule issueVisibility
when permittedGroup.changed {
var msg = "Please make issue public and add protected attachments and comments to post non-public data";
assert loggedInUser.isInGroup("jetbrains-team"): msg;
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Assignee and multiple subsystems*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Extract single assignee of several subsystems{td}
{td:class=confluenceTd}{code}rule Set assignee from Subsystems
when Subsystems.changed {
var owner = null;
for each subsystem in Subsystems {
if (owner == null) {
owner = subsystem.owner;
} else if (subsystem.owner != owner) {
owner = null;
break;
}
}
if (owner != null && Assignee == null) {
Assignee = owner;
}
}{code}
{td}
{tr}
{tr:class=confluenceTr}{td:colspan=2|class=confluenceTd}{*}Feedback management*, distributive{td}
{tr}
{tr:class=confluenceTr}
{td:class=confluenceTd}Handle feedback getting from mailbox {td}
{td:class=confluenceTd}Set state 'Answered' if new comment is posted: {code}rule issue is answered if new comment posted
when comments.added.isNotEmpty {
if (comments.last.author.isInGroup("youtrack-developers")) {
State = {Answered};
} else {
State = {Unanswered};
}
}{code}
Issue becomes answered if new comment posted.
{expand:title=Click to see code}
{code}rule issue is answered if new comment posted
when comments.added.isNotEmpty {
if (comments.last.author.isInGroup("youtrack-developers")) {
State = {Answered};
} else {
State = {Unanswered};
}
}{code}
{expand}
SPAM issues become answered:
{expand:title=Click to see code}
{code}rule resolve on mark as SPAM
when Type.becomes({Spam}) {
State = {Answered};
}{code}
{expand}
Last developer answer sets assignee.
{expand:title=Click to see code}
{code}rule set assignee on developer comment
when comments.added.isNotEmpty && comments.added.last.author.isInGroup("youtrack-developers")
&& comments.added.last.author != Assignee {
Assignee = comments.added.last.author;
}
{code}
{expand}
{td}
{tr}
{table}
h2. Default Workflows
This table contains workflows included into YouTrack distributive by default.
|| Name || Description || Code || Workflow Distributive ||
| Additional Notifications | Notify previous assignee when issue is reassigned | {code}rule Notify on issue reassign
when isReported() && Assignee.changed {
var oldAssignee = Assignee.oldValue;
var newAssignee = Assignee.login;
if (newAssignee == null) {
newAssignee = "nobody";
}
if (oldAssignee != null) {
oldAssignee.notify("[YouTrack, Reassigned] Issue " + getId() + ": " + summary,
"Issue " + "<a href=\"" + issue.getUrl() + "\">" + issue.getId() + "</a>
was reassigned from you to " + newAssignee);
}
}{code} | [^jetbrains-youtrack-additionalNotifications.zip] |
}{code} | [^jetbrains-youtrack-additionalNotifications.zip] |
| {anchor:No comments for verified issues} No comments for verified issues | Do not allow to add comments to verified issues | {code}rule No Comments for Verified Issues
when State == {Verified} && !State.becomes({Verified}) {
}{code} | [^jetbrains-youtrack-comments.zip] |
| Description template for external reporters | Provide description template for external users | {code}rule Description template for external users
when !isReported() && description == null {
description = "What steps will reproduce the problem?\n" + "1.\n2.\n3.\n\n" +
"What is the expected result?\n\n" + "What happens instead?\n\n" +
"Please provide any additional information below.\n" +
"Attach a screenshot if possible\n";}{code} | [^jetbrains-youtrack-defaultDescription.zip] |
| Do not allow fix issue with unresolved dependencies | Prohibit moving an issue to Fixed state if it 'depends on' unresolved issue(s). | {code}rule Do not allow fix issue with unresolved dependencies
when State.becomes({Fixed}) && depends on.isNotEmpty {
for each dep in depends on {
assert dep.State.isResolved: "The issue has unresolved dependencies and thus cannot be set Fixed!";
}
}{code} | [^jetbrains-youtrack-dependencies.zip] |
| Voting for resolved issues | Do not allow to vote for resolved issues | {code}rule jetbrains-youtrack-doNotVoteForResolvedIssue
when isResolved() {
assert !votes.changed: "It's not allowed to vote for resolved issue.";
}{code} | [^jetbrains-youtrack-doNotVoteForResolvedIssue.zip] |
| Issue due date management | Don't allow to submit an issue without Due Date set. Notify issue assignee about overdue issues. | {code}
rule Don't allow to submit issue without Due Date set
when !issue.isReported() {
Due date.required("You must set the Due Date!");
}{code}Notify issue assignee about overdue issues:
{code}
schedule rule Notify assignee about overdue issues
daily at 10 : 00 : 00 [now > issue.Due Date] {
var user;
if (issue.Assignee == null) {
user = issue.project.leader;
} else {
user = issue.Assignee;
}
user.notify("Issue is overdue", "Please, look at the issue: " + issue.getId());
}
{code} | [^jetbrains-youtrack-dueDate.zip] |
| Intelligent duplicates management | Don't allow duplicates to form a tree structure with height greater than one (source) \\
Don't allow duplicates to form a tree structure with height greater than one (target) \\
User can't remove all duplicate links from issue in state Duplicate \\
When have duplicate link set issue state to Duplicate \\
When is duplicated link added set try to raise priority \\
When issue becomes duplicate it must have duplicate link | Click to expand the code | [Download distributive zip file|^jetbrains-youtrack-duplicates.zip] |
| Deny certain field values combination | Do not allow to use certain fields value combination | {expand:title=Click to expand the code}\\ {code}rule deniedCombinations
when <issue created or updated> {
assert !(Priority == {Show-stopper} && State == {Submitted}):
"Denied fields combination detected (Submitted Show-stopper)";
assert !(State == {Open} && Assignee == null):
"Denied fields combination detected (Open Unassigned)";
}{code}{expand} | [Download distributive zip file|^jetbrains-youtrack-issuePropertiesCombinations.zip] |
| Notify issue reporter to approve the fix | Introduce State life-cycle including reporter verification. Send specific notification to reporter to approve the fix | {expand:title=Click to expand the code}\\ {code}
statemachine State lifecycle with verification by reporter for field State {
initial state Submitted {
on Open[always] do {<define statements>} transit to Open
}
state Open {
on Fix[always] do {<define statements>} transit to Fixed
}
state Fixed {
enter {
Fixed in build.required("Please set 'Fixed in build' value.");
}
on Send for verification[always] do {<define statements>} transit to Pending verification
}
state Pending verification {
enter {
Assignee = reporter;
reporter.notify("Please approve fix for the issue" + getId(), "You have reported issue"
+ getId() + "Please verify the applied fix for the issue and set the appropriate state.");
}
on Approve[always] do {<define statements>} transit to Verified
on Reopen[always] do {<define statements>} transit to Open
}
state Verified {
on Reopen[always] do {<define statements>} transit to Open
}
}
{code}
{code}
rule Send specific notification to reporter to approve fix
when State.becomes({Pending verification}) {
reporter.notify("Please approve fix for the issue" + getId(), "You have reported issue"
+ getId() + "Please verify the applied fix for the issue and set the appropriate state.");
Assignee = reporter;
}
{code}\\ \\
\\ \\
\\ \\
\\ \\
\\ \\
\\
{expand} | [Download distributive zip file|^jetbrains-youtrack-notifyReporterToApproveFix.zip] |
| Fix Version and Fixed state | Require Fix Version value for Fixed state, and set state to Fixed when Fixed Version is added | {expand:title=Click to expand the code}\\ {code}
rule Assert Fix version is set for Fixed issues
when State.becomes({Fixed}) {
Fix versions.required("Please set the 'Fix versions' field!");
}{code}{expand}Cannot find the second rule code\!\!\! | [Download distributive zip file|^jetbrains-youtrack-setFixVersions.zip] |
| Set assignee to subsystem owner | Set subsystem owner as assignee for new issues when subsystem is specified | {expand:title=Click to expand the code}{code}
rule Set subsystem owner as assignee for new issues
when Assignee == null && (Subsystem.changed || project.changed) {
if (issue.Subsystem != null) {
issue.Assignee = issue.Subsystem.owner;
}
}{code}{expand} | [Download distributive zip file|^jetbrains-youtrack-subsystemAssignee.zip] |
| Time Management | Change issue state in time automatically, and notify assignee, project lead or subsystem owner when issue life-cycle is violated | {expand:title=Click to expand the code}\\ {code}
statemachine Time Management for field State {
initial state Submitted {
enter {
Subsystem = {Support};
Assignee.required("Responsible support engineer is required!");
}
in 1 hour[always] do {<define statements>} transit to Overdue
on reproducing[always] do {<define statements>} transit to Open
on incomplete[always] do {<define statements>} transit to Incomplete
}
state Overdue {
enter {
var user;
if (Assignee != null) {
user = Assignee;
} else if (Subsystem.owner != null) {
user = Subsystem.owner;
} else {
user = project.leader;
}
user.notify("Aknowledgement needed", "Issue " + getId() + " is waiting for aknowledgement.");
}
on incomplete[always] do {<define statements>} transit to Incomplete
on reproducing[always] do {<define statements>} transit to Open
}
state Open {
in 4 hours[always] do {<define statements>} transit to Wait for reproduce
on approved[always] do {<define statements>} transit to Approved
on incomplete[always] do {<define statements>} transit to Incomplete
on can't reproduce[always] do {<define statements>} transit to Can't Reproduce
}
state Wait for reproduce {
in 1 day[always] do {
var user;
if (Subsystem.owner != null) {
user = Subsystem.owner;
} else {
user = project.leader;
}
user.notify("Issue is not reproduced in 1 day", "Issue " + getId() + " is still waiting for reproduction steps.");
// Notify sales?
}
in 3 days[always] do {
var user;
if (Subsystem.owner != null) {
user = Subsystem.owner;
} else {
user = project.leader;
}
user.notify("Issue is not reproduced in 4 days", "Issue " + getId() +
" is not reproduced, it's better to visit customer on his site.");
// Notify sales?
}
on approved[always] do {<define statements>} transit to Approved
on can't reproduce[always] do {<define statements>} transit to Can't Reproduce
on incomplete[always] do {<define statements>} transit to Incomplete
}
state Can't Reproduce {
on reopen[always] do {<define statements>} transit to Open
}
state Incomplete {
on reopen[always] do {<define statements>} transit to Open
}
state Approved {
enter {
Assignee.required(fail message);
}
on fixed[always] do {<define statements>} transit to Fixed
on obsolete[always] do {<define statements>} transit to Obsolete
}
state Fixed {
on verify[always] do {<define statements>} transit to Verified
on reopen[always] do {<define statements>} transit to Open
}
state Obsolete {
on reopen[always] do {<define statements>} transit to Open
}
state Verified {
on reopen[always] do {<define statements>} transit to Open
}
}
{code}{expand} | [Download distributive zip file|^jetbrains-youtrack-timeManagement.zip] |
{tip:title=Continue}
New to workflow in YouTrack? Please take look at the [workflow guide|http://confluence.jetbrains.net/display/YTD3/Workflow+Guide].
Ready to create your own workflow rule? Use [YouTrack Workflow Editor|http://www.jetbrains.com/youtrack/download/index.html].
Want to share your workflow? Contact us: *youtrack-feedback@jetbrains.com* or [create an issue|http://youtrack.jetbrains.com/dashboard/JT].
{tip}