Propagate Values Between Issues
note
AvailabilityThe workflows on this page were built using the using the JavaScript Editor and may contain elements that are unavailable for editing in the Workflow Constructor.
To learn more about writing workflows in JavaScript, please check out the documentation in our Developer Portal.
A common requirement for issue tracking is to update the values in an issue based on changes that are applied to a related issue. In YouTrack, this use case is supported with workflows.
The next two cases demonstrate how to update issues in response to changes in other issues. These issues are linked with a parent-subtask relationship. However, you can apply this approach to issues with other link types, depending on your process.
The first direction is to apply updates with a top-down approach: when updates are applied to a parent issue, the subtasks are updated accordingly. We call this behavior inheritance and have several default workflows in YouTrack that implement this behavior for various fields, like Subtask Inherit Assignee, Subtask Inherit Fix Versions, and Subtask Inherit Subsystem. Let’s take a look at the corresponding rule for the Subsystem field:
const entities = require('@jetbrains/youtrack-scripting-api/entities');
const workflow = require('@jetbrains/youtrack-scripting-api/workflow');
exports.rule = entities.Issue.onChange({
title: 'Copy subsystem from parent task when issue is linked as a subtask',
guard: (ctx) => {
return ctx.issue.links['parent for'].added.isNotEmpty() &&
ctx.issue.fields.Subsystem;
},
action: (ctx) => {
const issue = ctx.issue;
const safeSetSubsystem = function (subtask) {
if (subtask.project && !subtask.project.isArchived) {
if (subtask.project.key === issue.project.key ||
subtask.project.findFieldByName(ctx.Subsystem.name)) {
if (!subtask.fields.Subsystem) {
const value = subtask.project.findFieldByName(ctx.Subsystem.name)
.findValueByName(issue.fields.Subsystem.name);
if (value) {
subtask.fields.Subsystem = value;
}
}
}
}
};
issue.links['parent for'].added.forEach(safeSetSubsystem);
},
requirements: {
Subsystem: {
type: entities.EnumField.fieldType
},
SubtaskOf: {
type: entities.IssueLinkPrototype,
name: 'Subtask',
outward: 'parent for',
inward: 'subtask of'
}
}
});
note
Workflow LocalizationDefault workflows in YouTrack use the
workflow.i18n(...)
method to localize workflow messages. When you write your own workflows, you always know the language of your YouTrack installation, so you don’t need to use this method.If you want to edit the messages in default workflows, remove this method and replace its usage with a plain string of message text. For details, see Workflow Localization.
You may notice that this rule is pretty verbose. This verbosity guarantees that the rule won't fail with an error when one of the subtasks belongs to a project that doesn't use a Subsystem field. We recommend that you follow this approach every time, as the rule can potentially refer to issues in projects other than the project to which this rule is attached.
The second direction is from the bottom up. This approach is used when subtasks represent the steps for completing a larger task. When all the subtasks reach a specific state, this means that the parent issue has reached this state as well. The most basic example is to close the parent issue as fixed when all subtasks are fixed. This rule is one of the modules in the default Subtasks workflow:
const entities = require('@jetbrains/youtrack-scripting-api/entities');
const workflow = require('@jetbrains/youtrack-scripting-api/workflow');
exports.rule = entities.Issue.onChange({
title: 'Fix parent when all subtasks are resolved',
guard: (ctx) => {
return ctx.issue.isReported && ctx.issue.becomesResolved;
},
action: (ctx) => {
const processParent = function (issue) {
if (issue.links['subtask of'].isEmpty()) {
return;
}
const parent = issue.links['subtask of'].first();
if (parent && parent.project && !parent.project.isArchived &&
parent.isReported && !parent.isResolved) {
const unresolvedSubtask = parent.links['parent for'].find(function (subtask) {
return subtask.isReported && !subtask.fields.State.isResolved;
});
if (!unresolvedSubtask) {
const field = parent.project.findFieldByName(ctx.State.name);
if (field) {
const value = field.findValueByName(ctx.State.Fixed.name);
if (value) {
parent.State = value;
workflow.message(workflow.i18n('Automatically set {0} as Done', parent.id));
return parent;
}
}
}
}
};
let issue = ctx.issue;
while (issue) {
issue = processParent(issue);
}
},
requirements: {
State: {
type: entities.State.fieldType,
Fixed: {}
}
}
});
Depending on your process, this ‘reaching-the-target-state’ condition can be very different. For example, you can have a field Documentation provided with the values No
(default) and Yes
. When each subtask is assigned Yes
to this field, this means that the entire parent issue is documented.
Thanks for your feedback!