Best Practices How-To's Tips & Tricks

Make It Workflow — Part 4: Generating New Issues

Last week, we shared a few tips for streamlining issue reporting. The goal was to improve the simplicity and accuracy of issue creation for your team and for your customers.

In this article, we take it one step further. Imagine what happens when you need to create a standard set of issues on a regular basis. Who has the time or patience to spam the Create Issue button and set values for each field in every issue? If your answer is “not me”, read on to learn how to generate issues in YouTrack automatically.

blog_2@2x


If you have read previous articles in this series, you might expect to see the same structure that we’ve used so far: first, describe the workflow-free mode and then move on to the workflow-enhanced approach. When it comes to issue generation, YouTrack doesn’t provide a lot of workflow-free options.

  • You can generate an issue template URL – this speeds up the process somewhat, but you need to define separate URLs for different issue types and it’s still a manual operation.
  • There’s also the Clone Issue action in the issue toolbar, but this is actually implemented with a workflow. We’ll describe this option in more detail a bit later.

Instead, we’ll jump straight to the workflows. Here are a few scenarios where workflow-based issue generation comes in handy.

Generate Subtasks When a Parent Issue is Submitted

Imagine that you’re working with a development team that releases updates to a software product on a regular basis. Apart from implementing new features and fixing bugs, each release requires a standard set of tasks. For example:

  • Distribute an updated version of the product.
  • Update the documentation.
  • Update the ‘What’s new’ page on the product website.
  • Prepare and publish an article on the company blog.
  • Send a newsletter to customers.
  • Send a newsletter to resellers.
  • And many more, depending on your product and your processes…

To save you and your colleagues a lot of time, write a workflow that generates a list of subtasks every time a ‘Release’ issue is created.

You need some criteria to distinguish release issues from other issues for your product. Otherwise, you end up generating subtasks for the wrong issues. There are a number of conditions you can set up, like using a separate project for release issues or adding a specific tag. In this example, we add Release to the set of values for the Type field and generate subtasks only when a new issue of this Type is created. The workflow rule looks like this:

var entities = require('@jetbrains/youtrack-scripting-api/entities');

exports.rule = entities.Issue.onChange({
  title: 'Release management',
  guard: function(ctx) {
    var issue = ctx.issue;
    return issue.becomesReported &&
      issue.fields.Type.name === ctx.Type.Release.name;
  },
  action: function(ctx) {
    var issue = ctx.issue;
    
    var createIssue = function(name, subsystem) {
      var newIssue = new entities.Issue(ctx.currentUser, issue.project,
        name + ' for ' + issue.summary);
      newIssue.fields.Subsystem = subsystem;
      newIssue.fields.Type = ctx.Type.Task;
      newIssue.links['subtask of'].add(issue);
    };
    
    createIssue('Update distribution', ctx.Subsystem.Distribution);
    createIssue('Update documentation', ctx.Subsystem.Documentation);
    createIssue('Update "What\'s new"', ctx.Subsystem.Website);
    createIssue('Blog post', ctx.Subsystem.Blog);
    createIssue('Newsletter for customers', ctx.Subsystem.Newsletters);
    createIssue('Newsletter for resellers', ctx.Subsystem.Newsletters);
  },
  requirements: {
    Type: {
      type: entities.EnumField.fieldType,
      Release: {},
      Task: {}
    },
    Subsystem: {
      type: entities.OwnedField.fieldType,
      Distribution: {},
      Documentation: {},
      Website: {},
      Blog: {},
      Newsletters: {}
    }
  }
});

Generate Subtasks When a Parent Issue is Updated

This case is similar to the previous one from a technical point of view but has a slightly different application.

Let’s look at the process for an operational team that organizes business trips to conferences and events for company employees. Line managers create issues in a special project. The project uses a multi-value Travelers field to store the names of the employees who participate in the event. The operational team prepares travel documents for each employee separately, so they want to have one issue per person.

You can support this case just as we did in the previous scenario by using the value for the Type field as the guard condition. Whenever a line manager submits an issue with multiple travelers, the operational team sets the type to Group Trip. This triggers a workflow that generates a subtask for each employee in the Travelers field and sets the type for each subtask to Individual Trip. We won’t add the rule here, as it can be supported by making minor modifications to the previous example.

However, sometimes employees decide that they want to go to the conference after the Group Trip issue is created. For this case, you need an additional rule that creates a subtask for new travelers on the fly:

var entities = require('@jetbrains/youtrack-scripting-api/entities');

exports.rule = entities.Issue.onChange({
  title: 'Create a subtask for each new traveler',
  guard: function(ctx) {
    var fs = ctx.issue.fields;
    return ctx.issue.isReported &&
      fs.Type && fs.Type.name === ctx.Type.GroupTrip.name &&
      fs.Travelers.added.isNotEmpty();
  },
  action: function(ctx) {
    var issue = ctx.issue;
    
    var createIssue = function(traveler) {
      var newIssue = new entities.Issue(ctx.currentUser, issue.project,
        traveler.fullName + ' at ' + issue.fields.Destination);
      newIssue.fields.Type = ctx.Type.IndTrip;
      newIssue.links['subtask of'].add(issue);
    };
    
    issue.fields.Travelers.added.forEach(createIssue);
  },
  requirements: {
    Destination: {
      type: entities.Field.stringType
    },
    Type: {
      type: entities.EnumField.fieldType,
      GroupTrip: {
        name: 'Group Trip'
      },
      IndTrip: {
        name: 'Individual Trip'
      }
    },
    Travelers: {
      type: entities.User.fieldType,
      multi: true
    }
  }
});

Generate Issues on Demand

There are several situations where you want to generate issues, but you can’t use a combination of field values as the initial condition. Here, the initial condition is inside the mind of the YouTrack user.

The most obvious example is the Clone Issue workflow. This workflow lets you make a copy of an existing request whenever you need it. This workflow uses an ‘action’ rule. This type of rule is triggered by a user-defined command, either by selecting this action from the Command Dialog menu or by executing this command in the Apply Command dialog.

Going back to the previous example, let’s say that you want to provide employees with some swag to give to partners or hand out at the conference booth. In this case, you can write an action rule that generates a subtask for the group trip. The workflow takes the value from the Destination field in the parent task and sets the summary for the subtask to ‘Prepare swag for “Destination”‘:

var entities = require('@jetbrains/youtrack-scripting-api/entities');

exports.rule = entities.Issue.action({
  title: 'Create a swag subtask',
  command: 'swag-subtask',
  guard: function(ctx) {
    return ctx.issue.isReported;
  },
  action: function(ctx) {
    var issue = ctx.issue;
    var newIssue = new entities.Issue(ctx.currentUser, issue.project,
      'Prepare swag for ' + issue.fields.Destination);
    newIssue.fields.Type = ctx.Type.Swag;
    newIssue.links['subtask of'].add(issue);
  },
  requirements: {
    Destination: {
      type: entities.Field.stringType
    },
    Type: {
      type: entities.EnumField.fieldType,
      Swag: {}
    }
  }
});

Generating Issues on a Schedule

So far we’ve shown you several ways to generate issues in response to human actions. However, there are many situations where you don’t even need a human to do the grunt work.

Consider the requirements for a team of developer advocates. One of the numerous responsibilities of this team is to prepare a monthly internal newsletter. The task is the same every month: same concept, same deadline, same assignee. Instead of creating this issue manually, set up a schedule to create it automatically:

var entities = require('@jetbrains/youtrack-scripting-api/entities');

var MONTHS = [
  "January", "February", "March", "April",
  "May", "June", "July", "August",
  "September", "October", "November", "December"
];

var DAY_IN_MS = 24 * 60 * 60 * 1000;

exports.rule = entities.Issue.onSchedule({
  title: 'Internal newsletter',
  search: '#DA-1', // anchor issue; it is required to ensure that
  // this rule is executed exactly once according to schedule
  cron: '0 0 19 15 1/1 ? *', // on the 15th day of every month at 19:00
  action: function(ctx) {
    var date = new Date(Date.now() + 31 * DAY_IN_MS);
    date.setDate(1);
    var month = MONTHS[date.getMonth()];
    var year = date.getFullYear();
    
    var newIssue = new entities.Issue(ctx.currentUser, ctx.issue.project,
      month + ' ' + year + ' Internal Newsletter');
    newIssue.fields.Assignee = ctx.author;
    newIssue.fields.Subsystem = ctx.Subsystem.Newsletters;
    newIssue.fields.DD = date.getTime();
  },
  requirements: {
    Assignee: {
      type: entities.User.fieldType
    },
    Subsystem: {
      type: entities.OwnedField.fieldType,
      Newsletters: {}
    },
    DD: {
      type: entities.Field.dateType,
      name: 'Due Date'
    },
    author: {
      type: entities.User,
      login: 'root'
    },
    da: {
      type: entities.Issue,
      id: 'DA-1'
    }
  }
});

As you can see, there are plenty of ways to make YouTrack do the heavy lifting for you. Define your recurring issues once and let the workflows do the rest.

The next article in this series will show you how to propagate values between linked issues to ensure that your data is always in sync. While we prepare the post for you, satisfy your curiosity by browsing other workflow-related resources: