(コードを書かずに)Backlogに課題が追加されたときに、課題の一部をリセットする仕掛けを作った

#困っていること
Backlogで過去の課題を複製する機能は便利なのですが、過去のチケットのデータを引き継いて欲しくないケースもあります。（複製時は消せと何度も言っているのに！）作成者が消すのを忘れてしまうことがあります。
こういうのは人間に任せるからミスをするので、人間に任せない仕組みにしてしまいたいと思います

#今回使ったもの
- Backlog WebHook(新規登録)
- Microsoft Flow (HTTP受信とHTTP発信)
- Backlog API(課題情報の更新)

今回は簡単に実現するために、Microsoft Flowを使いました。


#やってみよう
##1. BacklogAPIのKeyを発行
Backlog右上の人アイコンから、個人設定、APIからBacklogAPIのキーを発行し、APIをブラウザで叩いて自分の情報が取れるか確認しておきます。

```
https://kanaxx.backlog.com/api/v2/users/myself?apiKey=YOUR KEY
```


##2. BacklogのWebHook設定
Backlogのダッシュボードからプロジェクト設定を開き、WebHookを設定をします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/976e0a91-24cf-758b-03fe-67eb15a55249.png)

まだMicrosoft Flow側の受信の設定をしていないので、WebHook URLは一時的に```http://localhost```を設定しています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/33bdd372-a8c5-6a62-3cf9-4f38ac211dba.png)

今回は課題の新規作成のみWebHookが飛ぶように設定をしました。
画面の一番下にある「テスト実行」を押してサンプルのデータを出力してみます（実際にはlocalhostで受信はできません。）

テスト実行を押したあとに、WebHookの一覧から送信履歴から送信データを確認します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/6d66a00f-a853-4795-7080-2b6c1549681b.png)

「全て表示」を押すと、WebHookで送信されるデータの形式が確認できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/4055f835-7d49-f22b-18ef-03d87d724e33.png)

データはこんな感じです。（ユーザ情報は置き換え済みです）

```json:webhook-sample.json
{
  "created": "2019-03-26T11:52:03Z",
  "project": {
    "archived": false,
    "projectKey": "TEST",
    "name": "TestProject",
    "chartEnabled": false,
    "id": 100,
    "subtaskingEnabled": false
  },
  "id": 10,
  "type": 1,
  "content": {
    "summary": "test issue",
    "key_id": 100,
    "customFields": [
      
    ],
    "dueDate": "",
    "description": "test description",
    "priority": {
      "name": "",
      "id": null
    },
    "resolution": {
      "name": "",
      "id": null
    },
    "actualHours": null,
    "issueType": {
      "color": "null",
      "name": "Bug",
      "displayOrder": null,
      "id": 400,
      "projectId": null
    },
    "milestone": [
      {
        "archived": "false",
        "releaseDueDate": "null",
        "name": "prototype release",
        "displayOrder": null,
        "description": "",
        "id": null,
        "projectId": null,
        "startDate": "null"
      },
      {
        "archived": "false",
        "releaseDueDate": "null",
        "name": "alpha release",
        "displayOrder": null,
        "description": "",
        "id": null,
        "projectId": null,
        "startDate": "null"
      },
      {
        "archived": "false",
        "releaseDueDate": "null",
        "name": "beta release",
        "displayOrder": null,
        "description": "",
        "id": null,
        "projectId": null,
        "startDate": "null"
      },
      {
        "archived": "false",
        "releaseDueDate": "null",
        "name": "product release",
        "displayOrder": null,
        "description": "",
        "id": null,
        "projectId": null,
        "startDate": "null"
      }
    ],
    "versions": [
      {
        "archived": "false",
        "releaseDueDate": "null",
        "name": "Version0.1",
        "displayOrder": null,
        "description": "",
        "id": null,
        "projectId": null,
        "startDate": "null"
      },
      {
        "archived": "false",
        "releaseDueDate": "null",
        "name": "Version0.2",
        "displayOrder": null,
        "description": "",
        "id": null,
        "projectId": null,
        "startDate": "null"
      },
      {
        "archived": "false",
        "releaseDueDate": "null",
        "name": "Version1.0",
        "displayOrder": null,
        "description": "",
        "id": null,
        "projectId": null,
        "startDate": "null"
      }
    ],
    "parentIssueId": null,
    "estimatedHours": null,
    "id": 100,
    "assignee": null,
    "category": [
      {
        "name": "Category1",
        "displayOrder": null,
        "id": null
      },
      {
        "name": "Category2",
        "displayOrder": null,
        "id": null
      }
    ],
    "startDate": "",
    "status": {
      "name": "In Progress",
      "id": 2
    }
  },
  "notifications": [
    
  ],
  "createdUser": {
    "nulabAccount": {
      "nulabId": "xxx",
      "name": "xxx",
      "uniqueId": "xxx"
    },
    "name": "kanaxx",
    "mailAddress": null,
    "id": 0,
    "roleType": 1,
    "lang": "ja",
    "userId": null
  }
}
```

WebHookデータの中にissueKeyがありません。これが今後のドハマりポイントになります。


##3.Microsoft Flowの受信の詳細設定
Microsoft Flowでjsonデータを受信するときは、BacklogのWebHookデータのそのものか、json schemaが必要です。今回は上で作ったサンプルデータをもとにデータのペイロードをして形を作ります。

Flowを「一から作成」を選択
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/520ba8a5-bf1c-3189-6481-fe258f52eaf4.png)

トリガーに```HTTP要求の受信時```を設定し、サンプルのペイロードを利用してスキーマを生成するをクリック。これは実データをMicrosoft Flowに解析してもらい、データの項目を確定させる方法です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/e50d5eb7-dbaf-5a6d-0a63-bcb2d805e401.png)

上で作ったBacklogのWebHookサンプルデータを投入し、OKを押すとjson schemaが作成される。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/b705d25f-4698-ee15-9dca-6c0b7327eed6.png)

メソッドを```POST```に設定しておく。受信用のURLはまだ作成できません。次の工程までお待ちください

##4.Microsoft FlowにHTTP(発信)を追加
アクションに```HTTP```を追加して、Microsoft FlowからBacklogのAPIを実行する仕掛けを作ります。

初期状態
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/b27e73cd-c688-3c33-655f-b07e13475b97.png)

入力が簡単なところは以下の3つ。
方法は```Patch```
ヘッダ　```Content-Type``` = ```application/x-www-form-urlencoded ```
クエリー　```apiKey``` = ```YOUR API KEY```

さくっといれてしまいましょう。

###URLの設定
URLはちょっと複雑です。
FlowのURLには、Backlogの[課題更新のAPI](https://developer.nulab.com/ja/docs/backlog/api/2/update-issue/)のURLを設定します。このAPIは、URLの最後に課題のIDを設定する必要があります。つまりチケットごとにURLが変わるわけです。このため、課題IDは変数からIDを持ってきます。

URLにカーソルを置くと、右側の変数サポートで何も出てきません。「もっと見る」を押すと出てきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/e24add0e-6f33-9291-5196-10c87ccb51d8.png)

idが11個も並ぶ絶望的なインターフェイス:innocent:
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/0ec87823-afe3-f334-96fc-201c77e5225f.png)

課題のIDは9番目のIDなので、9番目のIDがURLの最後尾に付くように設定します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/67cd4be8-e7d6-b375-9a91-7f42ed134f49.png)

必ずidの9番目が課題IDなのか自信がないので、コードのプレビュー機能を使って確認できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/98111244-2c6f-4fbe-a568-cca3bde24ff4.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/c82e2696-d0df-bf9c-1553-523a0311a169.png)

```{triggerBody()?['content']?['id']}",``` になっていれば正解です。ならない場合には総当たりで11回試してください。たぶん、下から試すのが早いです。

###本文
本文はBacklog APIの課題更新のパラメータを設定します。name=valueを&で繋ぐ形式です。

```
versionId=[]&milestoneId[]=&dueDate=
```

今回はバージョン、マイルストーン、締切日を空にするように設定しています。カスタムフィールドも対応可能ですが、フリープランだと使えないのでできる範囲でやっています。パラメータの詳細はBacklog APIのドキュメントを見てください。


###URLの発行
フローを保存すると、HTTP要求受信用のURLが発行されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/8f395f7d-1e72-aa22-91c3-f506af1c6d6a.png)

次で使うのでコピーしておきます。

##5. Backlog WebHookのURLを修正

手順2で、Backlog側のWebHookのURLを一時的に```http://localhost```で作ってあります。WebHookの設定画面を開き、Microsoft Flowで発行されたURLに修正します。

これで準備完了。

#いざ実行

元になるチケットを準備
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/010bfaf8-1f44-1edf-a46f-ea8c2b6049d3.png)

バージョン、マイルストン、締切日を設定済み


チケットの複製を実行
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/6168d9a6-cf06-573f-3295-7f82da99b00d.png)

うっかり、マイルストーンとバージョンと期限日を消すのを忘れてしまい、新規登録を完了させる


#確認
##WebHookの発信確認

![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/e7e4c04a-ca7f-ab3a-e6be-8b54dfc18200.png)

増えてるので何か送っているっぽい。一番新しい送信履歴を見てみる

![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/17c6b68c-0dbf-ec2d-58ab-f48109bbc06e.png)

今追加したチケットの情報が送られているのを確認。（issueKeyはやっぱり無い）

##チケットの確認
新規登録直後の状態
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/48624449-e13d-1896-d4f2-219d18415b99.png)



WebHook -> Microsoft Flow -> Backlog APIが実行されたあと。右上の期限日も消えてる
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/239a1a6e-eaa9-6400-dec4-51be671f2c14.png)

コメントにも変更履歴が残ります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/72f67866-c7a7-e5a5-5f33-cf62c52e583a.png)


Flowのプランにもよると思いますが、自分の環境だと新規登録の1秒後には消えていました。
登録日：```2019/03/26 23:05:22```
更新日：```2019/03/26 23:05:23```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/b4675bbb-0392-4b1e-685a-0473f3720a5c.png)


#まとめ
Microsoft Flowは便利です。プログラム作らずにできちゃいます。たぶんZapierでもできると思います。
今回はやらなかったですが、Flowの分岐機能を使えば、特定のチケット種別のときだけ初期化するとか、子チケットのときだけ初期化するとか、期限日を強制的に3日後に更新してしまうとか、応用はいろいろできると思います。

FlowからのHTTPコネクタがFlow for Office365のライセンスでは使えないかもしれません。自分のところは使えているので、使えない場合にはライセンスを確認してください。
https://blog.iotaker.jp/?p=294

##おまけ
実は手順3時点で、工夫したjson schemaを入れると、もうちょっと簡単に課題IDを設定できるようになります。参考までに自分が作ったものを置いておきます。細かいところは正確じゃないかもしれません。Backlog WebHookの仕様が変わってもこのQiitaは更新しないと思うので、変わっていたらごめんなさい。（json schemaはAPI提供側から提供されるのがよいですよね。）

**変更点１：**
ペイロードしたjson schemaドキュメントの```"type"="object"```の内側に、"title"="project"や"title"="content"と設定を追加する。

**変更点２：**
11個あるIDに名前が付くようになるので、```backlog_issue content id```を選べばOK
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/ad387ead-9227-005d-890e-b1439e83885d.png)


```json
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "title": "backlog_issue",
    "properties": {
      "created": {
        "type": "string"
      },
      "project": {
        "type": "object",
        "title": "project",
        "properties": {
          "archived": {
            "type": "boolean"
          },
          "projectKey": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "chartEnabled": {
            "type": "boolean"
          },
          "id": {
            "type": "integer"
          },
          "subtaskingEnabled": {
            "type": "boolean"
          }
        },
        "required": [
          "archived",
          "projectKey",
          "name",
          "chartEnabled",
          "id",
          "subtaskingEnabled"
        ]
      },
      "id": {
        "type": "integer"
      },
      "type": {
        "type": "integer"
      },
      "content": {
        "type": "object",
        "title": "content",
        "properties": {
          "summary": {
            "type": "string"
          },
          "key_id": {
            "type": "integer"
          },
          "customFields": {
            "type": "array",
            "items": {}
          },
          "dueDate": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "priority": {
            "type": "object",
            "title": "priority",
            "properties": {
              "name": {
                "type": "string"
              },
              "id": {
                "type": "integer"
              }
            },
            "required": [
              "name",
              "id"
            ]
          },
          "resolution": {
            "type": "object",
            "title": "resolution",
            "properties": {
              "name": {
                "type": "string"
              },
              "id": {
                "type": "null"
              }
            },
            "required": [
              "name",
              "id"
            ]
          },
          "actualHours": {
            "type": "null"
          },
          "issueType": {
            "type": "object",
            "title": "issue_type",
            "properties": {
              "color": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "displayOrder": {
                "type": "integer"
              },
              "id": {
                "type": "integer"
              },
              "projectId": {
                "type": "integer"
              }
            },
            "required": [
              "color",
              "name",
              "displayOrder",
              "id",
              "projectId"
            ]
          },
          "milestone": {
            "type": "array",
            "items": {}
          },
          "versions": {
            "type": "array",
            "items": {}
          },
          "parentIssueId": {
            "type": "integer"
          },
          "estimatedHours": {
            "type": "null"
          },
          "id": {
            "type": "integer"
          },
          "assignee": {
            "type": "null"
          },
          "category": {
            "type": "array",
            "items": {}
          },
          "startDate": {
            "type": "string"
          },
          "status": {
            "type": "object",
            "title": "status",
            "properties": {
              "name": {
                "type": "string"
              },
              "id": {
                "type": "integer"
              }
            },
            "required": [
              "name",
              "id"
            ]
          }
        },
        "required": [
          "summary",
          "key_id",
          "customFields",
          "dueDate",
          "description",
          "priority",
          "resolution",
          "actualHours",
          "issueType",
          "milestone",
          "versions",
          "parentIssueId",
          "estimatedHours",
          "id",
          "assignee",
          "category",
          "startDate",
          "status"
        ]
      },
      "notifications": {
        "type": "array",
        "items": {}
      },
      "createdUser": {
        "type": "object",
        "title": "created_user",
        "properties": {
          "nulabAccount": {
            "type": "null"
          },
          "name": {
            "type": "string"
          },
          "mailAddress": {
            "type": "null"
          },
          "id": {
            "type": "integer"
          },
          "roleType": {
            "type": "integer"
          },
          "userId": {
            "type": "null"
          }
        },
        "required": [
          "nulabAccount",
          "name",
          "mailAddress",
          "id",
          "roleType",
          "userId"
        ]
      }
    },
    "required": [
      "created",
      "project",
      "id",
      "type",
      "content",
      "notifications",
      "createdUser"
    ]
  }
```