azihsoyn's blog

技術のこととか釣りの事とか(書けたらいいなぁ)

goa v1でサポートされてないint64を使う方法

たまにはアウトプットしようかと思い立ったので。 タイトルの通りです。

goa が如何に便利かはいろんな方がすでに書いているのでそちらを参照して下さい。

さてとても便利なgoaですが、designで使えるTypeは全てのプリミティブ型をサポートしているわけではありません(v2から対応するらしいです)。

例えばint64は使えないのでStringで定義してstrconvなどで変換する必要がありました。

var Account = MediaType("application/vnd.account+json", func() {
    Description("A tenant account")
    Attributes(func() {
        Attribute("id", Integer, "ID of account", func() {
            Example(1)
        })
        Required("id")
    })

    View("default", func() {
        Attribute("id")
    })
})

※viewは省略してます

生成されるコード

type Account struct {
    // ID of account
    ID int `form:"id" json:"id" xml:"id"`
}

ここでgoa v1.3で追加された Metadata(struct:field:type) を使うと生成される型を上書きすることができます。

var Account = MediaType("application/vnd.account+json", func() {
    Description("A tenant account")
    Attributes(func() {
        Attribute("id", Integer, "ID of account", func() {
            Metadata("struct:field:type", "int64")
            Example(1)
        })
        Required("id")
    })
})

生成されるコード

type Account struct {
    // ID of account
    ID int64 `form:"id" json:"id" xml:"id"`
}

シンプルですがものすごく強力な機能だと思います。 int64な配列にも変換できます。

var Sample = MediaType("application/vnd.sample+json", func() {
    Description("struct:field:type sample")
    Attributes(func() {
        Attribute("id_list", ArrayOf(Integer), "ID List", func() {
            Metadata("struct:field:type", "[]int64")
            Example([]int64{1, 2, 3})
        })
        Required("id_list")
    })
})
type Sample struct {
    // ID List
    IDList []int64 `form:"id_list" json:"id_list" xml:"id_list"`
}

ただ当然といえば当然ですが、ValidationはMetadataで上書きした型に対しては上手く使えない場合があります。 例えば

var Account = MediaType("application/vnd.account+json", func() {
    Description("A tenant account")
    Attributes(func() {
        Attribute("id", Integer, "ID of account", func() {
            Metadata("struct:field:type", "uint64")
            Example(1)
            Minimum(1)
            Maximum(math.MaxUint64)
        })
        Required("id")
    })
})

上記の用にuint64で上書きしてもMaximumの引数がintのためmath.MaxUint64を指定すると

constant 18446744073709551615 overflows int

のようにビルドエラーになってしまいます。(uint64の時点で範囲は決まってるので上のValidationは意味ないかもですが)

個人的にgoaを使っていて一番不便なところが解消されたので既存のコードもgoaに移行しちゃっていいかなと思ってきました。

以上です。

※追記(2017/12/6)

  • 配列のサンプルコードが間違ってたのを修正
  • リソース内のParamではMetadataによるtypeの上書きはできない模様
var _ = Resource("sample", func() {
    BasePath("/sample")

    Action("test", func() {
        Description(`sample`)
        Routing(
            GET("/:ID"),
        )
        Params(func() {
            Param("ID", Integer, "ID", func() {
                Metadata("struct:field:type", "int64")
            })
        })
        Response(OK, SampleMedia)
        Response(InternalServerError)
    })
})

生成されるcontext

type TestSampleContext struct {
    context.Context
    *goa.ResponseData
    *goa.RequestData
    ID int // 😱
}