【アジャイル系男子】Go Gin Server + OpenAPI Generator 爆速サイクル戦線で生き抜く ⚔⚔

おはようございます。最近仕事が全然進まず、一寸先は闇状態の社会人2年目の清田です。☔☔☔☔
前回の記事「俺的【OAS】との向き合い方 (爆速でOpenAPIと友達になろう)」が好評らしいので今回は続きを書くことにいたしました。🐈🐈🐈
この記事を読んでほしい人

  • Go言語入門者 or 修行中の方
  • OpenAPI Generator入門者 or 修行中の方
  • Hello World

tech-blog.optim.co.jp

アジャイル系男子の日常

A「Spring bootよりGo Ginサーバーとかどう」

                         「キー(おっけーよ)」私

A「マイクロサービスにはもってこいだよね?」

                  「キー(なにそれおいしいの)」私

まぁ大体こんな感じで、アジャイル系男子の十八番にでもなっているのではないでしょうか。
Go言語嫌い大好きです。

Go Ginとは❔❔

Go言語のWebアプリケーションのフレームワークらしい。
そしてこちらを見てほしい。
おわかりいただけただろうか。

  • MIT(ライセンス)
  • 42.3k(star)
  • 君に決めた

github.com

自動生成手順📚

1. ファイル構成

└─openapi/
  └─openapi.yml 

2.openapi/openapi.yml

openapi: 3.0.1
info:
  title: title
  version: 1.0.0
paths:
  '/sample/point/{pathParam1}':
    get:
      tags:
        - api1
      operationId: getSamplePoint
      parameters:
        - $ref: '#/components/parameters/pathParam1'
        - $ref: '#/components/parameters/queryParam1'
      responses:
        '200':
          description: Pointの取得成功
          content:
            application/json: 
              schema:
                $ref: '#/components/schemas/SampleArray'
        '400':
          description: 不正なリクエストです。
        '401':
          description: 認証エラーです。

components:
  parameters:
    pathParam1: 
      in: path
      name: param1
      schema:
        type: string
      required: true
    queryParam1:
      in: query
      name: param2
      schema:
        type: string
      required: false
  schemas:
    SampleArray:
      type: array
      items:
        $ref: '#/components/schemas/SampleResponse'
    SampleResponse:
      type: object
      properties:
        value:
          type: string

3.生成コマンド

$docker pull openapitools/openapi-generator-cli

$docker run --rm -v ${PWD}/openapi:/local -v ${PWD}:/project openapitools/openapi-generator-cli generate -i  /local/openapi.yml -g go-gin-server -o /project

4.★うわぁ。できたぁ。★

├─.openapi-generator-ignore
├─Dockerfile
├─main.go
├─.openapi-generator
│ ├─FILES
│ └─VERSION     
├─api
│ └─openapi.yml
├─go
│ ├─api_api1.go
│ ├─model_sample_response.go
│ ├─README.md
│ └─routers.go
└─openapi
  └─openapi.yml

そして重要なことに気が付きます💦💦

Go言語に高階関数が見当たらない🔍

  • 現状Goはジェネリクスがない?
  • Go2とかいうバージョンが来たらジェネリクスが使える?
  • それはいつなのか

GitHub - thoas/go-funk: A modern Go utility library which provides helpers (map, find, contains, filter, ...)

GitHub - wesovilabs/koazee: A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices.

GitHub - ahmetb/go-linq: .NET LINQ capabilities in Go

ジェネリクスがないと辛いと悟り、奇跡の出会いをした。

Go Generate との出会い

blog.golang.org

qiita.com

qiita.com

qiita.com

speakerdeck.com

後半へ続く

新解体実行手順 ~Go2まで耐え抜く編~

解らないし高階関数ジェネレーターを適当に作っちゃおう(業務で作ったやつコピペ)🔨🔨

github.com

型変換も一行でやりたいよね。作っちゃお(業務で作ったやつコピペ)🔨🔨

github.com

1.ファイル構成

└─openapi/
  ├─codegen <- New holder
  │ └─model.mustache <- New File
  └─openapi.yml 

2. openapi/model.mustache

{{>partial_header}}
package {{packageName}}
{{#models}}{{#imports}}
{{#-first}}import (
{{/-first}} "{{import}}"{{#-last}}
)
{{/-last}}{{/imports}}{{#model}}{{#isEnum}}{{#description}}// {{{classname}}} : {{{description}}}{{/description}}
type {{{name}}} {{^format}}{{dataType}}{{/format}}{{#format}}{{{format}}}{{/format}}

// List of {{{name}}}
const (
  {{#allowableValues}}
  {{#enumVars}}
  {{{classname}}}_{{name}} {{{classname}}} = {{{value}}}
  {{/enumVars}}
  {{/allowableValues}}
){{/isEnum}}{{^isEnum}}{{#description}}
// {{classname}} - {{{description}}}{{/description}}
//go:generate stream-generator -type={{classname}}
type {{classname}} struct {
{{#vars}}{{#description}}
  // {{{description}}}{{/description}}
  {{name}} {{#isNullable}}*{{/isNullable}}{{{dataType}}} `json:"{{baseName}}{{^required}},omitempty{{/required}}"{{#vendorExtensions.x-go-custom-tag}} {{{.}}}{{/vendorExtensions.x-go-custom-tag}}`
{{/vars}}
}{{/isEnum}}{{/model}}{{/models}}

3.生成コマンドをもう一度

$docker pull openapitools/openapi-generator-cli

$docker run --rm -v ${PWD}/openapi:/local -v ${PWD}:/project openapitools/openapi-generator-cli generate -i  /local/openapi.yml -g go-gin-server -o /project -t /local/codegen

4. go generate

$go get -u github.com/optim-kazuhiro-seida/stream-generator
$go mod init sample
go: creating new go.mod: module sample

$cd go
$go generate
go: finding module for package github.com/gin-gonic/gin
go: found github.com/gin-gonic/gin in github.com/gin-gonic/gin v1.6.3
2020/10/16 04:03:24 Generating Struct name:  SampleResponse
2020/10/16 04:03:24 Scan Type Name.
2020/10/16 04:03:27 Scan Package Name.
2020/10/16 04:03:27 Generated C:\Users\kazuhiro.seida\work\hatena-test\go\stream_sampleresponse.go

参考

5.★うわぁ。できたぁ。2★

├─.openapi-generator-ignore
├─Dockerfile
├─main.go
├─.openapi-generator
│ ├─FILES
│ └─VERSION     
├─api
│ └─openapi.yml
├─go
│ ├─api_api1.go
│ ├─model_sample_response.go
│ ├─README.md
│ ├─routers.go
│ └─stream_sampleresponse.go
└─openapi/
  ├─codegen
  │ └─model.mustache
  └─openapi.yml

とりあえず適当なレスポンス返してみる。🌶

main.go

package main

import (
  "log"
  sw "sample/go"
)

func main() {
  log.Printf("Server started")

  router := sw.NewRouter()

  log.Fatal(router.Run(":8080"))
}

go/api_api1.go

package openapi

import (
  "net/http"

  "github.com/gin-gonic/gin"
)

// GetSamplePoint -
func GetSamplePoint(c *gin.Context) {
  var (
    entity = SampleResponseStreamOf()
  )
  entity.
    AddAll(
      SampleResponse{"1"},
      SampleResponse{"2"},
      SampleResponse{"3"},
    )
  c.JSON(http.StatusOK, entity)
}

実行👼

$go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /                         --> sample/go.Index (3 handlers)
[GIN-debug] GET    /sample/point/:pathParam1 --> sample/go.GetSamplePoint (3 handlers)
[GIN-debug] Listening and serving HTTP on :8080
[GIN] 2020/10/16 - 04:20:53 |?[97;42m 200 ?[0m|            0s |             ::1 |?[97;44m GET     ?[0m "/sample/point/hogehoge"
[GIN] 2020/10/16 - 04:21:10 |?[97;42m 200 ?[0m|            0s |             ::1 |?[97;44m GET     ?[0m "/sample/point/hogehoge"

上が出たら成功!!!

確認👼👼

$curl --location --request GET 'http://localhost:8080/sample/point/hogehoge'
[{"value":"1"},{"value":"2"},{"value":"3"}]

上が出たら成功!!!!!!!
そう僕たちは成し遂げたんだ。
大きな一歩を(思い込み)

Gitで管理する際のテンプレート

.gitignore

api
go/model_*
go/utils.go
go/routers.go
go.sum
.idea/*
.openapi-generator/*
go/.openapi-generator/*
**.exe

.openapi-generator-ignore

go/api_*
go/README.md
main.go
api/*
Dockerfile

docker-compose.yml

version: '3'
services:
  openapi-generator:
    image: openapitools/openapi-generator-cli
    command: generate -i /local/openapi.yml -g go-gin-server -o /project -t /local/codegen
    volumes:
      - ./openapi:/local
      - .:/project

最後に

完全なサンプル

github.com

まとめ

今回はGo言語をたくさん書いても全然慣れないjava大好きっ子が、記事を書かせていただきました。 かなりざっくりとした内容ですので、どこかの誰かの役に立つことを祈っています。

オプティムはIT戦線の最前線希望の戦友エンジニアを募集しています❗

www.optim.co.jp