修行編 - 5日目

やったこと

1. Next.jsやめました

ReduxとReactの接続でいろいろと試しているときに、詰まってる問題がReac由来なのかNext由来なのかが分からず思い切ってやめました。

getInitialPropsの挙動とか、専用のプラグインの挙動がかなりのブラックボックスで自分には早すぎたようです。もう少しReactスキルがアップしてから再挑戦したいと思います。

アプリは $ npx create-react-appで再度作り直しました。

2. redux-sagaについてひたすら考える

ひたすら考えてました。

そもそもSagaが提唱された経緯から thunkとの比較、 observable epicとの比較、型安全な書き方、フォルダ構成、テストの書き方、Generator構文の復習などをやっていたらあっという間に1日が過ぎてしまいました。

正直今やっているプロダクト程度ならThunkで十分なのですが、Sagaの書き心地が知りたいのでもう少し粘ってみます。

明日こそは実装に移りたい・・・

(*余談ですがSagaの紹介記事でやたらと「Thunkはコールバック地獄が厳しい」みたいな記述を見たけど、 async/awaitで書いたら大して変わらへんやんけというお気持ちになりました)

3. Reducerの書き方を変えました

一度はtypesafe-actionsで記述してみましたが、 actionCreator内での型定義やReducerの書き心地などの理由でtypescript-fsaおよびtypescript-fsa-reducersに乗り換えました

github.com

github.com

こんな感じで書けます

//  actions.ts
import actionCreatorFactory from 'typescript-fsa'

export const receiveTodos = actionCreator<ReceiveTodosActionPayload>(Types.RECEIVE_TODOS)

// reducer.ts
import { reducerWithInitialState } from "typescript-fsa-reducers"
import * as actions from './actions'
import { TodoState } from './types'

const initialState: TodoState = {
  items: []
}

const reducer = reducerWithInitialState(initialState)
    .case(actions.receiveTodos, (state, {items}) => {
      return {
        ...state,
        items
      }
    })

export default reducer

VSCode上での補完もバッチリ効いてナイスです。Async Actionsの書き方は要検証。

4. API疎通テスト(失敗)

Reactアプリケーションを localhost:3000で動かしているのに対してSwaggerで生成したモックサーバーは localhost:8080で動いているため、当然のごとくCORSで怒られました。React Nativeの開発をしている時は全然気がつかなかった・・・

改めてSwagger Specを読んで当該レスポンスに headers -> Access-Control-Allow-Origin: *を追記したのですが何故か適用されず🤔

Swagger Editor上では構文エラー等出ていないのですがなんでなんでしょうか。

明日もう一度試してだめならExpressからハードコーディングしたダミーレスポンスを返してみます

5. おひす訪問

恵比寿の某社にお邪魔してピカピカのおひすを案内していただきました。気になっていたチョコレートもついでに頂戴。

肝心のミーティングはというと、「デザイナーさんとかエンジニアさんと話してみたいっすwww」と舐めたお願いをしたことをひたすら反省するアレでした。

無防備でお邪魔したせいでこっちが面接してるのかあっちが面接してるのかよく分からない感じになっちゃいましたね。本当に申し訳ないです。

お忙しいなか時間をいただき、本当にありがとうございます。

6. 恵比寿探索

普段あまり足を運ぶ機会がない恵比寿なのでいくつかカフェ巡りをしました。

3つほど訪れましたが、本日のベストはessence cafe

https://essence-cafe.jp/

表向きは花屋と見紛う可愛らしさですが(実際にお花も売っていた)、B1フロアには電源とWi-Fiが完備されており作業スペースに持ってこいでした。

間食でいただいたミートパイも美味しかったです。

7. 会食

Vueコミュニティがきっかけで仲良くなった友人たちとご飯に行きました。

600円のスペアリブがめちゃめちゃ美味しかったです。

とてもワクワクする野望が聞けたので僕も気を引き締めて頑張らなきゃなと思いました。また近いうちにご飯しましょう!

8. ランニング

雨の上がった12:30ごろから深夜ランへ。

1kmを5分フラットで走ってみたくていつもより速めのペースで飛ばしたら、2km付近で見事にバテました...orz

徐々にペースを上げていって、ある程度のスピードに乗ったら均一の速度をキープするのが大事だなという学びです。

まあ今日は恵比寿から渋谷まで歩いたり、お酒も飲んだ後でのランなので疲れていて当然という気もします(言い訳)

明日はずっと雨のようなので休息日にするのもアリかもしれません。

明日やること

明日も本日と同じくオフィス訪問 & 会食の予定が入っています。そろそろ梅雨本番といった感じで天気もイマイチですが、粛々と進捗を生めたらなと思います。

  • オフィス訪問 & 会食
  • Redux Sagaの実装
  • API疎通テスト
  • Render List View

そういえばまだDead Poolを観に行けてない・・・

明日も良い1日になりますように

修行編 - 4日目

やったこと

1. Redux with TypeScriptの導入

最低限動くカタチで実装しました。

「ReduxはVuexと比べてTypeScriptとの相性最高!」って言ってた人いたけどマジかよって感じです。

今回はこちらの typeafe-actionsというライブラリをAction CreatorとReducerで利用しました。

github.com

Next.jsとの接続はこちらの next-redux-wrapperでやっています。

github.com

毎回Storeの実装について考えるのも結構大変なので自分向けにRedux Boilerplateを作成しました。

github.com

自分で書いておきながらそもそもこれが型安全なのかどうか不安でいっぱいなので、温かいツッコミをお待ちしています。

2. API Clientの作成

某所で利用していたAPI Clientをそのまま持ってきました。

こちらも時間のあるときにいろいろなOSSの実装を見て勉強したいです。ロギングとかエラーハンドリングとか全然分かっていないので。

認証の仕組みについてはバックエンド側(Django)の仕様が未定なので実装を保留にしています。おそらくJWTかな。

github.com

3. Redux Sagaについて調べる

Thunkは以前利用していたものの、Sagaは未経験だったのでこの機会に入門。

API通信時の非同期処理だけでなく、様々なSide Effectsを管理できるmiddlewareのようですね。ジェネレーターを実践でゴリゴリ書いたことが無いので少し慣れが必要かも。

sagaのイメージ Sagaのイメージ図↑

まだまだ「全然わからん」というレベルなので公式のドキュメントと合わせてこちらの記事なんかも読みながら明日もう少し勉強したいです。

redux-sagaで非同期処理と戦う

4. ランニング

継続中です。

20時ごろの駒沢公園は花火に興じる子どもなども居て風情があります。

5. NBA Finals Game 2観戦

2-0だぜ!やったぜ!!

破られるのも時間の問題だろうなーと思ってた1試合最多3ポイント成功記録もCurryがあっさり更新。Greenのプレイメイキングが攻守に渡って冴えまくっていたり、ベンチメンバーの奮闘もあったりとファン冥利に尽きる一戦でした。

次回の第3戦はCLEでのアウェイマッチ。正直連勝はキビしいだろうとは思いますが、なるべくLeBronを消耗させるようなディフェンスが観れるのを楽しみにしています。

6. SES-12打ち上げミッション

Space XによるSES-12打ち上げミッションをライブで鑑賞。

www.youtube.com

夜間の打ち上げということでいつもほどの盛り上がりはありませんでしたが、相変わらず美しい仕事でした。

再利用されたBlock 4エンジンは今回は回収されていません。そろそろ全面的に耐用性に利点のあるBlock 5への移行が進んでいるようです。

次回のSpace Xによる打ち上げ予定は6/28(日本時間6/29)。ISSに向けてDragon補給ポッドを送り届けるようです。

フェアリングの回収も予定されているようなので今度こそ成功してほしい!前回は回収船のStevensまで15メールの地点に着水したようなので期待が高まります。

明日やること

しばらく会食や打ち合わせの日々が続きます。早朝や夜の時間を活用して生産性のレベルを維持していきたいです。

  • 会食 & オフィス訪問
  • Saga middlewareのリサーチ & 実装
  • ReactとReduxの接続
  • API接続テスト

みなさん良い1日を

修行編 - 3日目

やったこと

1. 東京へ移動

初夏の京都を満喫したので東京に戻りました。

家族と有意義な時間が過ごせて良い週末でした。

2. React in pattersを読む

新幹線の中でようやく読み終わりました。

Introduction · React in patterns

改めて基本的なReactのコンポーネント実装パターンやDIパターン、Fluxデザイン等を体系的におさらいできて勉強になりました。

3. Redux with TypeScriptの実装パターンを調べる

TSでReduxを書いたことがないので改めて調査しました。 Statetypesのハンドリングは大体理解できたかと思います。

また、改めてStoreのディレクトリ構成についてもお勉強。

主に以下の2つが参考になりました。

dev.to

github.com

後者についてはReactコンポーネントをTSで記述する方法も詳しく解説してくれているので最高でした。

実装は明日行う予定です。Thunkしか使ったことないからsagaの構成も調べなきゃ・・・

4. Viewの実装

ダメダメです。

昨日導入したMaterial UIの表現力ではやはり不十分なので独自でCSSを定義してあげる必要があるのですが、どこに書いてもしっくりこない・・・

CSS in JS, Styled Component, Styled JSXを全て試してみましたがキモすぎる・・・

JSXでDOMをJSとして管理しようぜ!というノリは理解できます。実際相性良いし。

ただしCSS、てめーはダメだ。

JSで扱うことの恩恵よりデメリットの方が大きく感じられてなりません。

取り合えずコンポーネントごとにStyles Objectを定義してその中で愚直に記述することにしました。

これ、React以外のフレームワークに移行したくなったときほんとどうするんだろう

5. ランニング

なんとか3日続きました。夜の駒沢公園は気持ち良いです。

どうにか1周はできるようになったので、明日は1.5周走れるよう頑張ります。

6. ファッションポジウム

こちらの記事でも紹介されているファッションポジウムに参加するため、東京大学安田講堂まで足を運びました。新幹線の東京駅から直行です笑

www.huffingtonpost.jp

僕がこのイベントについて知ったのは、個人的にファンだった畑島楓(@kaedehatashima)さんのツイートがきっかけ。

これまでは、所謂LGBTというタグで扱われる問題について外野として眺めることしかできませんでした。ですが今日のイベントに参加したことでまた自分の世界が広がった気がします。

特定の性別を自身のレゾンテールにする人もいれば、「性別とはコンディション」と言い切る人もいる。

この社会の一員として「後天的に己の属性を選び取れる社会」「自ら枠組みの外に踏み出した人がどこまでも羽ばたける社会」を作っていかなければならんなと改めて思う素敵な機会でした。

モデルのみなさん、本当に美しかったです。

7. おかえり金井飛行士

半年間のISS(国際宇宙ステーション)滞在を完了した金井宣茂宇宙飛行士がソユーズ宇宙船で地球に帰還する様子がネット配信されました。

www.youtube.com

生憎着陸の瞬間は見られなかったのですが、パラシュートが無事に開いたときはホッとしました。

今回のライブ配信は視聴者が1万人を超えるなど徐々に宇宙熱が高まってきているようで僕は嬉しいです。

長期間のミッション、本当にお疲れ様でした!ぼくらのヒーローです。

明日やること

UIの実装が芳しくないですが、あまりマークアップにはこだわらずデータフローを重点的に攻めていきたいと思います。

  • スケジュール振り返り・再設定
  • Reduxの導入
  • API Clientの導入
  • Redux Sagaについてリサーチ

明日はせっかくの平日なのでDead Pool 2でも観に行こうかな。

それではみなさん良い月曜日を!

修行編 - 2日目

やったこと

1. Next.jsチュートリアル

公式のREADMEにしたがって簡単なSPAを作成しました。Nuxt.jsに比べて比較的シンプルな思想だなという印象です。

ルーティングは見慣れた方式ですが、Layoutの扱いが特徴的なので慣れが必要そう。HOC使うのかな?

2. TypeScriptの導入

Next.jsでTypeScriptを使うには用意されたtemplateでinitする方法もあるようですが、今回は公式で用意されている next-typescriptというプラグインを利用しました。

github.com

tsconfig.jsonの設定は公式推奨のものをそのまま利用しています。TSのバージョンは最新の v2.9.1です。

この機会に今まで雰囲気で書いていたTypeScriptもガッツリ勉強したいのですが、優先度としてはReact >> TypeScriptという感じなので、後々必要に応じて設定のチューニングを行う予定です。

ちなみに fork-ts-checker-webpack-pluginも設定しました。

3. Material UIの導入

先日のGoogle I/Oで発表されたMaterial Themingに興味があったので、Material UIを導入しました。

material-ui.com

正直Material UIには「退屈だな」「世界観が固定されてイヤだな」という抵抗感があったのですが、先日Flutterでアプリを作ってみて「ええやんけ!!」と手のひらを540°ひっくり返したところです。

わくわく。

4. React in patternsを読む(50%ほど)

改めてReact全般の知識を復習&アップデートするためにReact in pattersというサイトを読み直しています。

全て読み切るには集中力が持たず、50%ほどの進捗です。

Introduction · React in patterns

HOCを有効活用したことは無かったのですが、VueでSlotやDynamic Componentを利用したコンポーネント設計に慣れてきたのでこちらも上手く活用していきたいところ。

シュッと目を通して早く手を動かすことに時間を割きたいです。

5. 鴨川ラン

昨日久しぶりの運動をしたせいで太ももがパンパンなのですが、気合いでランニング2日目に臨みました。

やっぱり鴨川は最高だぜ!

心なしか昨日より足運びが軽い気がします。(おそらく錯覚)

明日も頑張るぞい

6. 弟の誕生会 & 京都観光

6/2は三男の誕生日。

留学中の次男を除いた家族全員とそこに祖母が加わってみんなで美味しいフランス料理をいただきました。

昼間からワインが飲める贅沢・・・

日中は叡山電鉄で八瀬に向かい新緑の瑠璃光院を拝観。

2年ぶりに訪れましたが観光客の数も増え、山門の前には行列ができていました。

f:id:andoshin11:20180603010740j:plain

境内も若干混雑はしていたものの、書院はあいも変わらず息をのむほど美しかったです。

おそらくこの緑が萌えているのもあと1~2週間ほど。拝観自体も6/15までなので、まだの方はお急ぎでどうぞ。

弾丸帰省ですが初夏の京都を感じられて大満足です。

明日やること

今日は日中にドップリ観光してしまい、あまり進捗がありませんでした。昼間からグラスで3杯もキメたし・・・

とりあえずのミッションは達成したので明日はおそらく東京に戻ります。

  • React in patternsを全て読む
  • Redux + TSの実装パターンを調べる
  • ダミーデータでUIの骨格を作る(ルーティングも)
  • 東京へ移動

来週は会食の予定が詰まってきたので、時間がある今のうちになるべく作業を進めたいです。

それでは明日も良い休日を!

修行編 - 1日目

やったこと

1. Django + Reactプロジェクトの計画

いつもは7日間のWeekly Sprintを組んで作業するのですが、今回はスピードが欲しいため3日間でSprintを切って細かく振り返りを行えるようにしました。

タスクの管理はGitHub Projectを利用しています。

github.com

2. APIドキュメントの作成

お決まりのSwaggerを利用してREST APIの雛形を作成しました。

ForkしたSwagger Editor Liveの設定と、Dockerを利用したモックサーバーの生成フローの導入までコピペでDone。

github.com

いい加減Yaml書くのツライなーという気持ちが高まってきたのでgRPC向けにprotoを書くことも考えたのですが、初めてのDjangoプロジェクトなのでベーシックなHTTP + RESTFulでがんばります

3. Flutterアプリの写経だん

inKinoというOSSのFlutterアプリが非常に完成度も高く勉強になるので、数日前から写経を行っていました。

github.com

ようやくReduxとUIの設定が終わり一通り動くところまで完成。

Viewの実装パターン等もフワッと理解できたので、近い将来にガチめなFlutterアプリをスクラッチで書くぞという機運が高まってきました。

これにてしばらくFlutterはお休みです。

4. ランニング

久々に走りました。東京に来てからは初めてです。

平日の昼間ということもあり人も少なく、外気も適温で絶好のコンディションでした。

が、、、

予想以上に体力の減退がひどく、3kmほど走ったところで死亡😇😇😇

続くかなこれ・・・

5. 帰省しました

京都に戻ってきました。

上賀茂神社で蛍を見る恒例のイベントも無事完了。月曜日まで初夏の京都を楽しむ予定です。

6. NBA Final観た

Game 1勝ちましたね。

サンキューJ.R.

明日やること

弟の誕生日だったり、入洛する祖母の案内だったりで作業時間取れないフラグがビンビン立ってます笑

  • Next.jsでプロジェクト立ち上げ
  • TypeScriptの設定
  • React in patternsを読む

頑張るやで。

それではみなさん良い週末を!

修行編 - 0日目

ひょんなことから1ヶ月ほどゆっくりする時間ができました。

久々に納期の概念から解放されるまたと無い機会なので、今一度立ち止まってカラダとアタマを鍛え直そうと思います。

カラダを鍛える

走ります。

長く続けたバスケットを止めてからというもの、サイクリングの他には碌に運動というものをしてきませんでした。

体脂肪率8%の中学時代の身体をもう一度!とまでは言いませんが、まずはお腹周りの肉を落とす&持久力をつける意味で毎日ランニングをやってみます。

幸い家から3分で駒沢オリンピック公園があるので地の利を活かしたいです。ホコリを被ったFitBitも充電しなければ・・・

アタマを鍛える

新しい武器を身につけます。

書き慣れたRuby on RailsとVue.jsをしばらく封印して、他のスキルを業務レベルで使えるようにしたいです。

まずは基礎であるPython(Django)とReactをガッツリ復習して自信を持って仕事が受けられるようにするのが目標。作りたいサービスがいくつかあるので2週間くらいでカタチにするところまでかな。

現在取り組んでいるGo + Flutterのネイティブアプリも完成させたいですが、そちらはおそらく保留。後半の2週間はせっかくなのでScalaやRustのような新しい概念を学ぼうと思います。

そのあとはKotolinもやりたいしk8sも勉強したいしML Kitも触りたいしword2vecでも遊びたいし(ry

とにかく時間が欲しい。

生活を切り詰めて+もう1ヶ月お休みするのもアリなんじゃないかという気がしてきました(フラグ

ただし欲望の赴くままにあれもこれもと手をつけると中途半端な結果になりそうなので、戦略的にスキルアップを図っていきたいと思います。

というわけで明日は・・・

  • Flutterプロジェクト(写経)をキリの良いところまで進める
  • Django + Reactプロジェクトのカンバンとマイルストーンを用意する
  • SwaggerにAPI定義を書いてモックサーバーを立てる

くらいまで進めれば最高ですね。

新幹線で京都に移動したり、両親と食事したりというイベントがあるので先行きは不安です笑

最後に

今回「ヒマ人になるよー」という話をしたところ、本当にビックリするほど多くの方から仕事のお誘いをいただきました。

プログラマとしての経歴も浅く東京に来てからの日も浅い自分にこれだけお声がけをいただけて、本当に感謝の気持ちでいっぱいです。

生憎そのすべてにはお応えできそうにありませんが、この機会にまたパワーアップしてより大きな価値を提供できるよう頑張ります。

改めて今後とも不肖Andyをどうぞよろしくお願い致します。

P.S.

関東近郊の方へ:

食事のお誘いをいただければいつでもどこでも飛んでいきます。

TwitterのプロフィールをGitで差分管理できるようにした話

f:id:andoshin11:20180501185335p:plain

ネタです。

先日こんな記事がバズっていました。

qiita.com

この記事を読んでGitHubSNSのプロフィールを差分管理して、それをもとにサーバーレスプログラムで情報を更新できたら面白いなーと思ったのが今回のモチベーションです。

仕組みとしてはこんな感じ。

f:id:andoshin11:20180501170406p:plain

CircleCIでmasterブランチの変更を検知したらBotを起動し、API経由でSNSのプロフィール情報を更新します。

GCP大好きマンなのでTypeScript on Firebase Cloud Functionsなプログラムです。

プロジェクトの立ち上げ

いつもどおりFirebase consoleからプロジェクトを新規に作成し、CLIから $ firebase initを実行します。

関連ツールのインストールやCLIの操作はこの辺を参照のこと。

f:id:andoshin11:20180501171639j:plain

Master fileの取得

今回はGitHub上にあらかじめ別のプロジェクトを作成し、そちらに profile.jsonというSNSのプロフィールを管理するMaster fileを用意しました。

github.com

JSONの中身はこんな感じ

// profile.json
{
  "twitter": "Vue lead at merpay, Inc."
}

このファイルをCloud Functions上で取得・パースするプログラムを実装していきます。 functions/src/index.tsに以下の内容を記述。

// index.ts
import * as functions from 'firebase-functions';
import fetch from 'node-fetch';

// 自分のmaster fileのurlを入れる
const SOURCE = "https://github.com/andoshin11/social-profile/raw/master/profile.json";

enum Service {
    TWITTER = "twitter",
}

interface Profile {
    [Service.TWITTER]: string;
}

/**
 * getProfile function
 *
 * @return {Profile} Profile master data
**/
const getProfile = async (): Promise<Profile> => {
    const res = await fetch(SOURCE);
    const json = await res.json();
    return json;
}

export const testFunc = functions.https.onRequest(async (req, res) => {
  try {
    const profile = await getProfile();
    res.send(profile[Service.TWITTER]);
  } catch(e) {
    console.log(e)
  }
});

$ firebase deployコマンドでデプロイしたら表示されるURLを叩いてレスポンスを確認します。

f:id:andoshin11:20180501173404j:plain

Cloud FunctionsからGitHubで管理するデータを参照できるようになりました👏👏

Twitterのプロフィールを更新する

Cloud FunctionからTwitter APIを叩いてプロフィールを更新してみます。

APIを直接叩く実装はなかなか大変なので以下のnodeパッケージを利用

www.npmjs.com

新たに functions/src/twitter.tsを作成し、以下の内容を記述します。

// twitter.ts
import * as functions from 'firebase-functions'
import * as Twitter from 'twitter'

export default class TwitterClient {
  private client

  constructor() {
    this.client = new Twitter({
      consumer_key: functions.config().twitter.consumer_key,
      consumer_secret: functions.config().twitter.consumer_secret,
      access_token_key: functions.config().twitter.access_token_key,
      access_token_secret: functions.config().twitter.access_token_secret
    })
  }

  /**
   * updateProfile function
   * updates twitter profile
  **/
  updateProfile(description: string): Promise<void> {
      return this.client.post('account/update_profile', {
          description
      })
  }
}

Twitter APIの利用に必要なAPI KeyとSecretは事前にこちらから取得し、Firebaseの環境変数設定しておいてください。

index.tsでmaster fileを取得後に以下の処理を追加

// index.ts
...
export const testFunc = functions.https.onRequest(async (req, res) => {
  try {
    const profile = await getProfile();
    const client = new TwitterClient()

    await client.updateProfile(profile[Service.TWITTER])
    res.send('success');
  } catch(e) {
    console.log(e)
  }
});

再度関数をデプロイし、 curlでエンドポイントを叩いて起動します。

こちらが関数実行前のプロフィール

f:id:andoshin11:20180501180031p:plain

そしてこちらが関数実行後のプロフィール

f:id:andoshin11:20180501180053p:plain

GitHubのデータがTwitterに反映されたのが確認できました!

IAMでセキュリティ強化

デフォルトの状態では誰でもCloud Functionsを起動できてしまいセキュリティ上よろしくないので、認証フローを追加していきます。

studio-andy.hatenablog.com

手前味噌ながら先日書いた上の記事を参考にIAMを利用した認証の仕組みを実装。 index.tsは全体的に書き直しました。

// index.ts
import * as functions from 'firebase-functions';
import fetch from 'node-fetch';
import * as Google from 'googleapis-async';
import TwitterClient from './twitter';

const SOURCE = "https://github.com/andoshin11/social-profile/raw/master/profile.json";

enum Service {
    TWITTER = "twitter",
}

interface Profile {
    [Service.TWITTER]: string;
}

/**
 * getProfile function
 *
 * @return {Profile} Profile master data
**/
const getProfile = async (): Promise<Profile> => {
    const res = await fetch(SOURCE);
    const json = await res.json();
    return json;
}

/**
 * getAccessToken function
 *
 * @param {Object} req Cloud Function request context
**/
const getAccessToken = (req) => {
  const header = req.get('Authorization')
  if (header) {
      const match = header.match(/^Bearer\s+([^\s]+)$/);
      if (match) {
          return match[1];
      }
  }
  return null;
}

/**
 * isValidUser function
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 * @return {boolean} whether the user is valid
 */
const isValidUser = async (req, res): Promise<boolean> => {
  const accessToken = getAccessToken(req);

  if (!accessToken) return false;

  const auth = new Google.auth.OAuth2();

  // Set credential
  auth.setCredentials({ access_token: accessToken });

  const bucket = functions.config().bucket.pac;
  const permission = 'storage.buckets.get';
  const options = {
    bucket,
    permissions: [permission],
    auth
  }

  try {
    const response = await Google.storage('v1').buckets.testIamPermissions(options)
    if (response && response['permissions'] && response['permissions'].includes(permission)) {
      return true;
    } else {
      return false;
    }
  } catch (e) {
    throw new Error(e)
  }
}

export const updateProfile = functions.https.onRequest(async (req, res) => {
  try {
    const isValid = await isValidUser(req, res)
    if (isValid) {
      const profile = await getProfile();
      const client = new TwitterClient();

      await client.updateProfile(profile[Service.TWITTER]);
      res.send('success');
    } else {
      res.status(403).send("The req is forbidden.");
    }
  } catch(e) {
    console.log(e)
  }
});

超絶コードが汚いのはご容赦ください。Cloud Bucketの名前を環境変数に設定するのをお忘れなく。

この状態で普通にcurlでエンドポイントを叩くとちゃんと認証エラーで怒られるはずです。

f:id:andoshin11:20180501182009p:plain

Circle CIの設定

master fileを更新するたびに手動でCloud Functionを起動するのは面倒なので、Circle CIに仕事を任せます。

Circle CIの設定を記述するのは前述のmaster fileを管理しているレポジトリの方です。

github.com

こちらののmasterブランチに変更がpushされたらCloud Functionsのエンドポイントをaccess token付きで叩けるよう、 .circleci/config.ymlに記述します。

version: 2
jobs:
  build:
    docker:
      - image: google/cloud-sdk
    working_directory: ~/repo
    steps:
      - checkout
      - run:
          name: Decode Client Secret
          command: echo "$CLIENT_SECRET" | base64 -i --decode > ./client-secret.json
      - run:
          name: Invoke Cloud Functions
          command: curl $FUNCTION_URL -H "Authorization:Bearer $(GOOGLE_APPLICATION_CREDENTIALS=./client-secret.json gcloud auth application-default print-access-token)"
    branches:
      only:
        - master

あらかじめ記事を参考にGCPの認証情報を持ったJSONbase64エンコードしてCircle CIの環境変数( $CLIENT_SECRET)に設定しておいてください。Cloud Functionsのエンドポイントも環境変数で管理しています。

設定が完了したらmasterブランチに変更を加えて差分をpush。

f:id:andoshin11:20180501184159p:plain

f:id:andoshin11:20180501184213p:plain

ビルドに成功👏👏

f:id:andoshin11:20180501184236p:plain

Twitterのプロフィールが自動で更新されました!!

TODO

今後やりたいことは以下の通り

  • コードを綺麗にする(一番大事)
  • FacebookGitHubにも対応する
  • cronでSNSを監視し、masterと差分があればpull request作成

まぁ気が向いたらやります。レポジトリはこちらです。

github.com

まとめ

Cloud Functionsはシュッと書けるのでなれると便利です。デバッグは気合いで頑張る。TypeScript全然分からないので誰か教えてください。teachaっていう素敵なサービスがあるらしいです。

TOP | teacha(ティーチャ)