lambda + Goで定期バッチを作成する

この記事はディップ株式会社[Advent Calendar 2022](https://qiita.com/advent-calendar/2022/dip-dev) の18日目です

## 概要

私が所属しているチームでは週１回定例があり、[DocBase](https://docbase.io/)という情報提供ツールに議事録を残しています。
議事録は毎週定例実施前にテンプレートを手動で準備していましたが、以下問題点がありました。

#### 新規issueの記入が面倒
議事録には、定例までの期間に新しく作成されたissueを記入をする必要がありますが、所属チームではリポジトリを複数管理しています。
そのため、リポジトリごとにissueを検索 -> md形式に変換 という工程を複数回を行う必要があり、issueの記入に時間がかかりました。

#### 手作業によるミス
人の手で作業を行うため、日付のミス・issue起票のミス・議事録作成忘れなどが稀に発生していました。

これらを解決し業務を効率化させるために、定例議事録の作成をバッチ化させました。

## 詳細

定例バッチの作成には lambda + EventBrigeを使用し、最終的には以下のような構成にしました。

![アーキテクチャ図 (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321767/c51cd7b5-e7da-41bb-764c-7823269468f6.jpeg)

一部実装を簡単に説明していきます

### lambdaハンドラーの実装

`github.com/aws/aws-lambda-go/lambda`を使用してハンドラーを作成します。
`event`にはEventBridgeで設定できる定数パラメーターが入ってくるので、こちらを使用することでどの処理を実行するかを判断させています。

```go:main.go
type myEvent struct {
	Name string `json:"name"`
	Days []int  `json:"days,omitempty"`
}

func HandleRequest(ctx context.Context, event myEvent) (string, error) {
	fmt.Println("start HandleRequest")
	var err error

	switch event.Name {
	// 定例テンプレート作成
	case constant.MEETING_TEMPLATE_CREATING: // meeting_template_creating
		err = meeting.Handler(event.Days)
    // issue記入
	case constant.MEETING_ISSUE_EXPORT: // meeting_issue_export
		err = issue.Handler()
	default:
		err = fmt.Errorf("unexpected batch name")
	}

	return fmt.Sprintf("Finish %s!", event.Name), err
}

func main() {
	lambda.Start(HandleRequest)
}

```

### 議事録の作成

DocBaseへ議事録のテンプレート投稿する処理を行なっています。
議事録のテンプレートを`TPL_BODY`に格納しておき、定例ごとに変動する箇所のみを置き換え後、DocBaseが提供しているメモ投稿APIを使用し投稿を行なっています。


```go: create.go
const TPL_BODY = `
議事録のテンプレート
`

const URL = "https://api.docbase.io/teams/{チーム名}/posts"

func create(day int) error {
	date := time.Now().AddDate(0, 0, day).Format("2006/01/02")
	GROUP_ID, err := strconv.Atoi(os.Getenv("DOCBASE_GROUP_ID"))
	if err != nil {
		return err
	}

	// 置き換え用文字列を置換
	r := strings.NewReplacer("{日付記入欄}", date, "{参加者}", os.Getenv("MEMBERS"))
	rBody := r.Replace(TPL_BODY)

	params := &param{
		Title:  "タイトル " + date,
		Body:   rBody,
		Tags:   []string{"議事録", "チームのタグ"},
		Scope:  "group",
		Groups: []int{GROUP_ID},
	}
	jsonParam, err := json.Marshal(params)
	if err != nil {
		return err
	}

	// メモの新規作成
	req, err := http.NewRequest(http.MethodPost, URL, bytes.NewBuffer(jsonParam))
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-DocBaseToken", os.Getenv("DOCBASE_TOKEN"))

	client := new(http.Client)
	res, err := client.Do(req)
	if err != nil {
		return err
	}
	res.Body.Close()
}
```

### issueの記入

こちらは以下の処理の流れで実装を行いました。

1.[メモの検索API](https://help.docbase.io/posts/92984)を使用し更新する議事録を取得
2. [github API](https://docs.github.com/ja/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues)を使用してリポジトリごとにissueを取得し、 \[issueのタイトル](リンク)のmdに変換
3. 作成したissueパートの文章を本文に記入・更新を行う

```go:issue.go
func Handler() error {
	searchRes, err := search() // 該当議事録の検索
	if err != nil {
		return err
	}
	txt := issue() // issueの収集
	body := strings.Replace(searchRes.Body, "{issue起票置き換え}", txt, 1)
	err = updateBody(body, searchRes.ID) // 議事録の更新
	if err != nil {
		return err
	}
	return nil
}
```

### コンパイル ~ EventBridgeの設定

作成したlambda関数のコードはコンパイルしてzip化し　lambdaにアップロードする必要はあるのでコンパイル〜zip化をしていきます

:::note warn
[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/golang-package.html#golang-package-mac-linux)には記述がありませんが、実行環境がM1Macの場合、
`fork/exec /var/task/main: exec format error`というエラーが発生します。
こちらのエラーは`GOARCH=amd64`を追加することで回避することができます。
:::

```
	GOARCH=amd64 GOOS=linux go build main.go
	zip function.zip main
```

lambda関数を作成しzip化したコードをコンパイル後、EventBridgeにlambdaを紐付けて作成します。この時定数パラメーターを設定することで、先ほど述べた通り実行したいバッチ処理を指定することができます。

![mosaic_20221217233902.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321767/b11335ad-fe82-8cc9-201c-a4aa559aaf87.png)


## 終わり

今回は定例議事録の作成を定期バッチ化させました。
自分が普段使っているサービスのAPIを調べてみると、意外な発見があり効率化に結びつくことがあるので皆さんも是非確認して見てください！