これまで様々なToDo管理ツールを試してきましたが、どれも少し使ってはやめるということを繰り返してきました。
最近使い始めてこれは続きそうだと感じているのが、GithubのProjects機能を利用したToDo管理です。
Githubは仕事で毎日使っていてインタフェースに慣れているし、Projects機能を使ってみたかったというのもあります。
こんな感じでプライベートや仕事のToDoを管理しています(ToDo少な...)。
他のToDo管理ツールにはよくあるリストの通知機能が欲しくなったので、Serverless(AWS)とTypeScriptで作ってみました。
システムの全体像
GithubからToDoリストを取得してメール送信するというプログラムを、AWS Lambdaで動かしています。
LambdaはCloudWatchEventsで毎朝9時に起動され、SESを経由して自分のGmailにHTMLメールを送信するという流れです。
送られてくる内容のイメージはこんな感じです。
個人で使うだけなのでデザインは一切なし!そのうち考える!
Github APIからToDoリストを取得する
GithubAPIのクライアントライブラリは@octokit/restを使用しました。
TypeScriptの宣言ファイルもしっかり用意されているので、補完がきいて楽に書けます。
例えばあるリポジトリ上のProject一覧の取得は以下のように書きます。
const {data: projects} = await octokit.projects.getRepoProjects({
owner: 'リポジトリオーナー名',
repo: 'リポジトリ名'
});
ToDoのタイトルを取得するまでの長い道のり
プロジェクト一覧を取ってくればその中にカラム名(To do
やIn progress
)や各ToDoのタイトルが入っているだろうなどと甘い考えでいたら、これがそうではなくToDoのタイトルを取得できるまで多くのAPIを叩かなければなりませんでした。。
- プロジェクト一覧を取得する:
octokit.projects.getRepoProjects()
- プロジェクト内のカラム一覧を取得する:
octokit.projects.getProjectColumns()
- カラム内のカード一覧を取得する:
octokit.projects.getProjectCards()
- カードの詳細を取得する:
octokit.projects.getProjectCard()
- カードの詳細データからIssueのURLを取り出し、Issueを取得する:
octokit.issues.get()
- Issueの詳細データからタイトルを取り出す
Issue(ToDo)の数が多ければ多いほど叩くAPIの数も増えてしまうので、ToDoが増えてくるとこのやり方は破綻しますね。。
SearchAPIを使う方法も検討しましたが、このAPIだとIssueとProjectの関連付けがされていないため、IssueがどのProjectやカラムに紐付いているのかがわからず、やりたいことが実現できませんでした。
うまいやり方ご存知の方、是非ご教授ください
HTMLメールの送信
ToDoリストが取得できたら、それをHTMLに反映させます。
テンプレートエンジンはPugを使用しました。
後述しますが、Webpackとpug-loaderを使用することで、以下のようにHTMLテキストを生成できます。
const emailTemplate = require('./templates/html.pug')
const html = emailTemplate({projects: todoList})
require('./templates/html.pug')
はHTML生成用の関数をロードします。
この関数にToDoリストデータを渡すことで、HTMLにToDoリストが描画されます。
デプロイ(Serverless+Webpack)
実装はTypeScriptで行ったので、これをコンパイルしてからLambda上にデプロイする必要があります。
デプロイにはServerlessプラグインのserverless-webpackを使用しました。
Webpackの設定はserverless-webpackのサンプルから持ってきたものをほぼそのまま使いました。
pug-loaderだけ追加しています。
const path = require('path');
const slsw = require('serverless-webpack');
module.exports = {
entry: slsw.lib.entries,
resolve: {
extensions: [
'.js',
'.json',
'.ts',
'.tsx'
]
},
output: {
libraryTarget: 'commonjs',
path: path.join(__dirname, '.webpack'),
filename: '[name].js'
},
target: 'node',
module: {
rules: [
{
test: /\.ts(x?)$/,
use: [
{
loader: 'ts-loader'
}
]
},
{
test: /\.pug$/,
use: [
{
loader: 'pug-loader'
}
]
}
]
}
};
Serverlessの設定も特に特別なことはありません。
service: todo-notification
plugins:
- serverless-webpack
provider:
name: aws
runtime: nodejs8.10
region: ap-northeast-1
profile: ${opt:profile}
iamRoleStatements:
- Effect: 'Allow'
Action:
- 'ses:SendEmail'
Resource:
- 'arn:aws:ses:us-east-1:*'
functions:
notifyTodos:
handler: src/index.notifyTodos
events:
- schedule:
name: ${self:service}-${opt:stage, self:provider.stage}-notifyTodos
rate: cron(0 0,23 * * ? *)
timeout: 20
memorySize: 256
詰まったところ
実装はぶっちゃけ大したもんじゃないです。
一番時間がかかったのはWebpackでした。
Webpackに悩まされました
@octokit/restをWebpackでバンドルすると実行時エラーが起きる
@octokit/restの最新バージョン(この時点ではv15.4.0)をWebpackでバンドルすると、内部で使われているnode-fetchモジュールの実行に失敗しました。
fetch is not a function
というエラーになるのです。
一旦バージョンをv15.1.3まで落とすことで回避できました。
この問題に対してはすでにPRも出ていて、すぐに対応されそうです。
https://github.com/octokit/rest.js/issues/830
Webpackのrequire with expression問題
最初はHTMLの生成にemail-templatesというライブラリを使おうとしていました。
スターも結構ついていて人気そうだし、HTMLメールのプレビュー表示なんかもしてくれて便利だったので使っていたのですが、これまたWebpackでバンドルしようとした時に問題が置きました。
依存しているライブラリにconsoliate.jsというのがあって、この中でモジュールの動的インポートをしている関係で、Webpackが依存関係をうまく解決できずエラーになってしまうのです。
ERROR in ./node_modules/consolidate/lib/consolidate.js
Module not found: Error: Can't resolve 'atpl' in '/path/to/todo-notification/node_modules/consolidate/lib'
@ ./node_modules/consolidate/lib/consolidate.js 573:51-66
@ ./node_modules/consolidate/index.js
@ ./node_modules/email-templates/lib/index.js
@ ./src/index.ts
この問題はIssueに上がっていて、紹介されている解決方法も試してみたのですがうまくいかず、最終的にpug-loaderを使う方法を選択しました。
https://github.com/tj/consolidate.js/issues/295
最後に
Github ProjectsでのToDo管理はシンプルで使いやすいですが、一方でtodoistのようなToDo機能に特化したサービスに比べると、通知機能やスマホでの操作性といった点では劣ります。
しかし、GithubのAPIやIntegration機能を利用すれば、自分独自のToDoサービスが作れるという魅力もあります。
今後もGithub ProjectsでのToDo管理を便利にする機能を作っていこうと思っています。