Follow items automatically

📘

How to apply this rule?

Add the owner as a follower

[
  {
    "type": "source:targetprocess:EntityChanged",
    "entityTypes": [
      "UserStory",
      "Bug",
      "Task",
      "Request",
      "Feature",
      "Epic",
      "PortfolioEpic"
    ],
    "modifications": {
      "created": true,
      "deleted": false,
      "updated": [
        "Owner"
      ]
    }
  },
  {
    "type": "filter:JavaScript",
    "script": "const api = context.getService(\"targetprocess/api/v2\");\nconst followers = await api.queryAsync(\"GeneralFollowers\", {\n  result: \"count\",\n  where: `General.Id=${args.ResourceId} and User.Id=${args.Current.Owner.Id}`\n});\n\nreturn followers == 0;"
  },
  {
    "type": "action:targetprocess:CreateEntity",
    "entityType": {
      "type": "constant",
      "value": "GeneralFollower"
    },
    "fields": {
      "General": {
        "type": "field",
        "name": "Id",
        "target": {
          "type": "pipelineBlockOutput"
        }
      },
      "User": {
        "type": "field",
        "name": "Id",
        "target": {
          "type": "field",
          "name": "Owner",
          "target": {
            "type": "pipelineBlockOutput"
          }
        }
      }
    }
  }
]

Add assigned users as followers

[
  {
    "type": "source:targetprocess:EntityChanged",
    "entityTypes": [
      "Assignment"
    ],
    "modifications": {
      "created": true,
      "deleted": false,
      "updated": false
    }
  },
  {
    "type": "filter:JavaScript",
    "script": "const api = context.getService(\"targetprocess/api/v2\");\nconst followers = await api.queryAsync(\"GeneralFollowers\", {\n  result: \"count\",\n  where: `General.Id=${args.Current.Assignable.Id} and User.Id=${args.Current.GeneralUser.Id}`\n});\n\nreturn followers == 0;"
  },
  {
    "type": "action:targetprocess:CreateEntity",
    "entityType": {
      "type": "constant",
      "value": "GeneralFollower"
    },
    "fields": {
      "General": {
        "type": "field",
        "name": "Id",
        "target": {
          "type": "field",
          "name": "Assignable",
          "target": {
            "type": "pipelineBlockOutput"
          }
        }
      },
      "User": {
        "type": "field",
        "name": "Id",
        "target": {
          "type": "field",
          "name": "GeneralUser",
          "target": {
            "type": "pipelineBlockOutput"
          }
        }
      }
    }
  }
]

Remove a user from followers when they are unassigned

[
  {
    "type": "source:targetprocess:EntityChanged",
    "entityTypes": [
      "Assignment"
    ],
    "modifications": {
      "created": false,
      "deleted": true,
      "updated": false
    }
  },
  {
    "type": "filter:Relational",
    "or": [
      {
        "and": [
          {
            "target": {
              "type": "field",
              "name": "Id",
              "target": {
                "type": "field",
                "name": "Owner",
                "target": {
                  "type": "field",
                  "name": "Assignable",
                  "target": {
                    "type": "pipelineBlockOutput"
                  }
                }
              }
            },
            "value": {
              "type": "field",
              "name": "Id",
              "target": {
                "type": "field",
                "name": "GeneralUser",
                "target": {
                  "type": "pipelineBlockOutput"
                }
              }
            },
            "operator": {
              "type": "is not"
            }
          }
        ]
      }
    ]
  },
  {
    "type": "filter:JavaScript",
    "script": "const api = context.getService(\"targetprocess/api/v2\");\nconst assignments = await api.queryAsync(\"Assignment\", {\n  result: \"count\",\n  where: `Assignable.Id=${args.Current.Assignable.Id} and GeneralUser.Id=${args.Current.GeneralUser.Id}`\n});\n\nreturn assignments == 0;"
  },
  {
    "type": "action:JavaScript",
    "script": "const api = context.getService(\"targetprocess/api/v2\");\nconst follows = await api.queryAsync(\"GeneralFollower\", {\n  select: \"id\",\n  where: `General.Id=${args.Current.Assignable.Id} and User.Id=${args.Current.GeneralUser.Id}`\n});\n\nreturn follows.map(id => {\n  return {\n    command: \"targetprocess:DeleteResource\",\n    payload: {\n      resourceType: \"GeneralFollower\",\n      resourceId: id\n    }\n  };\n});"
  }
]

Add commented users as followers

[
  {
    "type": "source:targetprocess:EntityChanged",
    "entityTypes": [
      "Comment"
    ],
    "modifications": {
      "created": true,
      "deleted": false,
      "updated": false
    }
  },
  {
    "type": "filter:JavaScript",
    "script": "const api = context.getService(\"targetprocess/api/v2\");\nconst followers = await api.queryAsync(\"GeneralFollowers\", {\n  result: \"count\",\n  where: `General.Id=${args.Current.General.Id} and User.Id=${args.Current.Owner.Id}`\n});\n\nreturn followers == 0;"
  },
  {
    "type": "action:targetprocess:CreateEntity",
    "entityType": {
      "type": "constant",
      "value": "GeneralFollower"
    },
    "fields": {
      "General": {
        "type": "field",
        "name": "Id",
        "target": {
          "type": "field",
          "name": "General",
          "target": {
            "type": "pipelineBlockOutput"
          }
        }
      },
      "User": {
        "type": "field",
        "name": "Id",
        "target": {
          "type": "field",
          "name": "Owner",
          "target": {
            "type": "pipelineBlockOutput"
          }
        }
      }
    }
  }
]

Add mentioned user as a follower

{
  "pipeline": [
    {
      "type": "source:targetprocess:EntityChanged",
      "entityTypes": [
        "Comment"
      ],
      "modifications": {
        "created": true,
        "deleted": false,
        "updated": false
      }
    },
    {
      "or": [
        {
          "and": [
            {
              "value": {
                "type": "constant",
                "value": "@user:"
              },
              "target": {
                "name": "Description",
                "type": "field",
                "target": {
                  "type": "pipelineBlockOutput"
                }
              },
              "operator": {
                "type": "contains"
              }
            }
          ]
        },
        {
          "and": [
            {
              "value": {
                "type": "constant",
                "value": "@user:"
              },
              "target": {
                "name": "Description",
                "type": "field",
                "target": {
                  "type": "pipelineBlockOutput"
                }
              },
              "operator": {
                "type": "contains"
              }
            }
          ]
        }
      ],
      "type": "filter:Relational"
    },
    {
      "type": "action:JavaScript",
      "script": "console.log(args.Current.Description);\n\n\n\nconst MARKDOWN_MARK = \"<!--markdown-->\";\nconst isMarkdown = desc => desc.startsWith(MARKDOWN_MARK);\nconst preprocessMarkdown = desc => desc.replace(MARKDOWN_MARK, \"\");\nconst preprocessHtml = desc => decodeHtmlText(removeHtmlTags(desc));\nconst removeHtmlTags = desc => desc.replace(/<\\/?[^>]+(>|$)/g, \"\");\nconst decodeHtmlText = desc => desc.replace(/&#(\\d+);/g, (_, code) => String.fromCharCode(code));\nconst rawDescription = args.Current.Description;\nconst description = isMarkdown(rawDescription) ?\n  preprocessMarkdown(rawDescription) :\n  preprocessHtml(rawDescription);\nfunction findLogins(desc) {\n  const result = [];\n  const regex = /@user:([[email protected]_]+)/g;\n  let match;\n  while ((match = regex.exec(desc)) !== null) {\n    const login = match[1];\n    if (!result.includes(login)) {\n      result.push(login);\n    }\n  }\n  return result;\n}\nconst logins = findLogins(description);\nconsole.log(\"Mentioned users:\" + JSON.stringify(logins));\n\nconst api = context.getService(\"targetprocess/api/v2\");\nconst followers = await api.queryAsync(\"GeneralFollowers\", {\n  select: \"User.Id\",\n  where: `General.Id=${args.Current.General.Id}`\n});\nconsole.log(\"Followers:\" + JSON.stringify(followers));\n\nconst userIds = await api.queryAsync(\"Users\", {\n  select: \"id\",\n  where: `login in ${JSON.stringify(logins)} and not (id in ${JSON.stringify(followers)})`\n});\nif (!userIds || !userIds.length) { return; }\n\nreturn userIds.map(userId => {\n  return {\n    command: \"targetprocess:CreateResource\",\n    payload: {\n      resourceType: \"GeneralFollower\",\n      fields: {\n        General: { Id: args.Current.General.Id },\n        User: { Id: userId }\n      }\n    }\n  }\n});"
    }
  ]
}