テキトウな設定ファイル(yaml,json)から、swagger specを生成して、goのstructを定義してloadしてみる
テキトウな設定ファイル(yaml,json)から、swagger specを生成して、goのstructを定義してloadしてみることにしてみた。 生成されるswagger specとgoのstruct定義は雛形っぽい感じであんまり真面目に作っていない。とりあえず雰囲気だけでも分かるようにまとめておく。
手順
手順はMakefileに
default: json2swagger --name conf ./config.yaml > swagger.yaml python gen.py swagger.yaml --package main --ref "#/definitions/conf" > conf.go dictknife concat config.yaml -f json | go run *.go setup: pip install dictknife json2swagger go get -v github.com/k0kubun/pp
詳細
設定ファイルのサンプルはテキトウにmongodbのところから持ってきた
こういうyaml
config.yaml
# from: https://docs.mongodb.com/v3.2/reference/configuration-options/ systemLog: destination: file path: "/var/log/mongodb/mongod.log" logAppend: true storage: journal: enabled: true processManagement: fork: true net: bindIp: 127.0.0.1 port: 27017 setParameter: enableLocalhostAuthBypass: false
json2swaggerで設定ファイルのyamlからswagger specを生成
これをswagger specに変換すると以下の様な感じ。
swagger.yaml
definitions: systemLog: type: object properties: destination: type: string example: file path: type: string example: /var/log/mongodb/mongod.log logAppend: type: boolean example: true required: - destination - path - logAppend journal: type: object properties: enabled: type: boolean example: true required: - enabled storage: type: object properties: journal: $ref: '#/definitions/journal' required: - journal processManagement: type: object properties: fork: type: boolean example: true required: - fork net: type: object properties: bindIp: type: string example: 127.0.0.1 port: type: integer example: 27017 required: - bindIp - port setParameter: type: object properties: enableLocalhostAuthBypass: type: boolean example: false required: - enableLocalhostAuthBypass conf: type: object properties: systemLog: $ref: '#/definitions/systemLog' storage: $ref: '#/definitions/storage' processManagement: $ref: '#/definitions/processManagement' net: $ref: '#/definitions/net' setParameter: $ref: '#/definitions/setParameter' required: - systemLog - storage - processManagement - net - setParameter
作った gen.pyでswagger specからgoのstruct定義を生成
ここから以下のようなgoのstructを作る。生成されるstructはdefaultは値。pointerにしたければ x-nullable=true
を追加する。このあたりの変換の基準はswaggerのそれとは合っていないので注意(真面目に対応しようとするとgo-swaggerと同様にほとんどがpointerになってしまう)。
package main // Conf : type Conf struct { Storage Storage `json:"storage" bson:"storage"` ProcessManagement ProcessManagement `json:"processManagement" bson:"processManagement"` Net Net `json:"net" bson:"net"` SystemLog SystemLog `json:"systemLog" bson:"systemLog"` SetParameter SetParameter `json:"setParameter" bson:"setParameter"` } // Storage : type Storage struct { Journal Journal `json:"journal" bson:"journal"` } // Journal : type Journal struct { Enabled bool `json:"enabled" bson:"enabled"` } // ProcessManagement : type ProcessManagement struct { Fork bool `json:"fork" bson:"fork"` } // Net : type Net struct { BindIP string `json:"bindIp" bson:"bindIp"` Port int64 `json:"port" bson:"port"` } // SystemLog : type SystemLog struct { Destination string `json:"destination" bson:"destination"` Path string `json:"path" bson:"path"` LogAppend bool `json:"logAppend" bson:"logAppend"` } // SetParameter : type SetParameter struct { EnableLocalhostAuthBypass bool `json:"enableLocalhostAuthBypass" bson:"enableLocalhostAuthBypass"` }
dictknife concat config.yaml -f json
は yamlをjsonに変換するために使っている。作ったmain.goはjsonにしか対応していなかったので。
package main import ( "encoding/json" "log" "os" "github.com/k0kubun/pp" ) func main() { decoder := json.NewDecoder(os.Stdin) var conf Conf if err := decoder.Decode(&conf); err != nil { log.Fatal(err) } pp.Print(conf) }
実際にloadして使ってみる
実際上手く読み込めて以下のような出力を返す。
$ json2swagger --name conf ./config.yaml > swagger.yaml $ python gen.py swagger.yaml --package main --ref "#/definitions/conf" > conf.go $ dictknife concat config.yaml -f json | go run *.go main.Conf{ SetParameter: main.SetParameter{ EnableLocalhostAuthBypass: false, }, Net: main.Net{ BindIP: "127.0.0.1", Port: 27017, }, ProcessManagement: main.ProcessManagement{ Fork: true, }, SystemLog: main.SystemLog{ LogAppend: true, Path: "/var/log/mongodb/mongod.log", Destination: "file", }, Storage: main.Storage{ Journal: main.Journal{ Enabled: true, }, }, }
swagger specを生成するときにちょっとした変更を追加
--annotations
オプションにjson reference(ほとんどjson pointer)と対応する追加の設定を書くとswagger specに反映させられる。
nameはschemaの名前になる。それ以外は全て階層上の属性として追加される。配列への指定は末尾に[]
をつける(e.g. #/definitions/ob/foo[]
)。
生のconfigの値から生成した場合にschemaの名前が期待した値にならない場合がある(例えば、personという名前のschemaであって欲しいfatherとmotherがfatherという名前のschemaとして定義されてしまうなど)。それを防ぐためにnameを指定できるようにしたけれど。そう言えばdescriptionなどを追加する機会もないということに気づいたのでそれらも追加出来るようにした。あんまり考えていないので安直な対応かもしれない。
"#/conf": description: my config file "#/conf/systemLog": name: LogSetting "#/conf/processManagement": name: ProcessSetting "#/conf/net": name: NetSetting "#/conf/storage": name: StorageSetting
そしてswagger.yamlを再生成
--- swagger.before.yaml 2017-05-03 17:17:41.000000000 +0900 +++ swagger.yaml 2017-05-03 17:17:06.000000000 +0900 @@ -1,5 +1,5 @@ definitions: - systemLog: + LogSetting: type: object properties: destination: @@ -23,14 +23,14 @@ example: true required: - enabled - storage: + StorageSetting: type: object properties: journal: $ref: '#/definitions/journal' required: - journal - processManagement: + ProcessSetting: type: object properties: fork: @@ -38,7 +38,7 @@ example: true required: - fork - net: + NetSetting: type: object properties: bindIp: @@ -62,13 +62,13 @@ type: object properties: systemLog: - $ref: '#/definitions/systemLog' + $ref: '#/definitions/LogSetting' storage: - $ref: '#/definitions/storage' + $ref: '#/definitions/StorageSetting' processManagement: - $ref: '#/definitions/processManagement' + $ref: '#/definitions/ProcessSetting' net: - $ref: '#/definitions/net' + $ref: '#/definitions/NetSetting' setParameter: $ref: '#/definitions/setParameter' required: @@ -77,3 +77,4 @@ - processManagement - net - setParameter + description: my config file