Issue/PRに起こったイベントを取得できるGithub Timeline API

概要

最近GithubのIssue Trackerを自作してみようかなーと思っており、Issue/PullRequestで発生したイベントを取得できる Timeline API について調べたのでまとめておきます。

Timeline APIとは

GithubのIssue/PullRequestでは、誰かがアサインされたり、コメントがついたり、レビューされたりといったイベントが発生します。このイベントの一覧を取得できるのが Timeline API GET /repos/{owner}/{repo}/issues/{issue_number}/timeline です。Issue/PRの状況を取得してなんらかの通知をするようなシステムを独自で作るときに便利だと思います。完全に正確ではありませんが、GithubのUIで見られる以下の一連の表示がそのままAPIで取得できると思っておけばOKです。

f:id:muijp:20201208195549p:plain

ちなみに、ほぼ同じような機能を持っている Events API というAPIもあって使い分けが謎ですが、調べた限り以下の差分がありました。

  • Events API/Timeline API それぞれどちらかでしか取得できないイベントの種類/情報がある
  • Events APIはIssue/PRに関するイベントに加えてRepositoryに関するイベントを別途取得できるが、Timeline APIはIssue/PRについてしか取得できない

この記事ではEvents APIのことは気にせずTimeline APIについてのみ書きます。

APIの叩き方

Timeline APIを単純に叩くと以下のレスポンスが返ってきます。

{
    "message": "If you would like to help us test the Timeline API during its preview period, you must specify a custom media type in the 'Accept' header. Please see the docs for full details.",
    "documentation_url": "https://docs.github.com/rest/reference/issues#list-timeline-events-for-an-issue"
}

このAPIはまだpreview stateなので、専用のヘッダをつける必要があるとのこと。 ドキュメント にしたがって以下のヘッダをつけて送ることでレスポンスを得ることができます。

Accept: application/vnd.github.mockingbird-preview+json

また、プライベートレポジトリのIssueのTimelineを取得するにはAccess Tokenが必要です。 GithubのSettings からrepoの権限のついたTokenを発行して、以下のヘッダを付与することで、自分が権限を持ったプライベートレポジトリのTimelineもAPIから取得できるようになります。

Authorization: token <発行したToken>

レスポンス

Timeline APIを叩くとだいたいどういうレスポンスが返ってくるのか把握するために、 適当なrails/railsのPullRequest のTimelineを取得してみます。PR番号が40114なので、

GET https://api.github.com/repos/rails/rails/issues/40114/timeline

を叩くと、以下のようなレスポンスが返ってきます。あくまでだいたいどういうレスポンスが返ってくるのかを見るためにここに示しているだけなので、大したことがなさそうなフィールドは全部省略しています。実際のフルのレスポンスをみたい場合は実際にAPIを叩いてみてください。rails/railsはパブリックレポジトリなのでTokenは不要です。

[
    {
        "sha": "e0637a5a503fd624b5346c5c51adb447a23d8035",
        "node_id": "MDY6Q29tbWl0ODUxNDplMDYzN2E1YTUwM2ZkNjI0YjUzNDZjNWM1MWFkYjQ0N2EyM2Q4MDM1",
        "url": "https://api.github.com/repos/rails/rails/git/commits/e0637a5a503fd624b5346c5c51adb447a23d8035",
        "html_url": "https://github.com/rails/rails/commit/e0637a5a503fd624b5346c5c51adb447a23d8035",
        "author": {
            "name": "Adrianna Chang",
            "email": "adrianna.chang@shopify.com",
            "date": "2020-08-26T17:30:29Z"
        },
        "message": "Add attr_writer for credentials to Rails::Application",
        "event": "committed",
        ...
    },
    {
        "id": 475768545,
        "node_id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3NDc1NzY4NTQ1",
        "user": {
            "login": "rafaelfranca",
            "id": 47848,
            ...
        },
        "body": "",
        "state": "commented",
        "html_url": "https://github.com/rails/rails/pull/40114#pullrequestreview-475768545",
        "event": "reviewed"
        ...
    },
    {
        "sha": "55a668db2937a5fe7b1c8b2f5e1913e54a16e427",
        "node_id": "MDY6Q29tbWl0ODUxNDo1NWE2NjhkYjI5MzdhNWZlN2IxYzhiMmY1ZTE5MTNlNTRhMTZlNDI3",
        "url": "https://api.github.com/repos/rails/rails/git/commits/55a668db2937a5fe7b1c8b2f5e1913e54a16e427",
        "html_url": "https://github.com/rails/rails/commit/55a668db2937a5fe7b1c8b2f5e1913e54a16e427",
        "author": {
            "name": "Adrianna Chang",
            "email": "adrianna.chang@shopify.com",
            "date": "2020-08-26T17:30:40Z"
        },
        "message": "Remove references to secrets.yml from documentation",
        "event": "committed",
        ...
    },
    {
        "id": 3696199379,
        "node_id": "MDIzOkhlYWRSZWZGb3JjZVB1c2hlZEV2ZW50MzY5NjE5OTM3OQ==",
        "url": "https://api.github.com/repos/rails/rails/issues/events/3696199379",
        "actor": {
            "login": "adrianna-chang-shopify",
            "id": 22918438,
            ...
        },
        "event": "head_ref_force_pushed",
        "created_at": "2020-08-26T18:59:38Z",
        ...
    },
    {
        "id": 3696281850,
        "node_id": "MDE1OlJlZmVyZW5jZWRFdmVudDM2OTYyODE4NTA=",
        "url": "https://api.github.com/repos/rails/rails/issues/events/3696281850",
        "actor": {
            "login": "rafaelfranca",
            "id": 47848,
            ...
        },
        "event": "referenced",
        "created_at": "2020-08-26T19:22:34Z",
        ...
    },
    {
        "id": 3696281853,
        "node_id": "MDExOk1lcmdlZEV2ZW50MzY5NjI4MTg1Mw==",
        "url": "https://api.github.com/repos/rails/rails/issues/events/3696281853",
        "actor": {
            "login": "rafaelfranca",
            "id": 47848,
            ...
        },
        "event": "merged",
        "created_at": "2020-08-26T19:22:34Z",
        ...
    },
    {
        "id": 3696281858,
        "node_id": "MDExOkNsb3NlZEV2ZW50MzY5NjI4MTg1OA==",
        "url": "https://api.github.com/repos/rails/rails/issues/events/3696281858",
        "actor": {
            "login": "rafaelfranca",
            "id": 47848,
            ...
        },
        "event": "closed",
        "created_at": "2020-08-26T19:22:34Z",
        ...
    },
    {
        "id": 3696425655,
        "node_id": "MDE5OkhlYWRSZWZEZWxldGVkRXZlbnQzNjk2NDI1NjU1",
        "url": "https://api.github.com/repos/rails/rails/issues/events/3696425655",
        "actor": {
            "login": "adrianna-chang-shopify",
            "id": 22918438,
            "node_id": "MDQ6VXNlcjIyOTE4NDM4",
            ...
        },
        "event": "head_ref_deleted",
        "created_at": "2020-08-26T20:07:17Z",
        ...
    }
]

イベントのリストが返ってきていますね。リスト要素のeventフィールドが、そのオブジェクトが表すイベントの種別です。今回の例だと、イベント種別は、上からcommittedreviewedcommittedhead_ref_force_pushedreferencedmergedclosedhead_ref_deletedですね。それぞれのイベント種別についてはこの記事の次項でまとめますが、 公式のドキュメント によくまとまってます。

event以外のフィールドを見ると、該当のイベントの詳細がわかります。例えば、committedイベントだと、shaからコミットハッシュが、messageからコミットメッセージがわかりますね。

イベント一覧

どのようなイベント種別が存在するか、それぞれのイベント種別にどのようなフィールドが存在するかを簡単にまとめておきます。自分から見て重要そうなイベントの重要そうなフィールドについてのみ書くので、この記事ではだいたいこんな情報が取れるんだなということを把握するのにとどめて、すべてのイベント種別についてちゃんと知りたい場合は 公式ドキュメント を参照してください。ただし、自分が見つけた範囲でもforce pushを表すhead_ref_force_pushedというイベント種別がドキュメントになかったり、イベント種別によって実際のAPIレスポンスとフィールドのキーが異なったりしたのでもしかしたら公式ドキュメントが実際の仕様に追従できていない部分があるかもしれません。

共通

ほとんどのイベント種別に共通のフィールド。

  • event: イベント種別
  • node_id: global node ididというフィールドも存在するが、イベント種別によってはidがないこともあるのでイベントのユニークなIDがほしいときはnode_idを使うのが良さそう(たぶん)
  • created_at: イベントの発生日時

assigned

Issue/PRがアサインされたこと。

  • actor: アサインした人
    • login: Githubのユーザネーム
    • id: GithubのユーザID
    • avatar_url: アイコンのURL
  • assignee: アサインされた人
    • login
    • id
    • node_id
    • avatar_url

committed

コミットのこと。

commented

Issue/PRにつくコメントのこと。

  • url
  • html_url
  • actor: コメントした人
    • login
    • id
    • avatar_url
  • body: コメントの本文

mentioned

メンションのこと。

  • actor: メンションされた人
    • login
    • id
    • avatar_url

メンションされた人の情報しかなく、メンションした人やメンションがあったコメントに関してはこのイベントでは取得できないみたいです。

review_requested

レビューリクエストのこと。

  • review_requester: レビューをリクエストした人
    • login
    • id
    • avatar_url
  • requested_reviewer: レビューをリクエストされた人
    • login
    • id
    • avatar_url

reviewed

レビューのこと。

  • user: レビュアー
    • login
    • id
    • avatar_url
  • body: レビュー時につけたコメント
  • state: レビューのstate。commented changes_requested approved のいずれか
  • html_url
  • author_association: レビュアーレポジトリ権限。 OWNER COLLABORATOR など

merged

PRのマージのこと。

  • actor: マージした人
    • login
    • id
    • avatar_url
  • commit_id: マージコミットのコミットハッシュ

closed

Issue/PRのクローズのこと。

  • actor: クローズした人
    • login
    • id
    • avatar_url

結論

Timeline APIはIssue/PRの流れがわかってうれしい。Issue/PRを定期的に監視してなにかあったらいい感じで通知するみたいなツールを自分で作りたいときに使えそうです。

情熱を失っても夢は諦められないこと

これは SHIROBAKO AdventCalendar 2020 5日目の記事です。

2020年はSHIROBAKOの劇場版が公開されましたね。ぜひとも劇場版について書きたいところなんですが、いろいろな状況により1回しか観ておらず、全体的に最高だったということ以外ほとんどを忘れてしまいました。ただ、平岡が楽しそうにやっていたところが印象的だったので、今日はアニメ版の平岡について振り返ってみようと思います。

劇場版ではやたらと爽やかだった平岡ですが、ムサニに入社してきてしばらくは本当にやばいやつなんですよね。社内でも社外でも態度が悪いし、りーちゃんにいちゃもんを仕掛けたり円さんとの喧嘩で大暴れしたり...。

f:id:muijp:20201202225512p:plain
会社でこんな表情になることあるんだ

平岡くんの夢

もちろん、平岡は最初からそんな人間だったわけではありません。専門学校の同級生の矢野さんから昔の様子が語られています。

f:id:muijp:20201202225917p:plain

平岡くんも磯川くんも絶対アニメの仕事がやりたいって燃えてた。二人とも大学出てきたから私より年上なんだけどね。磯川君は、あんまり授業に出てこなかったけど平岡君は真面目でさ。リーダシップもあって、文化祭でもみんなを仕切ってた。やる気のかたまりで、こんな作品やりたいって熱く語ってたなあ。

ムサニでの平岡は完全にやる気を失っていますが、自分にはやる気のかたまりの平岡の様子もなんとなく想像ができます。何があったのかはわからないですが、大学を出てからわざわざアニメの専門学校に行くなんてよっぽどアニメが好きだったんでしょう。

酔っ払った平岡自身もタローにかつての夢を明かしています。

f:id:muijp:20201205000846p:plain
野望を語ってます

俺の野望はな、アニメで初めてカンヌで「ある視点」部門の作品賞と国際批評家連盟賞をとるつもりだったんだよ!

平岡くんの現実

ということで夢と情熱を持ってアニメの仕事をはじめた平岡ですが、会社ではうまくいきませんでした。平岡の回想からは、職場に恵まれなかったことがわかりますね。もっといい会社に行けばよかったのではと思ってしまいますが、大学を出ている平岡は、年齢がそこそこにもかかわらずアニメータのようなわかりやすい専門性がないことがネックになって志望する会社には入れなかったとかかなーと想像しています。面接も苦手そうですし。

描いていた理想とのあまりのギャップに、しばらくすると平岡はこのような顔になってしまいます。

f:id:muijp:20201202230319p:plain
このような顔

最初は環境を恨んでいたと思いますが、そのうちにそんな職場にしか入れなかった自分や、職場を変えることができない自分への絶望に変わっていったのではないでしょうか。実際、平岡は仕事ができないわけではないと思いますが、宮森のように若くしてめちゃくちゃ有能というわけでもなさそうです。専門学校時代に描いていたであろう充実した仕事をすることはできず、夢だったカンヌなんてどう考えても無理だということに気づいた平岡はアニメへの情熱を失ってしまいます。

自分の人生これでいくぞと思ってたものに対する情熱がなくなっていくというのはつらいことです。平岡のムサニでの態度は本当にひどいと思いますが、そうなってしまうのもわかるんですよね。とくに、周りの人がちょっとうまくいったりしていると、昔は同じ状況にいたのにとか自分の方が思いが強いのにとか思っていらいらしたりしてしまうのはなんとなく理解できます。そのいらいらが一番顕著に描かれていたのは、専門時代の同期の磯川さんが会社にきたときです。よければ皆さんにも見直していただきたいんですが、このときの平岡の、名刺をスッ -> 荷物をバン -> 机をドン -> 一拍おいて目がうるうる、の流れが非常にリズミカルで芸術性が高いです(21話の8:30あたりから)。やりたいことをやるために会社を起こして、充実した日々を送っていそうな磯川さんと比べて、もともとは同じように持っていたはずの情熱を失って日々の仕事を雑にこなすだけの平岡がとても悲しく見える瞬間です。

f:id:muijp:20201205000426p:plain
宮森はちょっと平岡のことを気にしてますね

平岡くんは夢を諦めたのか

f:id:muijp:20201202235348p:plain
意外と素直に運転を引き受ける

平岡「あいつ、まだこの仕事に夢持ってんだよ。俺なんて、入って一年も経たずに夢醒めたけどな」

矢野さん「たまにいるよね、何十年もずっと夢が醒めてない人」

平岡「ああいるな、性懲りもなく」

矢野さん「私、そういう人が好き」

平岡「俺は嫌いだな」

矢野さんとの車のシーンで平岡は「夢から醒めた」と言っていますが、平岡がこんな状態になってまでアニメの仕事を続けている理由は逆に夢しかないはずだと思います。もちろん、自分には高い能力があり、社会人になってすぐ、すばらしい職場で、好きなアニメを作って大活躍する、という専門学校時代の理想は現実的じゃなかったんだということには気づいているし、新人時代のつらい経験により情熱も失ってしまっているのでそういうことを指して「夢から醒めた」と言っているんでしょう。それでも、アニメが好きであることはやめられないし、いいアニメを作るとか、カンヌに行くとかいう夢を完全には諦めていないからアニメの仕事を続けていると思うんですよね。アニメの世界を離れてしまえば夢が叶う可能性は0になってしまうし、それは夢を持った人には簡単ではない選択です。

ムサニに入った頃の平岡は、情熱を失ってから夢を諦めるまでの期間にいたのだと思います。矢野さんが宮森に「いま平岡くんを降ろしたら、ここで終わっちゃうような気がする」と言っていますが、終わっちゃうというのは平岡くんが夢を諦めてアニメ業界から去ってしまうということでしょう。

自分がSHIROBAKO全体の中でも好きなシーンに、平岡が夜の自宅で「世界アートアニメーション」なる本を開いている場面があります。

f:id:muijp:20201205001314p:plain

アニメ版の最後の数話では良い感じで仕事をするようになった平岡ですが、この時点では瀬川さんからのクレームで担当を外されそうになった直後だったりしてめちゃくちゃ調子が悪いんですよね...。調子の悪い時期でも、あるいは調子の悪い時期だからこそアニメの本を開いてしまうというのが、ほんとにアニメが好きなんだろうなというのが伝わってきます。想像ですが、きっと「世界アートアニメーション」に載っているような仕事は平岡自身の毎日と地続きには思えないだろうし、そこに近付く具体的な方法もわからないでしょう。それどころか、身近な同業者と比べてもひとまわり遅れている...そういうことを冷静に認識しながらも、心の奥底では専門学校の頃のようにアニメに向き合いたいし、いつか「世界アートアニメーション」に載るようなアニメを自分で作るという夢を持っているのだと感じました。

平岡くんと宮森

そんな平岡ですが、ムサニで働くうちに態度を改めていきます。これにはタローの存在とともに、宮森の助けも大きかったと思います。瀬川さんに平岡を担当からはずしてくれと言われた宮森は、平岡を続投させるよう瀬川さんにお願いします。

f:id:muijp:20201204194251p:plain

宮森「瀬川さんのお気持ちもあると思いますが...」

瀬川さん「それって、この次何があったら宮森さんが責任取ってくれるってことで、いいんだよね」

宮森「は、はい」

瀬川さん「具体的には、どうやって?エンドクレジットに作監の名前が出る意味わかる?良いものも悪いものも全部こっちの責任になるんだよ」

...

宮森「瀬川さんの平岡へ不信感の第一は、平岡が集めてきた原画マンへのものだと思います。ですから、瀬川さんがないと判断した原画マンは入れません。そしてリテイクを無責任に瀬川さんに丸投げするのはなしにします。あと、上がった原画は毎日お届けします。ためて渡して一気に上げてくれとか、無茶なお願いは絶対しません」

平岡はないなと思った時にちゃんと担当をはずしてくれと言う瀬川さんもプロだなと思うし、その発言も正しいし重いです。それに対してちゃんと自分の意見を通せる宮森はすばらしいですね。宮森からみた平岡って、年上で態度が悪い、急に入ってきた扱いづらい同僚だと思うんですよね。あと、この状況で失って打撃が大きいのはどう考えても平岡より瀬川さんな気がします。にもかかわらず、デスクとしての本来の業務が忙しいであろう中で平岡をリスクをとってまで救おうとする宮森の姿勢と、それを瀬川さんに受け入れさせる実行力はすごいです。ちなみに、個人的な経験では優秀な人はミーティングへの準備をちゃんとしてくるなと感じることが多いんですが、宮森もちゃんと瀬川さんを納得させるだけの準備を事前にしてますね。対応策を考えておくだけでなく、平岡にもちゃんと瀬川さんを説得する旨と、そのために変えてほしいことを伝えています。

f:id:muijp:20201204195925p:plain
謎のソーシャルディスタンス

宮森「瀬川さんは説得しますので、12話は予定通り、平岡さんと高梨さんでお願いします。ただし原画の割り振りは、瀬川さんや山田さんに相談して下さい。上がったカットは毎日届けて下さい。それから、ちゃんとコミュニケーション取って欲しいです。瀬川さんたちと」

平岡「...分かりました」

ここで、担当を続けられるということを告げられたときに平岡が驚いているのが印象的です。きっと今までの職場では、同じような流れから退職することになっていたんじゃないでしょうか。その状況から救ってくれた宮森の行動を受けて、平岡の行動も変わっていきます。

f:id:muijp:20201204201748p:plain
翌日の朝礼にちゃんと出る平岡

平岡くんの今後

ムサニに入った平岡はもともと持っていた仕事への姿勢を取り戻していきます。アニメ版ではそこまででしたが、劇場版では平岡が完全に情熱を取り戻した姿が描かれていましたね。タローという仲間も得て、その後の平岡が夢にどれくらい近づいているのか楽しみです。

f:id:muijp:20201203003359p:plain

Flutter Widget of the Weekを見たメモ

最近暇なときにFlutterを勉強してるんですが、これできないかなーと思って検索するとだいたいやりたいことができるWidgetがヒットするのですごい。このまま一生検索し続けてもいいが、事前に浅くても網羅的に知っておいた方が楽そうなので、Flutter Widget of the Weekというyoutubeを見ながらWidgetの概要ををメモしていく。

www.youtube.com

SafeArea

ノッチとかにかからない安全な領域にchildを閉じ込めてくれる。Safeにする方向を指定できるんですね...。

Wrap

行をはみ出さないようにいい感じに改行しつつ並べてくれるやつ。chipとかを並べるのに便利らしい。

AnimatedContainer

色とかborder、形みたいなContainerの見た目を変化させた時にinterpolateして自然なアニメーションにしてくれる。

Opacity

透明度を決めるWidget。要素を消したときに隙間を埋めたくないとき、単に消すのではなくOpacityを0にするという風にも使える。AnimatedOpacityというのを使えばこれも自然なアニメーションになる。

FutureBuilder

Futureの状態を見てViewを組み立ててくれる。Loadingアイコンを表示したいときとかに自前で処理を書かなくてもFutureごとこいつに渡すといい感じで表示してくれる。

FloatingActionButton

例のボタン。floatingActionButtonLocationというプロパティで位置を調整できる。

PageView

swipeでページを切り替えることができる。Navigatorで遷移するやつの子のレイヤーの遷移なのかな?

Table

Gridviewみたいなものだけど、スクロールはできない && より細かくレイアウトが指定できる。

SliverAppBar

スクロール位置によって表示/非表示や高さが変わるAppBarかな?CustomScrollViewというやつと一緒に使うみたい。楽しそうなので使ってみたい。

SilverList/SilverList

複数のListView/GridViewを一緒にスクロールさせることができる?遅延読み込みができるのでリストの要素が大量にあるときにも便利。

FadeInImage

ネットワークから画像を表示するときに、placeholderを表示するのに使える。

StreamBuilder

FutureBuilderのStream版かな。streamを渡しておくと決まり切った記述だけでいい感じにviewにしてくれる。ネットワークの状態とかも見られる。

InheritedModel

Providerと同じことを自分でやりたいときに使うと良いのかな?親に状態を持たせておいて子がそれを購読しているとき、子を再ビルドするかを細かい粒度で指定できる。

ClipRRect

render treeに差し込むことで子のborderRadiusを指定できる。ちなみに、同じようなことを他の形でやるClipPathやClipOvalというWidgetもある。

Hero

routes間の遷移で2つのWidgetを紐づけておくとアニメーションでいい感じにしてくれる。

CustomPaint

低レベルなWidget。独自の見た目/動きを持ったWidgetを使いたいときにこいつをextendsして使う。

Tooltip

Widgetをタップしたときに吹き出しをpopupしてくれる...だけでなく、読み上げとかにも対応しているのでアクセシビリティの観点からも良い。

FittedBox

あるWidgetを別のWidgetにfitさせたいときに使う。fitのさせ方は色々選べる。

LayoutBuilder

バイスの大きさ/向きによって表示するWidgetを出し分けたいときに使うのかな?

AbsorbPointer

すべての配下のWidgetについてタッチを無効にする。

Transform

いろいろなWidgetの形を変えたり、移動させたり、角度を変えたりできる。こいつを使うと3Dっぽいやつも含めていい感じのアニメーションが作れそう。

BackdropFilter

ImageFilterを適用するためのWidget。ぼかしたり歪ませたりなどの画像の加工ができる。Stackと合わせて使う(他のWidgetの上にかぶせる)と良い。

Align

Widgetの配置を決めるやつ。Centerのカスタムできる版みたいな感じ。

Positioned

Stackの中でWidgetの配置を細かく決めたいときに使う。top/bottom/left/right/height/widthがプロパティにあってcssのように位置が指定できる。

AnimatedBuilder

アニメーションを行う方法の1つ。animationとchildを渡すとアニメーションしてくれる。

Dissmissible

リストの要素を横にswipeして消すときに使えるやつ。方向を指定できるのでswipeの方向の左/右によって操作を変えることも可能。

SizedBox

Widgetを特定のサイズにしたいときに使う。SizedBox.expandを使うと可能な限り大きくなるWidgetを作れる。2つのWidget間に決まったスペースを作りたい場合にPadding/Marginではなく空のSizedBoxを置くこともある。

Draggable

ドラッグ&ドロップの実装に使う。ドロップ先にはDragTargetを置いておく。ドロップ時に何が起こるかはDragTarget側に指定する。

AnimatedList

リストの要素を削除したり編集したりするとき、急に表示が変わると何が起こったのかわからないことがある。AnimatedListにanimationを渡すことでいい感じにしてくれる。

Flexible

flex値を指定しておくと親の大きさに合わせていい感じにスペースを占有してくれる。Expandedとの違いは、余白を完全に埋めないことができることらしい。

flutterで均等にwidgetを並べる|Captain_PAG|note

MediaQuery

バイスに関する情報を取得してくれる。画面サイズや文字サイズ、アニメーションを有効化してるかなど。

Spacer

Row/Columnの要素の間に差し込むことで並び方をカスタムできる。flexプロパティを指定できる。

AnimatedIcon

animationを渡すことでiconをアニメーションで変化させることができる。

AspectRatio

Widgetを特定のアスペクト比で表示したいときに使う。Expandedのchildとして使うとうまく動かないが、間にAlignを挟むことでちゃんとアスペクト比が保たれるようになる。

LimitedBox

Containerのように親がサイズを指定するWidgetは、子のサイズを指定しないWidgetの子になるとうまく動かない。このときにLimitedBoxを使ってサイズの最大値を指定しておくと良い。親がサイズを指定してきた場合にはLimitedBoxは何もしない。

Placeholder

placeholderです。何も指定しなければ可能な限り余白を埋める。

RichText

1行のテキストの中でTextStyleを変えたいときにはRichTextのchildをTextSpanにするとできる。文中にリンクを差し込みたいときとかにも使える。

ReorderableListView

ドラッグ&ドロップで要素の順番を変えることができるリスト。

AnimatedSwitcher

あるWidgetを別のWidgetに置き換えたいときに使う。例えばAnimatedSwitcherのchildを変数にしておいて、setStateでchildに代入すればFadeアニメーションでWidgetを入れ替えてくれる。遷移のアニメーションは好きなようにカスタムできる。

AnimatedPositioned

paddingをアニメーションで変えることができる。curve/durationを指定して動きをカスタムできる。

IndexedStack

表示するWidgetを切り替えたいときに使う。切り替えたときに状態を保存しておいてくれる。タブ切り替えとかに使うのかな?

Semantics

Widgetにメタ情報を足すためのWidget。足した情報はアクセシビリティツールや検索エンジンが使う。

ConstrainedBox

Widgetのサイズの最大値や最小値を決める。

AnimatedOpacity

WidgetをFade in/Fade outさせることができる。これを使ったより高レベルなWidgetにFadeTransitionがある

FractionallySizedBox

サイズを親Widgetに対する割合で指定することができる。

DataTable

いい感じの表を作れる。項目でソートできたりする。

Slider

スライダーです。RangeSliderで両端をスライドさせて範囲を選べるようにできる。

AlertDialog

alertです。警告を表示するだけでも良いし、ユーザアクションを取らせることもできる。showDialogで描画する。iOS風にしたければCupertinoAlertDialogを使う。

DefaultTabController & TabBar

タブ切り替えを作れるやつ。

モデリングカフェの問題をやってみる:第5回〜第8回

誰にも頼まれてないのに新卒研修みたいなことをやっていくシリーズの第2弾です。

第5回:すごろく

問題

すごろくをモデリングしてください。

不足する情報は適宜補っていただいて結構です。補った情報は、コンセプトに記述してください。

f:id:muijp:20200104114439p:plain

自分の回答

コンセプト

  • すごろくは「ふりだし」「あがり」を含めて2つ以上のコマからなる
  • コマの内容は以下
    • ふりだし/あがり
    • Nコマ進む/戻る
    • ふりだしに戻る
    • M回休み
  • すごろくのプレイヤーは一度に1つのすごろくまでしか遊べないものとする
  • プレイヤーは次にサイコロを降るプレイヤーを持つ。ゲームの途中でプレイヤーが抜けたり入ったりした時はこの関係を付け替える

クラス図

f:id:muijp:20200104143137p:plain

回答例・解説を読んだ感想

  • すごろくに分岐がないということをコンセプトに明記するべきだった(すごろくのドメイン知識がなさすぎる)
  • 全体的に1つのクラスに役割を負わせすぎる傾向がある気がしてきたので、もうちょっと役割ごとにクラスを分ける方針のほうが良さそう
    • コマと指示を分けたほうがよかった?
    • プレイヤーとプレイヤーのコマを分けたほうがよかった?
  • コマをサブクラスで分類したのはよかった

第6回:登山ルート

問題

登山ルートをモデリングしてください。

奥岳山系の登山ルートは次のようになっています。

f:id:muijp:20200104142755p:plain

自分の回答

コンセプト

  • 登山ルートは一つの山に対して複数定義される
    • ここで言う山は、例にある「柳ヶ原山」「屏風岳」といった単位ではなくそれらをまとめたもの(一回の登山でいける範囲)をさす
  • 登山地点間を結ぶ道には注意事項をつけることができる

クラス図

f:id:muijp:20200104143119p:plain

回答例・解説を読んだ感想

  • 全体的によくできた気がする
  • 山という概念がわかりにくかったかも。山系とかにしたらコンセプトでごちゃごちゃ言う必要がなかった
  • 注意事項を地点関経路にひもづけたけど、往路・復路で注意事項は同じだと思うので冗長だった。マスターの回答のように区間と順路を分けて定義した方がきれい

第7回:カレーの作り方

問題

カレーの作り方をモデリングしてください。

不足する情報は適宜補っていただいて結構です。補った情報は、コンセプトに記述してください。

f:id:muijp:20200104143348p:plain

自分の回答

クラス図

f:id:muijp:20200104145652p:plain

回答例・解説を読んだ感想

  • 根本的に勘違いしていて、手順は一連の文章で良いかと思ったらそこをちゃんとモデリングしろという話だった。まあこういうこともあるでしょう
  • 回答が高度すぎる

第8回:サンドイッチ

問題

サンドイッチをモデリングしてください。

不足する情報は適宜補っていただいて結構です。補った情報は、コンセプトに記述してください。

f:id:muijp:20200104145905p:plain

自分の回答

コンセプト

  • サンドイッチは形・パン・中身の材料から決まる
  • 材料は素材とその量、挟む順番で決まる

クラス図

f:id:muijp:20200104150904p:plain

回答例・解説を読んだ感想

  • 中間にパンが挟まれるサンドイッチのことを考慮漏れしていた...
  • サンドイッチクラスにパンと材料をまとめさせるよりも、みんなの回答みたいにパンと材料の関係だけでサンドイッチを表現させる方が良いのかな?でもその場合サンドイッチの名前とか形を表すのが難しくなると思う
  • 分量をわざわざクラスにする必要はなかったのかも

モデリングカフェの問題をやってみる:第1回〜第4回

設計とかちゃんと勉強したことないよなと思って最近UMLモデリングの本を読みました。

UMLモデリングレッスン

UMLモデリングレッスン

  • 作者:平澤 章
  • 出版社/メーカー: 日経BP
  • 発売日: 2008/01/24
  • メディア: 単行本

一通り読んで雰囲気はわかったものの、実際やらないとわからないので適当にモデリングの演習問題を探してみたら非常に良さそうな連載があった。

www.ogis-ri.co.jp

毎回問題がモデリングの題材が与えられて、「コンセプト」と「クラス図」を回答として出していく形式。「コンセプト」はモデリング対象の捉え方と表現方法を文章にしたものらしい。この説明だけだとよくわからないが、数回実際にやってみて、問題文だけだと情報が完全には与えられていないので、解答者の側で足りない情報を想定する必要があるんだけどその想定を書くのが「コンセプト」だと理解しました。

今日は第1回から第4回までやりましたがけっこう面白い。UMLモデリングの本を読まないままやってたら悲しくて挫折してたと思うので読んでおいてよかった。

第1回:連絡網

問題

ゆうじ君たちは以下のように連絡をとることになっています。 この状況をモデリングしてください。

自分の回答

コンセプト

  • 連絡網は生徒が連絡し合うことによって成り立つ
  • 生徒は別の生徒から連絡を受け、またさらに別の生徒に連絡をする
  • ただし、連絡網の起点となる生徒は連絡を受けることはなく、終点となる生徒は連絡をすることはない

クラス図

f:id:muijp:20200102225643p:plain

回答例・解説を読んだ感想

みんなの回答めちゃくちゃちゃんと考えてあってすごい。自分は連絡手段や連絡内容には完全に考えが抜け落ちていたので反省。そして、コンセプトの書き方がいまいちわからないです。

第2回:社員と辞令

問題

ソフトウェアを受託開発するイラピス株式会社では、2006 年 1 月に以下の辞令が発令されました。

  • 山田さんはジュニアSEに昇格しました

  • 田中さんは技術2部に配属されました

  • 花山さんはミドルSEに昇格しました

社員と辞令の関係をモデリングして下さい。 ただし、職級に関することなど不足する情報は適宜補っていただいて結構です。

自分の回答

コンセプト

  • 1つの辞令ではある社員の職位・部署のいずれかもしくは両方が変更される
  • 部署の変更には異動や兼務追加・兼務解除があり得る
  • 職位は1つの社員につき同時に1つで、部署に関わらないグローバルな概念であるとする

クラス図

f:id:muijp:20200102230538p:plain

回答例・解説を読んだ感想

兼務とかを考慮できたのはよかったと思った。しかし、この時は職位をグローバルなものとして考えたんだけど、よく考えたら普通に部署に職位が紐づいていた方が普通だよなという気分になってきた。例えば、A部の部長とB部のエンジニアを兼務することになるとかがあり得そうなので。

解説の、種類(例えば、「部署」に対する「技術2課」)をサブクラスにするかインスタンスにするかの議論がわかりやすくて、種類ごとに異なる情報や振る舞いを持っていたらサブクラスにすると良いらしい。

回答2の

社員は入社すると格付辞令・配属辞令により、いずれかの職級に格付けされ、いずれかの部門に配属される。

という考えは面白いと思った。こうしてしまうと必ず直前の辞令をみることでその社員の現在の役職と職級がわかる、と。

第3回:部品の構成

問題

型番 D0101 の机の部品構成は以下のとおりです。

  • 机 (D0101) は天板 (T0212) 1つ、脚 (F0132) 4つ、引き出し (H0303) 1つで構成されます

  • 引き出し (H0303) は、箱 (B0505) 2つ、棚 (TA0635) 1つで構成されます

  • 天板 (T0212) や引き出し (H0303) などは他の製品でも利用されます

※ ( ) 内は型番

この例を参考に部品の構成をモデリングして下さい。 不足する情報は適宜補っていただいて結構です。補った情報は、コンセプトに記述してください。

自分の回答

コンセプト

  • 型番が付いているものは全て製品として扱う
  • 製品は複数の製品から構成されることがある
  • 製品と、その製品を構成する製品の関係を製品構成として表す

クラス図

f:id:muijp:20200102231545p:plain

オブジェクト図

これはクラス図を見るだけだとよくわからない系なのでオブジェクト図も作った。

f:id:muijp:20200102231558p:plain

回答例・解説を読んだ感想

これはうまくモデリングできたのでよかった。といっても、直前に読んだUMLモデリングレッスンにほぼそのままの例題が載ってたからなんですが...。「型番が付いているものは全て製品として扱う」としたのはやりすぎ感もあるけど、クラス図としてはシンプルになる。マスター(誰?)の回答では、製品を「単品目」と「ユニット」に分けていてなるほど太郎になった。

第4回:タイムテーブル

問題

2006 年 4 月 7 日(金)にダイエットセミナーが開催されます。 プログラムは次の通りです。 この例を参考に、セミナーのタイムテーブルをモデリングしてください。 不足する情報は適宜補っていただいて結構です。補った情報は、コンセプトに記述してください。

自分の回答

コンセプト

  • トラックは会場と1対1でひもづく
  • プログラムは食事と講演に分類できる
  • 講演には一人以上の講演者がいるものとする

クラス図

f:id:muijp:20200102232129p:plain

回答例・解説を読んだ感想

回答例と違って、自分は講演者が複数の講演をすることもしたらそれを追跡したいと思ってエンティティにした&パネルディスカッションもあるかなと思って講演に複数の講演者をひもづけるようにした。問題文の例に昼食の時間があったので「食事」という完全に謎のクラスを作ってしまったけど「休憩」とかでよかったですね...。マスター(誰?)の回答では休憩を明示的に管理してなかったけど、それは微妙かもと感じた。

カード決済のレシートに印字される6桁の承認番号について

こんにちは、この記事は Kyash Advent Calendar 2019 の17日目です。

以下の2枚のレシートは別の日に同じエクレアを買ったときのものです。エクレアは同じですが決済の方法が違って、左のレシートは現金、右のレシートはKyash Visaカードで決済したものです。

f:id:muijp:20191215125011j:plain

右のKyash Visaカードで決済したレシートにのみ、承認番号と呼ばれる6桁の数字が印字されていますね(赤枠部分)。Kyash Visaカードに限らず、一般にカード決済したレシートにはこの承認番号が印字されます。今日はこの承認番号がどういうもので、どのように使われるのかの一例について書きたいと思います。

オーソリゼーションとクリアリング

まず、カード決済の流れについて簡単に紹介しておきます。カード決済には大きく分けて2つのステップがあります。記事の一番下に図が貼ってありますので、適宜参照ください。

1つ目のオーソリゼーションというステップでは、カード発行会社(カードを"issue"するのでイシュアと呼ばれています)がカード会員が行おうとしている決済を承認もしくは拒否します。会員が加盟店でカードを切ると、オーソリ電文と呼ばれるメッセージが店頭の決済端末からカードの国際ブランド(VisaやMastercardなど)のネットワークを介してイシュアに送られます。電文を受け取ったイシュアは、その内容を見て取引を承認するか拒否するかを決め、結果を加盟店に伝えるため応答電文を送り返します。判断基準としては、会員の決済額が上限額を超えていないかや、カードの有効期限が切れていないかなどが挙げられます。

Kyash Visaカードを使った取引のオーソリゼーションでは、KyashユーザがコンビニやECサイトなどのVisa加盟店でカード決済をすると、Visaのネットワークを介してKyashにオーソリ電文が到着し、Kyashが取引を承認もしくは拒否するという流れになります。ちなみに、クレジットカード決済のオーソリゼーションでは本当に決済可否の判断を行うだけでカード会員の口座残高は動かないのですが、Kyash Visaカードプリペイドカードなのでオーソリゼーションの段階でユーザの残高から決済額を差し引くという違いがあります。このあたりのプリペイドカード・デビットカード特有の事情については Kyash Visaカードはなぜガソリンスタンドやホテルで使えないのか でも紹介されています。

さて、レシートに印字されている6桁の承認番号に話を戻しますが、これはオーソリゼーションでイシュアが取引を承認したときに発行する番号です。何らかの理由で取引を拒否した場合は承認番号が発行されないので、承認番号はイシュアが取引を承認したことの証拠であると考えることもできます。承認番号がどのように使われるかの話をする前に、カード決済のもう一つのステップを説明しておきます。

カード決済の2つ目のステップはクリアリングと呼ばれ、売上を確定させるためのものです。オーソリゼーションではあくまでイシュアが取引を承認(許可)するだけなので、決済額は確定していませんし、その後決済自体がキャンセルされることもあります。売上を確定させるために、加盟店はオーソリ電文とは別に売上電文と呼ばれるメッセージを送信します。一般に、イシュアに売上電文が到着するのはオーソリ電文が到着した数日後であることが多いです。売上電文が届くとイシュアはクリアリング業務を行うのですが、Kyashではその一環として売上電文を同じ取引のオーソリ電文と紐づけるマッチングという処理を行います。

オーソリ電文と売上電文のマッチング

マッチングでは売上電文とオーソリ電文を紐づけて、売上の確定や Kyashポイント の付与などの処理を行います。また、稀に為替レートの変動などが原因でオーソリ電文と売上電文に記載されている決済額が異なる場合があるのですが、その場合は売上電文の方の決済額が確定した額なので、そちらに合わせてKyash残高の調整を行います。

さて、そもそもどのように売上電文と同じ取引のオーソリ電文を見つけるのかについて説明します。記事の最初に貼ったレシートの例で考えると、同じような時間に同じ店で同じエクレアを購入した人は何人かいるかもしれないので、決済の内容やタイミングだけではマッチングはできません。そこで2つの電文の紐付けに利用されているのが、レシートに印字されている承認番号です。

すでに書いたように、承認番号はオーソリゼーション時にイシュアが取引を承認する際に発行し、加盟店に伝える番号です。実は、クリアリングで加盟店から送られる売上電文には、オーソリゼーション時にイシュアが発行した該当取引の承認番号が含まれているのです。Kyashのデータベースには生成された承認番号がオーソリ電文と紐づいた状態で保存されているので、売上電文に含まれる承認番号から、その取引のオーソリ電文を探すことができます。もちろん、承認番号は6桁しかないため承認番号が同じだからといって同じ決済と判断することはできず、他にもカード番号や決済額などの情報を鑑みてマッチングのロジックを組んでいるのですが、いずれにしても承認番号は同じ取引を探し出す上で重要な役割を果たしています。

f:id:muijp:20191215204334p:plain

まとめ

  • カード決済には大まかにオーソリゼーションとクリアリングの2段階がある
  • オーソリゼーションでイシュアが許可した取引には承認番号が発行され、レシートに印字される
  • Kyashではクリアリングにおいてオーソリ電文と売上電文のマッチングを行なっており、そこで承認番号が使われている

この記事で紹介したのはKyashが行なっている決済業務の中でもほんの一部に関する豆知識のようなものですが、もうすぐ発売の「WEB+DB PRESS Vol.114」では、決済の全体像に加えて、Kyashの裏側のアーキテクチャやインフラ設計、セキュリティについても書かれています。よかったら読んでみてください。