GoでWeb APIをCallする　BacklogのAPIを例に

## はじめに
GoのLambdaで[構成](#構成)に図示したような形でBacklogの課題情報を取ってきたり、課題情報を更新する実装を行ったのでその備忘録を残す。
※[Lambda関数(Go)でSlack通知](https://qiita.com/yuta-katayama-23/items/1e58d630410c7dd22d8e)や[AWS ChatbotでSlackからLambda関数(Go)を呼び出す](https://qiita.com/yuta-katayama-23/items/da16e8760bec47d7fd26)の記事の続きの関連で、DevOps導入に関係して行った事。

## 構成
Code BuildのBuildを実行するLambda（Go）を呼び出した時に、Backlogの課題のステータスを確認してあるステータスならそのままBuildを続行しそうでないなら中断。
また、Build完了時にBacklogの課題のステータスをあるステータスに更新する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/b5cbcbad-87be-c059-a74b-a3920a433918.png)

## GoでWeb APIをCallする
### GET
Backlogの課題のステータスを確認してあるステータスならそのままBuildを続行を判断するための、Backlogの課題ステータスをチェックする関数は以下。

```go
import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
)

type Issue struct {
	Status Status `json:"status"`
}

type Status struct {
	Id        int
	ProjectId int
	Name      string
}

func checkIssueStatus(issueKey string) string {
	reqUrl := "https://" + os.Getenv("BACKLOG_DOMEIN") + ".backlog.com/api/v2/issues/" + issueKey + "?apiKey=" + os.Getenv("BACKLOG_API_KEY")
	resp, err := http.Get(reqUrl)
	if err != nil {
		fmt.Println("Request Error:", err)
		return "Request Error:" + err.Error()
	}

	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		fmt.Println("Response Error:", resp.Status)
		return "Response Error:" + resp.Status
	}

	body, err := io.ReadAll(resp.Body)
	fmt.Println(string(body))

	var issue Issue
	json.Unmarshal(body, &issue)

	fmt.Println("issue.Status.name : ", issue.Status.Name)
	fmt.Println("issue.Status.id : ", issue.Status.Id)

	if issue.Status.Name != os.Getenv("BACKLOG_ISSUE_STATUS") {
		fmt.Println("Status Error:", issue.Status.Name)
		return "Status Error:" + issue.Status.Name
	}
	return "ok"
}
```

- ※注意点①<br>公式にも`The client must close the response body when finished with it:(クライアントは、応答が終了したら、応答本文を閉じる必要があります。)`と書かれている通り、`defer resp.Body.Close()`とする事。これをしないとTCPコネクションが開きっぱなしになる。
- ※注意点②<br>私はハマったが、構造体のフィールド名は**大文字**から始める必要があるので注意

・参考：[Backlog 課題情報の取得](https://developer.nulab.com/ja/docs/backlog/api/2/get-issue)
・参考：[Go の net/http を使って Web API にリクエストを行う：CData API Server](https://www.cdatablog.jp/entry/golanghttpapi)
・参考：[構造体フィールド名小文字にした場合](https://qiita.com/NaoyukiSato/items/6dcd26725d01d0b6b2fa#%E6%A7%8B%E9%80%A0%E4%BD%93%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E5%90%8D%E5%B0%8F%E6%96%87%E5%AD%97%E3%81%AB%E3%81%97%E3%81%9F%E5%A0%B4%E5%90%88)

### PATCH（Content-Type:application/x-www-form-urlencoded)
Backlogの課題のステータスをあるステータスに更新する関数は以下。

```go
import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func updateIssueStatus(issueKey string) string {
	reqUrl := "https://" + os.Getenv("BACKLOG_DOMEIN") + ".backlog.com/api/v2/issues/" + issueKey + "?apiKey=" + os.Getenv("BACKLOG_API_KEY")
	values := url.Values{}
	values.Set("statusId", os.Getenv("BACKLOG_STATUS_ID"))

	req, err := http.NewRequest(
		"PATCH",
		reqUrl,
		strings.NewReader(values.Encode()),
	)

	if err != nil {
		fmt.Println("NewRequest Error:", err)
		return "NewRequest Error:" + err.Error()
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	client := new(http.Client)
	resp, err := client.Do(req)

	if err != nil {
		fmt.Println("Request Error:", err)
		return "Request Error:" + err.Error()
	}

	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		fmt.Println("Response Error:", resp.Status)
		return "Response Error:" + resp.Status
	}

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))

	return "ok"
}
```

・参考：[Backlog 課題情報の更新](https://developer.nulab.com/ja/docs/backlog/api/2/update-issue)
・参考：[■パラメータをURLエンコードする場合](https://qiita.com/yyoshiki41/items/fc878494e19b9de93d56#%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%82%92url%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%89%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88)

## ソースコード全体
以下のリポジトリにある。

https://github.com/yuta-katayama-23/Go

## Goのリファレンス
- https://pkg.go.dev/net/http