気付いたら11月です。すっかり夜の帳が下りるのが早くなって、もう年末かねというくらいには相変わらず時間がマッハで過ぎていきます。

さて、なんだかんだ二ヶ月くらい細々と作ってましたが、大体形になったのでちょっと書いてみようかと思います。

どんなの作ったの

https://github.com/derui/jira-dependency-tree

まだ改善途中ですが、まずは論より証拠ということで、画像をペタリします。

もちろん内容は私のテスト用projectなので、色々適当です。気にせず。

特徴としては、

という感じです。

制作の動機

会社ではJIRAを使っているんですが、JIRAにはAdvanced Roadmapってのがあります。これは元々あるRoadmapの超高機能版で、依存関係の把握や期間の把握などがより細かくできるすぐれものです。

余談ですが、会社のJIRAにはプラグインとしてガントチャートのやつも入っていて、一応これも使えます。が、ガントチャートと聞くと うっ頭が ってなるのと、実際使ってみたけど色々課題があったので、使うのはすぐにあきらめました。

が、このAdvanced Roadmap、JIRA Software Premiumという契約が必要になります。このプランにすると、今の金額から1.25倍くらいの金額になります(課金はユーザー単位ということらしい)。そうなると、かなりの金額アップになるため、多分それだけが理由だと予算が通らない・・・ってなってます。一応相談したりしているのは横目に見ていましたが。

割と日々のスプリントの中で、依存関係がどうしてもあるものがあったりして、それを視覚的にわかりやすく管理したいなぁ、と思っていたときに、このサイトを発見しました。そういえばd3とかでできそうだなぁ、とかそういうのを思いついたので、よし作ってみるか・・・となりました。

ライブラリとアーキテクチャ

アーキテクチャの図は作る気力が失われてしまったので、ざっと書き連ねる形で失礼します。

苦労したポイント

Cycle.jsの考え方に馴染む

cycle.jsは、Angularなどよりも遥かにアグレッシブにStreamを利用します。というかStreamが本体で、他はそれを切り貼りしてくるものでしかない、という印象すら与えるレベルです。最近Solid.jsとかのリアクティブなものも触っていますが、Cycle.jsはさらに先鋭的だな、という感覚です。

stateの考え方とかも若干他と異なります。最近はcomponent local stateとかそういう方向に倒れる話が多く感じますが、Cycle.jsはそもそも単一関数を組み合わせるという方法論であるため、それが外部でどのように利用されるのか?は原則関与できません。そのため、state管理については、原則としてglobal stateのみに対応する形になります。

また、streamがすべてを支配する世界なので、とにかくstreamの考え方に慣れることを強制されます。ギブスになるのでこれはこれでよきかなと思います。なお、デフォルトのstreamライブラリは、https://github.com/staltz/xstream が使われています。私もmostとかRxJSは知ってましたが、これはCycle.jsで初めて知りました。RxJSと違い、hot streamしか存在しない、というシンプルさと、operatorが必要最小限しかない、というミニマルな設計になってます。

xstreamのoperatorには、RxJSにあるdistinctUntilChangedみたいな便利operatorはありません。foldとかで一応同じような処理を模倣できますが、streamには流れてしまいます。変化が無いこととは?とかそういう考え方もできるので、これはこれで無くてもなんとかなるな、という感じです。

JIRAのAPI仕様にハマる

なんでバックエンドとインフラがあるのか?という理由のすべてがこれになります。当初は、jira.jsを利用しようとしていたのですが、実はJIRAのAPIには、 ブラウザから呼ぶ場合は個別のCORS設定が必要 という、まぁ考えたらそりゃそうなるけど、という制約があります。この制約が、JIRAのプラグインは一杯あるけど、JIRAの機能を利用したWebアプリケーションが少ない理由になっていると推測します。実際、jira.jsのissueとかには、これが理由で利用できなかった、みたいな話がありました。企業のJIRAだったら、自分が管理者でもない限りそういう設定を追加もできませんし。

今回、JIRAのAPIv3を呼び出しているのですが、これを呼ぶための方法としては、

  1. APIキーを利用して、ブラウザではない場所から呼びだす
  2. アプリを接続する(マーケットプレイスにあるやつと同じ方法)

の二つに大別されます。今回はシンプルに1.をやるため、バックエンドを構築しました。Rustなのはまぁ使ってみたかったからってだけです。別にOCamlでもなんでもよかったんですが。なお、今回作成したバックエンドは一切のセキュリティ情報を保存しません。渡されたものをそのまま使うだけなので、MITMでもされなければAPIキーは流出しない、はずです。多分。

issueのレンダリング

今回絶対にやりたかったのは、issue間の依存グラフを表示する、ということでした。が、D3でこの辺をやる知識が全然ないのと、SVGをまともにやったことがなかったので、色々とHTMLと勝手が違って苦労しました・・・。

グラフについては、基本的にはシンプルな作りにしてます。所詮1スプリントあたりのissue数とかが数百になる、とかはありえないので(前提として、あるスプリントのissueだけ表示する、としているので)、性能とかはある程度度外視した実装になっています。グラフのレイアウトについてはわりとカッチリ計算して出すようにしていますが、なんかアニメーションさせたいなぁ、ということで、force simulationを利用しています。

https://github.com/d3/d3-force

↑こんなものを利用する形です。

これから

実際仕事で使うようになってみて、色々と機能が足りねーなーということがわかってきたので、ここを追加していこうと思ってます。

実際、依存関係が見られればいいだけなら、私の労働時間を使ってこれをメンテする方が安いんじゃないか・・・とか思ったりはしましたが、それはまた別だなということで一旦言わないことにしました。まぁ自分で作ってみたくて好き勝手やってるだけですし。リポジトリにはクレデンシャルとかは入れていないので、firebaseとAWSのアカウントがあれば、自分で立てることはできます。もしかしたらproject名だけ被るかもしれないですが、まぁそこは。