LoginSignup
6
5

More than 5 years have passed since last update.

Serverless+TypeScript+Github Projectsを使ったToDoリスト通知サービス

Last updated at Posted at 2018-05-10

これまで様々なToDo管理ツールを試してきましたが、どれも少し使ってはやめるということを繰り返してきました。
最近使い始めてこれは続きそうだと感じているのが、GithubのProjects機能を利用したToDo管理です。
Githubは仕事で毎日使っていてインタフェースに慣れているし、Projects機能を使ってみたかったというのもあります。

こんな感じでプライベートや仕事のToDoを管理しています(ToDo少な...)。

todo-list.png

他のToDo管理ツールにはよくあるリストの通知機能が欲しくなったので、Serverless(AWS)とTypeScriptで作ってみました。

システムの全体像

GithubからToDoリストを取得してメール送信するというプログラムを、AWS Lambdaで動かしています。
LambdaはCloudWatchEventsで毎朝9時に起動され、SESを経由して自分のGmailにHTMLメールを送信するという流れです。

送られてくる内容のイメージはこんな感じです。
個人で使うだけなのでデザインは一切なし!そのうち考える!

email.png

Github APIからToDoリストを取得する

GithubAPIのクライアントライブラリは@octokit/restを使用しました。
TypeScriptの宣言ファイルもしっかり用意されているので、補完がきいて楽に書けます。

例えばあるリポジトリ上のProject一覧の取得は以下のように書きます。

const {data: projects} = await octokit.projects.getRepoProjects({
    owner: 'リポジトリオーナー名',
    repo: 'リポジトリ名'
});

ToDoのタイトルを取得するまでの長い道のり

プロジェクト一覧を取ってくればその中にカラム名(To doIn progress)や各ToDoのタイトルが入っているだろうなどと甘い考えでいたら、これがそうではなくToDoのタイトルを取得できるまで多くのAPIを叩かなければなりませんでした。。

  1. プロジェクト一覧を取得する: octokit.projects.getRepoProjects()
  2. プロジェクト内のカラム一覧を取得する: octokit.projects.getProjectColumns()
  3. カラム内のカード一覧を取得する: octokit.projects.getProjectCards()
  4. カードの詳細を取得する: octokit.projects.getProjectCard()
  5. カードの詳細データからIssueのURLを取り出し、Issueを取得する: octokit.issues.get()
  6. Issueの詳細データからタイトルを取り出す

Issue(ToDo)の数が多ければ多いほど叩くAPIの数も増えてしまうので、ToDoが増えてくるとこのやり方は破綻しますね。。
SearchAPIを使う方法も検討しましたが、このAPIだとIssueとProjectの関連付けがされていないため、IssueがどのProjectやカラムに紐付いているのかがわからず、やりたいことが実現できませんでした。

うまいやり方ご存知の方、是非ご教授ください :bow: :sweat_drops:

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だけ追加しています。

webpack.config.js
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の設定も特に特別なことはありません。

serverless.yml
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に悩まされました:expressionless:

@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管理を便利にする機能を作っていこうと思っています。

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5