Github Webhookからlambda経由でkintoneにrecord登録する効率化ツールを作ったよ


この記事は[kintone Advent calender 2021](https://qiita.com/advent-calendar/2021/kintone)の12/3を担当します！🎄

## kintoneで全てを管理したい…
弊社しいては僕らのチームでは、kintone上でタスク管理・コミュニケーションをしています。
しかしコードの管理はGithub、ビデオ会議ではzoomというように、kintoneだけでなくさまざまなwebサービスを使って業務が成り立っています。

今まではGithubで自分たちが管理しているプロジェクトのコードにissueが登録されたり、dependabotによるPull Requestが行われた際は、手動でTaskとしてkintoneに登録していました。これが非常にめんどくさい…ヒューマンエラーも出てしまう…

今回これを、Github Webhook をトリガーにしてAWS経由でkintoneにrecord登録することで、従来手動で行っていた作業を自動化したのでシェアします！

## で、どんなの作った？
基本的には以下の流れです。
1. Github webhook
2. AWS lambdaで実行
3. kintoneにGithubの情報をレコード登録。

### 1.dependabotのPR→レコード登録
僕らが管理しているプロジェクトのライブラリがバージョンアップした際に、dependabotからPull Requestが来ます。
こいつの情報を取得して、PR来ると同時にレコードを追加させたい！

cybozuのESLintのルールセットがアップデートきた時の挙動です↓

#### Github側画面
![スクリーンショット 2021-11-17 18.15.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504011/7881f1b4-a096-87b7-e7eb-b8ca6530646b.png)


#### kintone側画面
![スクリーンショット 2021-11-18 9.51.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504011/432af878-5bd5-fa82-8d11-a66dcb31e384.png)


こんな感じでライブラリの情報などをkintoneへ渡してレコード登録できました！

### 2.issueの登録→レコード登録
OSSも管理しているので、issueが来たら確認する必要があります。
kintoneでコミュニケーションしているので、kintoneで確認できると尚良い！！

#### Github側画面
![スクリーンショット 2021-11-17 18.25.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504011/b290a937-3706-e53e-f324-6d0c28a39e3c.png)


#### kintone側画面
![スクリーンショット 2021-11-17 18.25.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504011/543e42ae-bf55-ac06-7c92-a3426727909b.png)

issueのtitleとbodyをkintoneに渡し、レコードが登録されました！

## 実装内容

### 使った技術たち
- typescript
- webpack
- API Gateway
- lambda
- serverless framework
- kintone-rest-api-client

### こんな感じで実装したよ！

#### セットアップ
まずserverless frameworkのプロジェクトを作成する必要があります。
今回はtypescript + AWSでセットアップ。

```bash
$ sls create -t aws-nodejs-typescript -p github-to-kintone
```

また、バンドルとコンパイルにwebpackを使用しました。
serverless frameworkでwebpackを使用する際は少し設定が必要です。

```typescript:serverless.ts
  plugins: ['serverless-webpack'],
```

プラグインを設定し、serverless.tsに記述。

https://github.com/serverless-heaven/serverless-webpack

これで大体のセットアップが完了しました。
今後は`src/functions`配下に処理を記述していきます。

#### functionの設定などをする
httpのメソッドとfunctionの名前を命名してあげましょう。

```typescript:/src/functions/functions_name/index.ts
import { handlerPath } from "../../libs/handlerResolver";

export default {
  handler: `${handlerPath(__dirname)}/handler.main`,
  events: [
    {
      http: {
        method: "post",
        path: "function_name",
      },
    },
  ],
};

```

#### hander.tsの作成された関数の内部に書いていく
`sls create`を打った際に作成された関数があると思うのでその中に処理を記述します。

最初に送信元の検証します。
下準備として、`ruby -rsecurerandom -e 'puts SecureRandom.hex(20)'`で取得したランダムな文字列を秘密鍵としてGitubに登録してください。
Githubが送ってくるhash値がプログラム側で計算したのと同じになるか計算させます。これにより安全なリクエストかどうかわかります。詳しくはGithubの[ドキュメント](https://docs.github.com/ja/developers/webhooks-and-events/webhooks/securing-your-webhooks)をご覧ください。

```typescript:handler.ts
const hashFromGithub: string = event.headers["X-Hub-Signature-256"];
// isAllowed（）は自前で用意した検証するための関数
if (!isAllowed(SECRETS, event, hashFromGithub)) {   
  return;
}
```

eventにpayloadが入ってくるのでその情報を利用し、Github webhookの情報に応じた処理の分岐を実現。

```typescript:handler.ts
const githubEventName: string = event.headers["X-GitHub-Event"];
const eventBodyAction: string = event.body.action;
const user: string = event.body.sender.login;

if (
  githubEventName === "pull_request" &&
  eventBodyAction === "opened" &&
  user === "dependabot[bot]"
){
  // dependabot[bot]によるPull Requestの場合の処理
}

if (githubEventName === "issues" && eventBodyAction === "opened") {
  // issueがオープンした際の処理
}
```

ifのブロックの中で実際にkintoneにレコード登録していきましょう。
kintoneのREST APIを打つために今回、[@kintone/rest-api-client](https://developer.cybozu.io/hc/ja/articles/900000767263-kintone-JavaScript-Client-kintone-rest-api-client-)を使用しています。packageの詳しい使い方はここでは割愛。

```typescript:handler.ts
// rest API clientに渡すパラメータを返す部分を関数に切り出し。
export const returnParams = (
  appId: number,
  summaryValue: string,
  descriptionValue: string
): Params => {
  const params: Params = {
    app: appId,
    record: {
      Summary: {
        value: summaryValue,
      },
      Description: {
        value: descriptionValue,
      },
    },
  };
  return params;
};

// レコード登録に含めたい情報を関数に渡して、整形する。
const sendToKintoneParams: Params = returnParams(appId, alertSummary, alertDescrption);
// レコード登録
await client.record.addRecord(sendToKintoneParams);
```

そして、AWSにデプロイして挙動を確認。
serverless.tsに適切な認証情報を記述したのちに、`sls deploy`コマンド打ちましょう。

Githubの適用させたいrepositoryに行き、setting>webhookからエンドポイントのurlやevent、secretsの設定をすれば終了です。

## 個人的に大変だった部分！

### ログインするためのセキュリティパラメーター
弊社が使っているkintone環境でrest api clientを用いてログインしrecord操作するためには、[セキュアアクセス](https://www.cybozu.com/jp/service/option/index.html)のパラメーターを設定する必要があります。

↓下記のような感じでclientのインスタンスを生成することで設定できました。

```typescript
const client: KintoneRestAPIClient = new KintoneRestAPIClient({
      baseUrl: KINTONE_DOMEIN,
      auth: {
        username: KINTONE_USERNAME,
        password: KINTONE_PASSWORD,
      },
      clientCertAuth: {
        pfxFilePath: path.resolve(
          __dirname,
          "../../../certs/file_name.pfx"
        ),
        password: KINTONE_CERT_AUTH_PASSWORD,
      },
    });
```

### hashの照合
上記でも紹介しましたが、Github webhookの[セキュリティの保護](https://docs.github.com/ja/developers/webhooks-and-events/webhooks/securing-your-webhooks)を設定しました。

```typescript
const hash: string = createHmac("sha256", secrets)
    .update(JSON.stringify(event.body))
    .digest("hex");
```

## まとめ
serverless frameworkを使うとlambdaが扱いやすくなるし、kintoneとlambdaの相性は抜群です！
ぜひ他のサービスとkintoneを連携してみてください！

## 参考リンク
- [Serverless Frameworkの使い方まとめ](https://serverless.co.jp/blog/25/)
- [webhookイベントとペイロード](https://docs.github.com/ja/developers/webhooks-and-events/webhooks/webhook-events-and-payloads)
- [webhoookのセキュリティ保護](https://docs.github.com/ja/developers/webhooks-and-events/webhooks/securing-your-webhooks)
- [kintone-rest-api-client](https://github.com/kintone/js-sdk/tree/master/packages/rest-api-client)