Golangでサーバレスのバッチ処理を構築する
HTTP Request
-
http.Get
やhttp.Post
- シンプルなリクエストを送信する場合
-
http.Client
- HTTP client headers, redirect policyやその他の設定を行う場合
http.Get
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
url := "https://google.co.jp"
resp, err := http.Get(url)
if err != nil {
fmt.Println("Error")
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
http.Client
クライアント(http.Client)とリクエスト(http.Request)とリクエストを実際に発行する(client.Do)という構造が見えると見通しが良くなると思います。http.Get ではこれらをデフォルトのクライアント(DefaultClient) を用いてよしなにリクエストを発行しているのです。
package main
import (
"fmt"
"io"
"net/http"
)
const AIRTABLE_API_KEY = "keyrJp9239YmiN7tk"
func main() {
url := "https://google.co.jp"
client := &http.Client{}
resp, err := client.Get(url)
if err != nil {
fmt.Println("Error")
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
Query Parametersの設定
req.URL.Query
で設定する
url := "https://api.airtable.com/v0/app9NApDWCvgUEs1J/Beer"
client := &http.Client{}
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", AIRTABLE_API_KEY))
params := req.URL.Query()
params.Add("maxRecords", "1")
req.URL.RawQuery = params.Encode()
参考資料
文字列の埋め込み
Sprintf
を使うのがテンプレートリテラルの機能に近い。
hello := "Hello"
greet := fmt.Sprintf("%s World!\n", hello)
Twitterの認証
一連の流れ
基本的にはOAuth 2.0 | Docs | Twitter Developer Platform に記載された流れの通りやればOK。
Bearer Tokenの取得
func twitter_oauth() string {
const API_KEY = ""
const API_SECRET_KEY = ""
const OAUTH2_ENDPOINT = "https://api.twitter.com/oauth2/token"
client := &http.Client{}
req, _ := http.NewRequest("POST", OAUTH2_ENDPOINT, nil)
// Basic認証情報を設定
req.SetBasicAuth(API_KEY, API_SECRET_KEY)
// Query Parameterを設定
params := req.URL.Query()
params.Add("grant_type", "client_credentials")
req.URL.RawQuery = params.Encode()
// リクエストの実行
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
fmt.Println(string(body))
token_data := new(token)
if err := json.Unmarshal(body, token_data); err != nil {
fmt.Println("JSON Unmarshal error:", err)
}
return token_data.Access_token
}
取得したTokenを使用してTweetを検索
func search_tweets(bearer string) {
const SEARCH_ENDPOINT = "https://api.twitter.com/2/tweets/search/recent"
client := &http.Client{}
req, _ := http.NewRequest("GET", SEARCH_ENDPOINT, nil)
// 認証情報を設定
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", bearer))
// Query Parameterを設定
params := req.URL.Query()
params.Add("query", "ヨロッコビール")
params.Add("tweet.fields", "geo,context_annotations,created_at,in_reply_to_user_id,lang,referenced_tweets,attachments")
req.URL.RawQuery = params.Encode()
// リクエストの実行
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
認証周りで使ったGoの書き方
認証情報の設定
Bearer Token取得時にBasic Authenticationを行う必要があったが、req.SetBasicAuth
を使えばOKだった。
// Basic認証情報を設定
req.SetBasicAuth(API_KEY, API_SECRET_KEY)
ページネーション
公式ページの以下の記載の通り、基本的にはAPIのレスポンスにあるメタ情報のnext_token
に値が存在するかどうかで次のページの存在有無を判定すればいい。
The recent search endpoints will respond to a query with at least one page, and provide a next_token in its JSON response if additional pages are available. To receive matching Tweets, this process can be repeated until no token is included in the response.
Search Tweets - How to paginate | Docs | Twitter Developer Platform
AWSのリソースにアクセス
V2
GoのSDKにはv2が出ているので、今からやるならv2を使ったほうが良いかも(Go1.15以上が必要)
- aws/aws-sdk-go-v2: AWS SDK for the Go programming language.
- Go言語(golang)でS3からファイルを取得する | DevelopersIO
- Developer Guide | AWS SDK for Go V2
- sdk package - github.com/aws/aws-sdk-go-v2 - pkg.go.dev
基本的な流れ
AWSのリソースへのアクセスの流れ
service client
をまず作成する必要がある。
service client
はlow-level accessを提供する。
Using the AWS SDK for Go V2 with AWS Services | AWS SDK for Go V2
NewFromConfig
にConfigを渡すことでclientを定義する。
import "context"
import "github.com/aws/aws-sdk-go-v2/config"
import "github.com/aws/aws-sdk-go-v2/service/s3"
// ...
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
panic(err)
}
client := s3.NewFromConfig(cfg)
S3にアクセス
$ go get -u github.com/aws/aws-sdk-go
$ go get -u github.com/aws/aws-sdk-go-v2/service/s3
指定したバケットのオブジェクトを一覧表示するコードが以下。
cfg, err := config.LoadDefaultConfig(
context.TODO(),
config.WithRegion("ap-northeast-1"))
if err != nil {
log.Fatalf("unable to load SDK config, %v", err)
}
client := s3.NewFromConfig(cfg)
output, err := client.ListObjectsV2(context.TODO(), &s3.ListObjectsV2Input{
Bucket: aws.String("YOUR_BUCKET_NAME"),
})
if err != nil {
log.Fatal(err)
}
log.Println("first page results:")
for _, object := range output.Contents {
log.Printf("key=%s size=%d", aws.ToString(object.Key), object.Size)
}
バケット名の指定が間違っていると以下のエラーメッセージが出る。何か追加の指定が必要なのかと思ったが、正しいバケット名を指定すれば正常にアクセスできたので注意。
operation error S3: ListObjectsV2, https response error StatusCode: 301, RequestID: 35BCD7E3V53YAGE5, HostID: vixRL81uKRGJQrAA5kN6YLqOCScuxSZtPVLuvlhyAMYzvEwM5v8I7HBNrqMFDR0Sx+fbgzQMtNo=, api error PermanentRedirect: The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.
ファイルのアップロード
aws-doc-sdk-examples/PutObjectv2.go at main · awsdocs/aws-doc-sdk-examples
上記のサンプルを元に構築すれば一旦S3へのアップロードは出来た。
Clientを使う方法だが、参考資料にあるようにハイレベルのライブラリを使ったほうが楽に構築できる?
Bodyに指定できるのはio.Reader
になる。
StructなどGoの型をそのままアップロードする場合
Convert a struct to io.Reader in Go (Golang) を参考にした。json.NewEncoder
にbytes.Buffer
を渡せばいいみたい。
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("ap-northeast-1"))
if err != nil {
log.Fatalf("unable to load SDK config, %v", err)
}
client := s3.NewFromConfig(cfg)
// S3へのアップロード
bucketName := "golang-sample-s3-bucket"
var buf bytes.Buffer
json.NewEncoder(&buf).Encode(jsonData)
input := &s3.PutObjectInput{
Bucket: aws.String(bucketName),
Key: aws.String(filename),
Body: &buf,
}
_, err = PutFile(context.TODO(), client, input)
参考資料
Terraformの構築
VPCの構築
上記の資料を参考にしながら以下のリソースを作成。
- VPC
- Subnet
- Internet Gateway
- Route Table
- Route TableとInternet Gatewayの関連付