20200525のGoに関する記事は8件です。

Go製のToolをHaskellで実装する

初めに

最近Haskellの学習としてGo製のToolをHaskellで実装するようにしています。

Haskellでなにかしら作りたいと考えていて、そういえばGo製のToolはよく作られているから面白そうなものありそうだなと思ったのがきっかけです。

mockサーバであるhttplabをHaskellで作ることにしました。

まだ100%の実装ができていないのですが、ある程度形ができたので記事を書くことにしました。

screeencast.gif

ソースコードはこちらにあります。

Haskellでbrickというライブラリを使っていますが、使い方等は以前書いた記事を参考してください

GoもHaskellも至らぬところがあるので間違った箇所はアドバイスや助言をいただけると嬉しいです。

HaskellとGoもさほど違いがない箇所があったので、比較していきます。

引数のパース

Goではpflagを使ってパースしています。

pflag.StringVarP(&args.status, "status", "s", "200", "Specifies the initial response status.")

Haskellではoptparse-applivativeを使ってパースしてます。

parseStatus :: Parser Status
parseStatus = option (maybeReader $ fmap toEnum . readMaybe)
  $ long "status"
 <> short 's'
 <> value status200
 <> help "Specifies the initial response status."
 <> metavar "StatusCode"

こうやってみると使い方はさほど違いがないように感じます。

ただHaskellの方は関数の引数ではなくmonoidを使ってプログラム引数の構成が自由にできることと、
IntやBool, String以外のデータ型に対してもパースできる強みがあります。

ただその分記述量が多くなっています。

mock server

main.go
    srv := &http.Server{
        Addr:    fmt.Sprintf(":%d", args.port),
        Handler: http.Handler(middleware(NewHandler(ui, g))),
    }

    go func() {
        // Make sure gocui has started
        g.Execute(func(g *gocui.Gui) error { return nil })

        if err := srv.ListenAndServe(); err != nil {
            errCh <- err
        } else {
            ui.Info(g, "Listening on :%d", args.port)
        }
    }()

serverの情報を定義してgo rountineで起動させています。

Haskellはwaiを使って実装しています。

App.hs
  race_ (WAI.run (argPort cmd) $ middleware $ respHandler state chan)
            $ customMain vty buildVty (Just chan) app state


middlewareはまだちゃんと実装していないのでmiddleware=idでやっています。

こちらもサーバを非同期で起動しています。

handlerについては

main.go
// NewHandler returns a new http.Handler
func NewHandler(ui *ui.UI, g *gocui.Gui) http.Handler {
    fn := func(w http.ResponseWriter, req *http.Request) {
        if err := ui.AddRequest(g, req); err != nil {
            ui.Info(g, "%v", err)
        }

        resp := ui.Response()
        time.Sleep(resp.Delay)
        resp.Write(w)

    }
    return http.HandlerFunc(fn)
}

uiをpointerで渡すことでスレッド間の値を共有しています。

App.hs
respHandler :: TVar ResponseData -> BChan Requested -> Application
respHandler tvar bchan req send = do
  dt <- readTVarIO $ tvar
  threadDelay $ dt ^. resDelay
  send $ responseBuilder
    (dt ^. resStatusCode)
    (M.foldrWithKey' (\k a -> (toResHeader k a :)) [] $ dt ^. resHeaders)
    $ B.byteString $ T.encodeUtf8 $ dt ^. resBody
  <* writeBChan bchan (toRequested req)
  where
    toResHeader a b = (CI.mk $ T.encodeUtf8 $ CI.foldedCase a, T.encodeUtf8 b)

スレッド間の値を共有する為にTVar を使用しています

大きな違いとして、Requestについては、brickはuiの情報は純粋なデータしか受け付けていない為、アプリケーション定義イベントで渡すようにしていますがgocuiも同じことができると思います。

おまけ:Zipper

httplabはRequest情報を履歴で持つ機能が備わっています。それを実現するために内部データは次のように保持しています。

ui.go
type UI struct {
    ... 
    reqLock        sync.Mutex
    requests       [][]byte
    currentRequest int
}

requestsがrequestのあった履歴、currentRequestがrequestを表示している場所になっています。
ただこれだと毎回リストを走査しないといけなく効率が悪いです。
やりたいことはrequest履歴の追加、履歴の表示、1つ前後の履歴の移動になります。Haskellでよくある手法として Zipper を使うことにしました。

Zipperについてはこちらがよくまとめられています。

今回はこのようにしました。

Types.hs
data RequestedZipper a = RZ
  { _reqzAbove :: [a]
  , _reqzBelow :: [a]
  , _reqzCurrent :: Maybe a
  , _reqzCurPos :: Int
  , _reqzLength :: Int
  }
reqzLength :: RequestedZipper a -> Int
reqzCurPos :: RequestedZipper a -> Int
reqzCurrent :: RequestedZipper a -> Maybe a
initReqZipper :: RequestedZipper Requested
moveAboveReqz :: RequestedZipper a -> RequestedZipper a
moveBelowReqz :: RequestedZipper a -> RequestedZipper a

最後に

UIについては割愛します。見比べたところIOに関してはHaskellもGoも対して変わらず、実装しやすかったです。
純粋なところは他の言語とかけ離れてしまいますが、実装できたときは嬉しく見比べたときはHaskellのが分かりやすく感じます。

Goは面白いToolがたくさんあって色々参考になりますので
Haskellを学びたいけど題材がないという方はぜひチャレンジしてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GoでGoogle Maps APIを使って場所の詳細情報を取得する(例:東京タワー)

はじめに

GoでGoogleMapsAPIを使った記事があまりなかったので書いてみました。
GoogleMapsAPIには様々なAPIがあるのですが、今回はPlaceというAPIを使ってみたいと思います!

Placeの詳細情報はこちら。
Overview | Places API

GCPで事前準備

GoogleMapsAPIを使うにはGCPのアカウント作成、プロジェクト作成、APIKEYの取得が必要です。

以下からこれらを作成します。
Google Cloud Platform

詳しい手順はこちらが参考になります。
Google Maps Platform APIキーの取得・発行について
⇒API有効化では「Place」のAPIを有効化します。

とりあえず直接たたいてみる

Placeの中にはさらにいくつかの機能があるのですが、まずは基本のsearch機能を使ってみたいと思います。

各リクエストパラメータの詳細はこちらが参考になります。
Place Search

必須パラメータのみ

こんな感じでたたきます!
試しに東京タワーの情報を取得します。

request
https://maps.googleapis.com/maps/api/place/findplacefromtext/json?key=(取得したAPIKEY)&input=東京タワー&inputtype=textquery

返ってきたjsonがこちら。
必須パラメータだけだと味気ないですね。

response
{
   "candidates" : [
      {
         "place_id" : "ChIJCewJkL2LGGAR3Qmk0vCTGkg"
      }
   ],
   "status" : "OK"
}

リクエストパラメータにfieldを追加する

Use the fields parameter to specify a comma-separated list of place data types to return. For example: fields=address_component,name,geometry. Use a forward slash when specifying compound values. For example: opening_hours/weekday_text.

詳細な情報を得るにはfieldパラメータに色々設定するみたいです。
とりあえず色々追加してみます。

request
https://maps.googleapis.com/maps/api/place/findplacefromtext/json?key=(取得したAPIKEY)&input=東京タワー&inputtype=textquery&fields=plus_code,photos,formatted_address,name,geometry

返ってきたjsonがこちら。
それっぽいものが返ってきました!

response
{
   "candidates" : [
      {
         "formatted_address" : "日本、〒105-0011 東京都港区芝公園4丁目2−8",
         "geometry" : {
            "location" : {
               "lat" : 35.6585805,
               "lng" : 139.7454329
            },
            "viewport" : {
               "northeast" : {
                  "lat" : 35.66027947989272,
                  "lng" : 139.7469715298927
               },
               "southwest" : {
                  "lat" : 35.65757982010728,
                  "lng" : 139.7442718701072
               }
            }
         },
         "name" : "東京タワー",
         "photos" : [
            {
               "height" : 3024,
               "html_attributions" : [
                  "\u003ca href=\"https://maps.google.com/maps/contrib/115866158754962714954\"\u003eABD RAHMAN DIN\u003c/a\u003e"
               ],
               "photo_reference" : "CmRaAAAA2VO4J5WaFY6Z5-tS2SJKkPhoVCqxjukhhc0b5iadF1EgksxqgJ9PrUipk_6YyHXIbrLhQEsf2cZjJb628Gyj7a61fkFRkjx3HHhmiUs44VRa3DfWT5kghBndXQ4WUL_NEhD2oTfglENvq6tpTpGqvxUvGhTgsdhh44EXIC5MnZKizUygr9LuoA",
               "width" : 4032
            }
         ],
         "plus_code" : {
            "compound_code" : "MP5W+C5 港区、東京都",
            "global_code" : "8Q7XMP5W+C5"
         }
      }
   ],
   "status" : "OK"
}

GoからAPIを実行する

SDKをインストール

$ go get googlemaps.github.io/maps

実装

公式githubを参考に実装します。

main.go
package main

import (
    "context"
    "log"
    "os"

    "github.com/kr/pretty"
    "googlemaps.github.io/maps"
)

func init() {
    err := godotenv.Load()
    if err != nil {
        log.Fatalf("Error loading .env: %s", err)
    }
}

func main() {
    c, err := maps.NewClient("取得したAPIKEY")
    if err != nil {
        log.Fatalf("fatal error: %s", err)
    }
    r := &maps.TextSearchRequest{
        Query: "東京タワー",
    }

    res, err := c.TextSearch(context.Background(), r)
    if err != nil {
        log.Fatalf("fatal error: %s", err)
    }

    pretty.Println(res)
}
実行結果
$ go run main.go 
maps.PlacesSearchResponse{
    Results: {
        {
            FormattedAddress: "4 Chome-2-8 Shibakoen, Minato City, Tokyo 105-0011, Japan",
            Geometry:         maps.AddressGeometry{
                Location:     maps.LatLng{Lat:35.6585805, Lng:139.7454329},
                LocationType: "",
                Bounds:       maps.LatLngBounds{},
                Viewport:     maps.LatLngBounds{
                    NorthEast: maps.LatLng{Lat:35.66027947989272, Lng:139.7469715298927},
                    SouthWest: maps.LatLng{Lat:35.65757982010728, Lng:139.7442718701072},
                },
                Types: nil,
            },
            Name:             "Tokyo Tower",
            Icon:             "https://maps.gstatic.com/mapfiles/place_api/icons/geocode-71.png",
            PlaceID:          "ChIJCewJkL2LGGAR3Qmk0vCTGkg",
            Scope:            "",
            Rating:           4.400000095367432,
            UserRatingsTotal: 48610,
            Types:            {"tourist_attraction", "premise", "point_of_interest", "establishment"},
            OpeningHours:     &maps.OpeningHours{
                OpenNow:           &bool(true),
                Periods:           nil,
                WeekdayText:       nil,
                PermanentlyClosed: (*bool)(nil),
            },
            Photos: {
                {
                    PhotoReference:   "CmRaAAAAwPa4Fr0AM5xeZcF5ygLaKHUmOeYMqrhlhm1i0fluDX2WQsv7gGbhdIGG4Tj8W8yX6AC35BXtU4RbF7qc5d72CqIo5vXTdub6tXlhEj9r0GjvnsFPpaN0l8GFOLOkI8X8EhBC_84rpT5MTKJ5_UD_ze0wGhRA8g0XTrLRFPBIv6NRjIIHoPQIhA",
                    Height:           3024,
                    Width:            4032,
                    HTMLAttributions: {"<a href=\"https://maps.google.com/maps/contrib/115866158754962714954\">ABD RAHMAN DIN</a>"},
                },
            },
            AltIDs:            nil,
            PriceLevel:        0,
            Vicinity:          "",
            PermanentlyClosed: false,
            BusinessStatus:    "OPERATIONAL",
            ID:                "0f193d8a0df922a2bcb369673944240a8cb182c3",
        },
    },
    HTMLAttributions: {},
    NextPageToken:    "",
}

いい感じに取得できました!

参考

Golangで郵便番号から住所を取得するAPIを作成する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Golang】構造体

【Golang】構造体

Golangの基礎学習〜Webアプリケーション作成までの学習を終えたので、復習を兼ねてまとめていく。 基礎〜応用まで。

package main 
//struct 構造体
//クラスのような感じ

import (
    "fmt"
)

//struct
//型
//キャピタル=頭文字を大文字にする。小文字だと外部からアクセスできない
type Vertex struct {
    X int
    Y string
    Z int
}

//3 値を書き換える関数は、引数をポインタ型にする
//関数 Xを書き換える
//引数がポインタ型ではないので変わらない
func Changev(v Vertex) {
    v.X = 100000
}

//関数 Yを書き換える
//引数はポインタ型で渡す
func Changev2(v *Vertex) {
    //本当は下記で書くといいが、
    // (*v).X = 1000
    //下記でも、自動で*vにしてくれる
    v.X = 1000000
}

func main() {
    //1
    //宣言
    v := Vertex{X: 1, Y: "A"}
    fmt.Println(v)
    //>>{1 A 0}

    //値にアクセス
    fmt.Println(v.X, v.Y, v.Z)
    //>>1 A 0

    //上書き
    v.X = 2
    fmt.Println(v.X)
    //>>2

    //宣言
    //複合リテラルs
    //順番に値を入れる
    v2 := Vertex{1, "B", 100}
    //Xのみ 他はデフォルトが入る
    v3 := Vertex{X: 1}
    fmt.Println(v2)
    //>>{1 B 100}
    fmt.Println(v3)
    //>>{1  0}


    //アドレスはない
    //宣言 空 デフォルト値が入る
    v4 := Vertex{}
    fmt.Println(v4)
    //>>{0  0}
    fmt.Printf("%T %v\n", v4, v4)
    //>>main.Vertex {0  0}


    //別の書き方 空
    var v5 Vertex
    fmt.Println(v5)
    //>>{0  0}
    fmt.Printf("%T %v\n", v5, v5)
    //>>main.Vertex {0  0}


    //new
    //アドレスが帰ってくる。
    //v4,v5とは意味合いが違う
    v6 := new(Vertex)
    fmt.Println(v6)
    //>>&{0  0}
    fmt.Printf("%T %v\n", v6, v6)
    //>>*main.Vertex &{0  0}


    //v6と同じ表現(アドレス)をnewを使わないで表現
    //明示的にアドレスを取得したい場合はこっちを使う場合が多い
    v7:= &Vertex{}
    fmt.Println(v7)
    //&{0  0}
    fmt.Printf("%T %v\n", v7, v7)
    //>>*main.Vertex &{0  0}


    //2
    //空を作る
    //スライスとかマップの場合はmake([]int, 0)の方が多い
    /*
            s := make([]int, 0)
            s := []int{}
            fmt.Println(s)
    */
    //goは書き方が複数あって困惑するが、今は書き方が複数あるということが理解できていればOK


    //3 構造体は値型
    //応用
    //値を書き換えたい
    v8 := Vertex{3, "B", 1000}
    //値渡しなので、コピーが作られて渡される
    Changev(v8)
    //書き換わらない
    fmt.Println(v8)
    //>>{3 B 1000}

    //書き換えたい場合 アドレス渡しにする
    v9 := &Vertex{4, "C", 122}
    //参照渡し
    Changev2(v9)
    fmt.Println(v9)
    //>>&{1000000 C 122}


}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vuguに触れてみた(構文確認編)

はじめに

こちらは別記事(Vuguに触れてみた(環境構築編) )の続きです。
今回は実際に構文確認しつつ、Vuguに触れていきたいと思います。

Vuguに触れてみた(環境構築)のファイル構成前提で始めますので、ご了承ください。

また、今回作ったものに関しては下記リポジトリに置いています。(更新するかもです。)
https://github.com/inagacky/vugu_sample

構文確認

まずは root.vugu を元に、色々と触れていこうと思います。
初期状態だと色々文言が入っていますので、削除してから始めています。

vg-if

Show Button というボタンをクリックすると、 Show というbool型の値が変わり、
SAMPLE TEXT という文言を表示するだけのコードです。

root.vugu
<div>
    <main role="main" class="container text-center">
        <div class="mt-5">
            <h1>Vugu Test</h1>
            <div class="lead">
                <input type="button" @click='c.Show=!c.Show' value="Show Button" />
            </div>
            <div vg-if='c.Show' style="margin-top: 10px; font-size: 20px;">
                SAMPLE TEXT
            </div>
        </div>
    </main>
</div>

<script type="application/x-go">
type Root struct {
    Show bool `vugu:"data"`
} 
</script>

実行結果

VUGU1.gif

vg-for

もちろんfor文もかけます。
下記のコードは、Add Button をクリックする度に、100までの乱数値をリストに追加し、
リスト文を表示しているコードです。

root.vugu
<div>
    <main role="main" class="container text-center">
        <div class="mt-5">
            <h1>Vugu Test</h1>
            <div class="lead">
                <input type="button" @click='c.HandleClick(event)' value="Add Button" />
            </div>
            <div vg-if='len(c.Numbers) > 0'>
              <span vg-for='_, num := range c.Numbers'>
                  <span vg-content='num' style="margin-left: 10px;"></span>
              </span>
            </div>
        </div>
    </main>
</div>

<script type="application/x-go">

import "math/rand"

type Root struct {
  Numbers []int `vugu:"data"`
}

func (r *Root) HandleClick(event vugu.DOMEvent) {
  // リストへ追加
  r.Numbers = append(r.Numbers, rand.Intn(100))
}

</script>

実行結果

VUGU2.gif

vg-attr

動的にStyle属性やClass属性の付与を行えます。
下記の例では、乱数を生成した後、乱数に応じたClass名を付与し、数字に色をつけています。

root.vugu
<div>
    <main role="main" class="container text-center">
        <div class="mt-5">
            <h1>Vugu Test</h1>
            <div class="lead">
                <input type="button" @click='c.HandleClick(event)' value="Add Button" />
            </div>
            <div vg-if='len(c.Randoms) > 0'>
              <span vg-for='_, rNumber := range c.Randoms'>
                  <span :class="rNumber.Style" vg-content='rNumber.Number' style="margin-left: 10px;"></span>
              </span>
            </div>
        </div>
    </main>
</div>

<script type="application/x-go">

import "math/rand"

type Root struct {
    Randoms []Random `vugu:"data"`
    Style string `vugu:"data"`
}

type Random struct {
    Number int
    Style string
}

func (r *Root) HandleClick(event vugu.DOMEvent) {

  num := rand.Intn(3)
  randomNum := Random {
    Number: num,
    Style: []string{"red", "green", "blue"}[num],
  }
  r.Randoms = append(r.Randoms, randomNum)
}

</script>

<style>
  .red {
    color: #f00;
  }
  .green {
    color: #0f0;
  }
  .blue {
    color: #00f;
  }
</style>

実行結果

VUGU3.gif

コンポーネント作成

もちろん独自コンポーネントも作成することができます。
今回はサンプルとして、先ほどのコードの乱数の数値表示と色付けを
random-line.vugu に切り出し、コンポーネントにしました。

root.vugu
<div>
    <main role="main" class="container text-center">
        <div class="mt-5">
            <h1>Vugu Test</h1>
            <div class="lead">
                <input type="button" @click='c.HandleClick(event)' value="Add Button" />
            </div>
            <div vg-if='len(c.Numbers) > 0'>
              <span vg-for='_, rNumber := range c.Numbers'>
                  <main:RandomLine :Number="rNumber"></main:RandomLine>
              </span>
            </div>
        </div>
    </main>
</div>

<script type="application/x-go">

import "math/rand"

type Root struct {
    Numbers []int `vugu:"data"`
    Style string `vugu:"data"`
}

type Random struct {
    Number int
    Style string
}

func (r *Root) HandleClick(event vugu.DOMEvent) {

  num := rand.Intn(3)
  r.Numbers = append(r.Numbers, num)
}

</script>

random-line.vugu
<span :class='c.getStyle(c.Number)' vg-content='c.Number' style="margin-left: 10px;"></span>

<script type="application/x-go">
    type RandomLine struct {
      Number int `vugu:"data"`
    }

    func (r *RandomLine) getStyle(num int) string {
        return []string{"red", "green", "blue"}[num];
    }
</script>

<style>
    .red {
        color: #f00;
    }
    .green {
        color: #0f0;
    }
    .blue {
        color: #00f;
    }
</style>

実行結果

先ほどと同様です。
VUGU4.gif

終わりに

GolangVue.js に触れたことがある方なら、ある程度直感的に理解ができるのではないでしょうか。
正直まだまだ発展途上の技術だと思いますし、日本語の情報(というか英語も)ほぼ無く、
GitHubのIssueを確認したり、時には Vugu 自体のコードを読むことが必要になってきそうです。
将来的に Golang× WebAssembly といった場合に、一つの選択肢になればいいなと思います。
次回は何かしらアプリでも作ってみようかなと思います。

閲覧頂きありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vuguに触れてみた(環境構築編)

はじめに

一時期小耳に挟んだ Vugu という技術について、今更ながら触れてみたので記事にしてみました。

Vuguとは

Golang で、 Vue.js っぽくフロントエンド を記述できるライブラリです。
WebAssebmly で実行されるのでコードの漏洩がなかったり、実行速度も比較的高速らしいです。
公式URLは下記
https://www.vugu.org/
2020/04/26v0.3.0 が出たようなので、まだまだ発展途上の技術ではあります。

環境構築

環境構築だけなら、下記公式の手順を行なっていけばできます。
https://www.vugu.org/doc/start

前提

Golangv1.14 以上らしいので、予めインストールをお願いします。
Macで、homebrewを使っていれば、下記コマンドだけでGolangのインストール可能です。

% brew install go

その他の方法に関しては、公式のインストールをご確認ください。
https://golang.org/doc/install

vgrunのインストール

下記コマンドでvgrunのインストール、その他ツール群をインストールします。

[hoge@mac] ~/vugu_sample
% go get -u github.com/vugu/vgrun
[hoge@mac] ~/vugu_sample
% vgrun -install-tools
2020/05/24 17:20:08 Installing vugugen
2020/05/24 17:20:13 Installing vgrgen
[hoge@mac] ~/vugu_sample

サンプルプロジェクトのダウンロード

公式の方がサンプルプロジェクトを用意してくださっているので、
こちらをダウンロードして利用していきます。
https://github.com/vugu-examples

下記コマンドで可能です。
(今回はsimpleリポジトリを利用しています。)

vgrun -new-from-example=simple .

上記コマンドを実行すると、ファイルがダウンロードされているかと思います。
go.mod も落ちてくるので助かりますね。

% ls -la
total 56
drwxr-xr-x   9 user staff   288  5 24 17:29 ./
drwxr-xr-x+ 99 user staff  3168  5 24 17:29 ../
-rw-r--r--   1 user  staff    24  5 24 17:29 .gitignore
-rw-r--r--   1 user  staff  1070  5 24 17:29 LICENSE
-rw-r--r--   1 user  staff   231  5 24 17:29 README.md
-rw-r--r--   1 user  staff   662  5 24 17:29 devserver.go
-rw-r--r--   1 user  staff    39  5 24 17:29 generate.go
-rw-r--r--   1 user  staff    48  5 24 17:29 go.mod
-rw-r--r--   1 user  staff  2380  5 24 17:29 root.vugu

サーバーの実行

下記コマンドでサーバーを立ち上げます。

% vgrun devserver.go
2020/05/24 17:31:39 Starting HTTP Server at "127.0.0.1:8844"

その後、 http://127.0.0.1:8844/ にアクセスします。
下記画面が表示されれば起動しています!

スクリーンショット 2020-05-24 17.34.16.png

ちなみに、 root.vugu の内容を修正すると、ホットリロードで変更を反映してくれます。

終わりに

ほぼ公式チュートリアルの通りの内容になってしまいましたが、以上で環境構築手順は終了です。
現状のフロントエンドの代替となるかと言われると微妙な気がしますが、
golang でフロントエンドが書けるのは嬉しいですね。
次回はroot.vuguの内容を修正したり、構文について見ていこうかと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pion/webrtc のexampleのタイムアウトを伸ばす

pion は Go で書かれた WebRTC 実装です。
これを試してみようとして、まずexampleを動かしてみようとしました。

https://github.com/pion/webrtc/blob/master/examples/README.md

通信を行うときに両者でお互いの通信条件を取り交わす必要があります。これをシグナリングと呼んでいます。
WebRTCではシグナリングの方法は仕様で定めらていません。
ここのexamplesでは、それを「コピぺ」によって行うようになっています。

(1) webページのフォームに表示された文字列をコピぺして実行ファイルの標準入力に食わせる。
(2) 実行ファイルの標準出力に出てきた文字列をコピぺしてwebページのもうひとつのフォームに入力する。
(3) webページ上の'Start Session'のボタンを押す。

やってみると、これがなかなかうまくいきません。(3)のボタンを押す直前でタイムアウトしてしまいます。
コピペする文字列は百行近くある巨大なものなので、スクロールしている間に時間がたってしまいます。
何度やっても間に合わなくてイライラが爆発しそうになったので、exampleを修正してタイムアウトを伸ばすことにしました。

diff --git a/examples/save-to-disk/main.go b/examples/save-to-disk/main.go
index 6318220..9effce0 100644
--- a/examples/save-to-disk/main.go
+++ b/examples/save-to-disk/main.go
@@ -33,6 +33,8 @@ func saveToDisk(i media.Writer, track *webrtc.Track) {
 }

 func main() {
+       s := webrtc.SettingEngine{}
+       s.SetCandidateSelectionTimeout(30 * time.Second)
        // Create a MediaEngine object to configure the supported codec
        m := webrtc.MediaEngine{}

@@ -42,7 +44,7 @@ func main() {
        m.RegisterCodec(webrtc.NewRTPVP8Codec(webrtc.DefaultPayloadTypeVP8, 90000))

        // Create the API object with the MediaEngine
-       api := webrtc.NewAPI(webrtc.WithMediaEngine(m))
+       api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithSettingEngine(s))

        // Everything below is the Pion WebRTC API! Thanks for using it ❤️.

これで30秒の猶予ができたので、ゆっくりと操作してexampleを動かすことができるようになりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Go言語でAWS SNSにメッセージを送信する

概要

Amazon Simple Notification Service(SNS)で、Go言語のSDKを使ってトピックに対してメッセージを送信します。

環境

  • Go : 1.14.3
  • Mac OS X : 10.15.4
  • AWS SDK for Go

シンプルな実装

シンプルなメッセージ送信
package main

import (
    "fmt"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/sns"
)

// TopicARNとRegionは環境に合わせます
const (
    TopicARN  = "arn:aws:sns:XXXXXXXXXXXXXXXXX"
    AwsRegion = "XXXXXX"
)

func main() {
    // SNSクライアントの作成
    mySession := session.Must(session.NewSession())
    svc := sns.New(mySession, aws.NewConfig().WithRegion(AwsRegion))

    message := "This is sample message."

    // メッセージを送信するための構造体を作成
    inputPublish := &sns.PublishInput{
        Message:  aws.String(message),
        TopicArn: aws.String(TopicARN),
    }

    // メッセージの送信(Publish)
    MessageId, err := svc.Publish(inputPublish)
    if err != nil {
        fmt.Println("Publish Error: ", err)
    }

    fmt.Println(MessageId)

}

シンプルにメッセージを送る場合の実装です。登録されたサブスクリプションに対してメッセージが送信されます。例として、SQSをサブスクリプションにした場合、以下の通り確認できます。
image.png

サブスクリプションごとにメッセージを変える場合

サブスクライブするプロトコルごとのメッセージ
package main

import (
    "encoding/json"
    "fmt"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/sns"
)

const (
    TopicARN  = "arn:aws:sns:XXXXXXXXXXXXXXXXX"
    AwsRegion = "XXXXXX"
)

func main() {
    mySession := session.Must(session.NewSession())
    svc := sns.New(mySession, aws.NewConfig().WithRegion(AwsRegion))

    message := "This is sample message."

    // サブスクリプションのプロトコルごとにメッセージを指定
    messageJson := map[string]string{
        "default": message,
        "sqs": "This is sample message for sqs.",
    }
    // メッセージ構造体はJSON文字列にする
    bytes, err := json.Marshal(messageJson)
    if err != nil {
        fmt.Println("JSON marshal Error: ", err)
    }
    messageForSQS := string(bytes)

    inputPublish := &sns.PublishInput{
        Message:  aws.String(messageForSQS),
        MessageStructure: aws.String("json"),    // MessageStructureにjsonを指定
        TopicArn: aws.String(TopicARN),
    }

    MessageId, err := svc.Publish(inputPublish)
    if err != nil {
        fmt.Println("Publish Error: ", err)
    }

    fmt.Println(MessageId)

}

このソースの実行結果は以下の通りです。SQS用に設定したメッセージが送信されています。
image.png

このようにサブスクリプションのプロトコルごとに異なるメッセージを送る場合、MessageStructurejsonを設定します。また、メッセージはJSON文字列として、トップレベルにプロトコル(今回の例ではsqs)を指定します。トップレベルにdefaultは必須です。
プロトコルとして指定する値はCLIのリファレンスを参照。
subscribe - Options

なお、メッセージ属性を設定する場合はMessageAttributesを使います。詳細は以下の公式ドキュメントを参照。
type PublishInput

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

goose設定ファイルに環境変数を読み込めない(Docker環境)

概要

Docker環境でGo製マイグレーションツールのgooseを使用する際、設定ファイルであるdbconf.ymlが環境変数を読んでくれませんでした。

dbconf.yml

development:
  driver: mymysql
  open: $MYSQL_URI

環境変数は、$変数という形式で読み込んでくれるはずなのですが・・・(公式参照)

Dockerfile

FROM golang:1.14.2-alpine

WORKDIR /go/src/server
COPY . .
ENV GO111MODULE=on

RUN apk add --no-cache \
        alpine-sdk=1.0-r0 \
    && go get github.com/pilu/fresh \
              bitbucket.org/liamstask/goose/cmd/goose
CMD ["fresh"]

コンテナ内でgooseコマンドを使うため、コンテナ内にもgooseを導入してあります。

原因

コンテナ内では外部ファイルやコマンドで指定してあげないと、環境変数を読み込むことができないみたいです。
コンテナ内を確認したところ、.envがコピーされていなかったので、Dockerの使用上コピーできないようになっているんですかね?
Docker で環境変数をホストからコンテナに渡す方法(ホスト OS 側からゲスト OS に渡す方法各種)

解決

docker-compose.ymlにオプション追加

version: "3.7"

services:
  server:
    container_name: server
    build:
      context: ./
      dockerfile: Dockerfile.dev
    ports:
      - 8000:8000
    env_file:  # 左記オプション追加
      - .env
    depends_on:
      - db
    tty: true
    stdin_open: true
    volumes:
      - ./:/go/src/server
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む