Slack経由でJiraの課題を作成する方法

#背景
プロジェクトやタスク管理、業務管理ツールとして「Jira」に日々触れるなかで、Slackなどのメッセージツールから直接チケットを発行(=課題を作成)できると便利です。Jiraの画面上で課題を作成したり、課題更新時の通知についてはWebhook、課題画面内にタブを追加できるアドオンがありますが、SlackからJiraの課題を作る機能がありません。そこで作ってしまいましょう。

#やりたいこと
- Slackに、プロジェクトキーと課題の要約を投稿すると、プロジェクトキーをもとに課題を作成し、課題のURLをレスポンスとしてSlackに返す。
- 課題タイプは、タスクに固定。
- 課題の説明は、デフォルトで「Slackから作成」に固定。

Slackのチャンネルにて、メッセージを送信。
![slack_jira_create_issues_1.PNG](https://qiita-image-store.s3.amazonaws.com/0/41414/08d8b02a-efe8-2af2-65fd-31a18f9ae215.png)

数秒待つと、Jiraから作成された課題のURLが返ってくる。
![slack_jira_create_issues_2.PNG](https://qiita-image-store.s3.amazonaws.com/0/41414/c5d707ba-fe4a-875d-58d5-87df8b60a5cf.png)



#仕様
- 外部から接続可能なサーバー上に、Node-REDを導入。手順例：[Amazon Linuexの場合](https://qiita.com/kolinz/items/94869a3ca8529b14f458)
- 外部から接続可能なサーバー上に、Jira(Jira Software)を導入。手順例：[IBM Cloud IaaSの場合](https://qiita.com/kolinz/items/039e8b7b47a358ff0c86)
- Node-REDは、ユーザー名とパスワードによる基本の認証を適用
- Node-REDのパレット管理にて、node-red-contrib-slackノードを追加

#実際の動作
1. Slack Bot in で、Slackからのメッセージを受信。Slack Bot Inで指定したチャンネルにて、メッセージの文法は、プロジェクトキー:課題の要約。プロジェクトキーと課題の要約をコロンでつなげる。
2. Slackからのメッセージをプロジェクトキーと課題の要約に分割し、別々の変数に変換。splitやswitchノードで処理。
3. JSON形式で、curl でJIRA REST API にPOSTするためのテキストファイル「apidata.txt」を作成する。
4. execノードを使い、Jira REST APIの仕様にもとづき、crulコマンドを実行し、プロジェクトキーに紐づく課題を作成。
5. 「4」のレスポンスをJSON形式に変換し、Slack向けのメッセージを生成。Slackに返信。

![slack_jira_create_issues.PNG](https://qiita-image-store.s3.amazonaws.com/0/41414/6a57b93c-3d92-4e3d-bac5-4b99bc050ef7.png)

#Node-REDフローのソース
お使いのJiraのURLやJiraのユーザー名、パスワードなどを置き換える必要がありますが、実際に動くフローのソースは下記になります。

```
[{"id":"c640a629.5ff6d8","type":"tab","label":"Jira Create Issue","disabled":false,"info":""},{"id":"58aafc60.c78114","type":"split","z":"c640a629.5ff6d8","name":"","splt":":","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":130,"y":160,"wires":[["edf7ab1a.c6d678"]]},{"id":"edf7ab1a.c6d678","type":"switch","z":"c640a629.5ff6d8","name":"","property":"payload","propertyType":"msg","rules":[{"t":"regex","v":"^[A-Z0-9]+$","vt":"str","case":false},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":130,"y":220,"wires":[["e2d141b6.a1175"],["9e5f869e.9fc248"]]},{"id":"e2d141b6.a1175","type":"function","z":"c640a629.5ff6d8","name":"variable:Project Key","func":"var message = msg.payload;\nvar msg = {\n    projkey: message\n    };\n    flow.set('project',msg);\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":200,"wires":[[]]},{"id":"9e5f869e.9fc248","type":"function","z":"c640a629.5ff6d8","name":"variable:Issue Summary","func":"var message = msg.payload;\nvar msg = {\n    summary: message\n    };\n    flow.set('issue',msg);\nreturn msg;","outputs":1,"noerr":0,"x":190,"y":300,"wires":[["85b34b69.bbaa98"]]},{"id":"85b34b69.bbaa98","type":"function","z":"c640a629.5ff6d8","name":"POST - Create Issue","func":"var project = flow.get('project');\nvar issue = flow.get('issue');\nvar projkey = project.projkey;\nvar summary = issue.summary;\nmsg.payload = {\n        \"fields\": {\n            \"project\":{\"key\":projkey},\n            \"summary\":summary,\n            \"description\":\"Slackから作成\",\n            \"issuetype\":{\"id\": \"10002\"}\n        }\n    }\nreturn msg;","outputs":1,"noerr":0,"x":180,"y":400,"wires":[["6ab60ef8.86d27","8c8219fd.2598e8"]]},{"id":"6ab60ef8.86d27","type":"file","z":"c640a629.5ff6d8","name":"","filename":"apidata.txt","appendNewline":false,"createDir":false,"overwriteFile":"true","x":410,"y":400,"wires":[]},{"id":"f700ac62.94151","type":"exec","z":"c640a629.5ff6d8","command":"curl -u admin:password -X POST --data @apidata.txt -H \"Content-Type: application/json\" https://your-jira-url/rest/api/latest/issue","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":540,"y":600,"wires":[["200e603f.7aa1f"],[],[]]},{"id":"8c8219fd.2598e8","type":"delay","z":"c640a629.5ff6d8","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":140,"y":500,"wires":[["f700ac62.94151"]]},{"id":"200e603f.7aa1f","type":"json","z":"c640a629.5ff6d8","name":"","property":"payload","action":"","pretty":false,"x":1090,"y":580,"wires":[["bb119331.35a4b"]]},{"id":"bb119331.35a4b","type":"function","z":"c640a629.5ff6d8","name":"post message","func":"var project = flow.get('project');\nvar projkey = project.projkey\nvar home = \"https://your-jira-url/projects/\"\nvar link = msg.payload.key;\nmsg.payload = \"issues-url:\"+home+projkey+\"/issues/\"+link;\nreturn msg;","outputs":1,"noerr":0,"x":960,"y":720,"wires":[["3914bc0c.1a1d44","68f10547.2916cc"]]},{"id":"37222cc3.a51ee4","type":"Slack Bot In","z":"c640a629.5ff6d8","name":"Slack Bot in","apiToken":"xxxxxx","channel":"slack-channel","x":110,"y":60,"wires":[["7b0c1264.e59bcc"]]},{"id":"68f10547.2916cc","type":"Slack Bot Out","z":"c640a629.5ff6d8","name":"Slack Bot out","apiToken":"xxxxx","channel":"#slack-channel","x":1190,"y":680,"wires":[]},{"id":"3914bc0c.1a1d44","type":"debug","z":"c640a629.5ff6d8","name":"debug:exec","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":1190,"y":720,"wires":[]},{"id":"7b0c1264.e59bcc","type":"function","z":"c640a629.5ff6d8","name":"slack message","func":"msg.payload = msg.payload;\nreturn msg;","outputs":1,"noerr":0,"x":300,"y":60,"wires":[["58aafc60.c78114"]]}]
```

実際に動かすには、次の処理が必要です。

- 外部公開済みのサーバー(仮想マシンでも可)に、Node-REDを導入し、上のソースを読み込みます。
- Jira上でプロジェクトを登録します。
- 「1」の「Slack Bot Inノード」にて、API Tokenとチャンネルを書き換えます。
- 「4」の「execノード」にて、Jiraのユーザー名とパスワード、JiraのURLを書き換えます。
- 「5」の「post messageノード」にてJiraのURLを書き換えます。
- 「Slack Bot outノード」にて、API Tokenとチャンネルを書き換えます。
- デプロイボタンを実行し、実際にSlackから投稿して動作確認しましょう。


#参考資料
Jira Software：https://www.ricksoft.jp/atlassian/jira/
Jira REST APIs：https://developer.atlassian.com/server/jira/platform/rest-apis/
REST API で Jira Software を操作してみる：https://www.ricksoft.jp/blog/archives/7021/