Developer Portal for YouTrack and Hub Help

Using the Workflow API

Use the information on this page to learn how to work with specific entities in the workflow API that are not described in other sections of the workflow reference.

Follow these guidelines to access issue properties, values in custom fields, and issue links.

Predefined Issue Fields

Predefined issue fields like summary, description, and reporter are all properties of the issue entity.

const summary = issue.summary; issue.summary = "Just a bug";
Custom Field Values

References to values in a custom field vary based on whether the field stores single or multiple values.

  • For fields that store single values, you can reference the value directly:

    const state = issue.fields.State; issue.fields.State = ctx.State.Open; if (issue.fields.State.name === ctx.State.Fixed.name) { // Do stuff }

  • For fields that store multiple values, the values are stored as a set. The operations that are available for sets are described in more detail in the next section.

    const versions = issue.fields["Fix versions"]; versions.forEach(function (v) { subtask.fields["Fix versions"].add(v); });

Aliases

If you set the alias property for a custom field in the requirements statement, you can use this alias to access values for the field as well. For example, if the alias for the Fix versions field is set to FV:

issue.fields.FV.forEach(function (v) { // Do stuff });
Issue Links

Issue link types are accessed by their inward or outward name. For example, relates to or parent for. Issue links are always treated as a set.

const parent = issue.links["subtask of"].first(); parent.links["parent for"].add(issue);

Note that custom fields and issue link types that contain spaces or other non-alphabetic characters must be set in quotation marks and brackets.

Working with Set Objects

There are several groups of operations you can do with multiple values (returned as Set<value type>):

  • Access them directly (first(), last(), get(index)) or by iterator (entries(), values()).

  • Traverse over all values in Set with forEach(visitor).

  • Look for values with find(predicate) and check if a value is in Set with has(value).

  • Check size with isEmpty(), isNotEmpty() and size property.

  • Modify content with add(element), remove(element) and clear().

  • Get the current changes for a Set object with the added, removed and isChanged properties.

For a detailed description of this object, see Set.

Calling Methods

// Call entity method: const stateCF = issue.project.findFieldByName('State'); // Call static method: const p = entities.Project.findByKey('INT'); // Call issue constructor: const newIssue = new entities.Issue(ctx.currentUser, issue.project, "Subtask");

Finding Specific Entities

There are two ways to find a specific entity, like an issue or a user, and use it in a workflow script:

  1. Add it in the requirements and reference it in the context. Use this approach as often as you can, as it is the most reliable. If the specified entity is not found in your database, the script is not executed.

  2. If the first option is not applicable for whatever reason, use the findBy* and find*By* methods from the workflow API:

Use findBy* methods to find a single occurrence of a specific entity:

const issue = entities.Issue.findById('MP-23'); // an entities.Issue or null const projectByName = entities.Project.findByName('Music Production'); // an entities.Project or null const projectByKey = entities.Project.findByKey('MP'); // an entities.Project or null const user = entities.User.findByLogin('jane.smith'); // an entities.User or null const userGroup = entities.UserGroup.findByName('MP team'); // an entities.UserGroup or null const agiles = entities.Agile.findByName('MP Scrum'); // a Set of entities.Agile const tags = entities.IssueTag.findByName('production'); // a Set of entities.IssueTag const queries = entities.SavedQuery.findByName('MP Backlog'); // a Set of entities.SavedQuery

Use find*By* methods to find child entities:

const sprint = agiles.first().findSprintByName('Sprint 23'); // an entities.Sprint or null const priorityField = projectByKey.findFieldByName('Priority'); // an entities.ProjectCustomField or null const major = field.findValueByName('Major'); // an entities.Field or null const critical = field.findValueByOrdinal(1); // an entities.Field or null const assigneeField = projectByKey.findFieldByName('Assignee'); const jane = assigneeField.findValueByLogin('jane.smith'); // an entities.User or null const groupField = projectByKey.findFieldByName('Requestors'); const newBand = groupField.findValueByName('New Band'); // an entities.UserGroup or null

Finding Multiple Issues

Sometimes you might want to find a set of issues that match certain criteria and process them in the scope of a single rule. For example, you can compile a list of issues and send it as an email message. In these situations, the search API can help. Here's a simple example:

const entities = require('@jetbrains/youtrack-scripting-api/entities'); const search = require('@jetbrains/youtrack-scripting-api/search'); const workflow = require('@jetbrains/youtrack-scripting-api/workflow'); exports.rule = entities.Issue.onChange({ title: 'Do not allow developers to have more than 1 issue in progress per project', action: (ctx) => { const issue = ctx.issue; if (issue.isReported && (issue.fields.becomes(ctx.State, ctx.State['In Progress']) || issue.fields.isChanged(ctx.Assignee)) && (issue.fields.Assignee || {}).login === ctx.currentUser.login) { // First, we build a search query that checks the project that the issue belongs to and returns all of the issues // that are assigned to current user except for this issue. const query = 'for: me State: {In Progress} issue id: -' + issue.id; const inProgress = search.search(issue.project, query, ctx.currentUser); // If any issues are found, we get the first one and warn the user. if (inProgress.isNotEmpty()) { const otherIssue = inProgress.first(); const message = 'Dear ' + ctx.currentUser.login + ', please close <a href="' + otherIssue.url + '">' + otherIssue.id + '</a> first!'; workflow.check(false, message); } } }, requirements: { State: { type: entities.State.fieldType, 'In Progress': {} }, Assignee: { type: entities.User.fieldType } } });
Last modified: 23 April 2024