いかにCloudEndpointsをやめSwaggerを愛するようになったか ajn #33 http://gcpja.connpass.com/event/30761/
͍͔ʹCloudEndpointsΛΊSwaggerΛѪ͢ΔΑ͏ʹͳ͔ͬͨΘ͔Ί ·͞ͻΖ
View Slide
Θ͔Ί ·͞ͻΖ @v vakameTypeScriptMasahiro WakameDefinitelyTypedappengine/gophoto from golang.org/doc/gopher/
Google API Discovery Service୭͔ͬͯΔʁ
APIs Explorer is ਆhttps://developers.google.com/apis-explorer/
APIs Explorer• ୭Ͱ؆୯ʹ͑Δ• ࣮ࡍͷAPI͕ୟ͔ΕΔ• ݁ՌΛڞ༗͍͢͠• ίʔυ͔ΒUI͕ੜ͞Ε͍ͯΔ• ίʔυ == CloudEndpointsCloudEndpoints→APIs Explorer
but…
GoogleͷҋͷྖҬϢʔβ GAEҋpath mappingrequest formatVersionସޙreqΛͳ͔ͬͨࣄʹcustom domainෆՄgo-endpoints͕ͨ·ʹͭΒ͍
Swaggerͦ͜ͰSwaggerͰ͢Α
✨swagger✨• Swagger͕Ұ൪ྑͦ͞͏ʂ• RAML, JSON Schema etc…• Open API Initiativeൃ• Swagger༷Λbaseʹ• ͍ͷʹר͔Ε͍ͨ• Qiitaʹൺֱ·ͱΊ goo.gl/BLS3uH
ucon࡞Δطଘ࣮ௐ͚ͨͲࣗͰ࡞Δ͜ͱʹͨ͠https://github.com/favclip/ucon
લఏ• appengineറΓʹ͠ͳ͍• ͱ͍͑appengineͰ͑ͳ͍ͱࠔΔ• net/httpʹ͍ۙAPI• ॊೈੑ• go-endpointsͱͷޓੑ• swaggeropt-in͍ͬͯ͘
໊͚a2c͞Μuconͱ໊͚Α͏ʂ
ܾΊͨޙa2c͞Μ←ΏΔ͞ͳ͍
ಈ͘σϞؒʹ߹Θͳ͍͔ͱࢥͬͨΒ͍ͭͷؒʹ͔ͬͯ͋ͬͨ
TODO• github.com/vvakame/ucon-todo• ucon-todo.appspot.com/• ucon-todo.appspot.com/swagger-ui/• ࣭ૉಈ͘ʂ
ར༻ࣄྫ
ར༻αΠτ• favclip• ٕज़ॻయ• TopgateࣾͰࠓޙ͍ͬͯͣ͘…
༷հ
ucon Features• net/http ͱͷྨࣅੑ• Routing• Method, Path Matching• Middleware• Bubble• Dependency Injection• Plugin
API likes net/httpucon.HandleFunc("GET", "/", func(w http.ResponseWriter, r *http.Request) {})
Routingucon.HandleFunc(“*", “/“, …ucon.HandleFunc(“OPTIONS", “/“, …ucon.HandleFunc(“GET", “/“, …ucon.HandleFunc(“POST", “/“, …ucon.HandleFunc(“GET", “/api/user“, …ucon.HandleFunc(“GET", “/api/user/me“, …ucon.HandleFunc(“GET", “/api/user/{id}“, …
Routing rule• METHOD͕Ұக͢Δ• * ࢦఆՄ ݫີҰக༏ઌ• Request Path͕Ұக͢Δ• ෳީิ͋Δ߹ΑΓ͍અҰக• Request GET /api/user/123• GET /api/user/{id}• ❌ GET /api/user• ઌొ༏ઌ
Middleware• 1 requestຖͷॲཧʹհೖ• JavaͰ͍͏ServletFilter• ASP.NET MVCͰ͍͏Filter• Logging, DI, CORS༻Header,error→JSONม etc, etc…
MiddlewareMiddlewareMiddlewareMiddlewareHandlerServeHTTP DICache-ControlCookieappengine.Contextetc, etc…CORS HeaderPath, Query, Body → JSON*http.Requesthttp.ResponseWriter
Middlewaretype MiddlewareFunc func(b *Bubble) errortype Bubble struct { R *http.Request W http.ResponseWriter Context context.Context RequestHandler interface{} ArgumentTypes []reflect.Type Arguments []reflect.Value Returns []reflect.Value } func (b *Bubble) Next() error {… }func (b *Bubble) do() error { hv := reflect.ValueOf(b.handler()) … b.Returns = hv.Call(b.Arguments) return nil }var httpReqType = reflect.TypeOf((*http.Request)(var httpRespType = reflect.TypeOf((*http.Responsefunc HTTPRWDI() MiddlewareFunc { return func(b *Bubble) error { for idx, argT := range b.ArgumentTypes { if argT == httpReqType { b.Arguments[idx] = reflect.ValueOf(b.R) continue } if argT == httpRespType { b.Arguments[idx] = reflect.ValueOf(b.W) continue } } return b.Next() } }
built-in middleware• HTTPRWDI• *http.Request, http.ResponseWriterͷDI• NetContextDI• net/contextͷContextΛDI
built-in middleware• RequestObjectMapper• path parameter, query paramter, postbodyΛObjectʹม͠DI• ResponseMapper• Handler͕returnͨ͠ObjecterrorΛJSONʹม
Plugin• ϓϩηεىಈ࣌1ճ͚ͩಈ࡞• શHandlerͷࠪ• Handler→Pluginؒͷͷୡػߏ• swaggerplugin• શHandlerͷใ͔Βॲཧ• swagger.jsonग़ྗ༻HandlerͷՃ
Plugintype pluginContainer struct { base interface{} } type HandlersScannerPlugin interface { HandlersScannerProcess(m *ServeMux, rds []*RouteDefinition) error } type RouteDefinition struct { Method string PathTemplate *PathTemplate HandlerContainer HandlerContainer }func (m *ServeMux) Prepare() { for _, plugin := range m.plugins { used := false if sc := plugin.HandlersScanner(); sc != nil { err := sc.HandlersScannerProcess(m, m.router.handlers) if err != nil { panic(err) } used = true } if !used { panic(fmt.Sprintf("unused plugin: %#v", plugin)) } } }
swagger plugin usageswPlugin := swagger.NewPlugin(…) ucon.Plugin(swPlugin)s := &fooService{} tag := swPlugin.AddTag(&swagger.Tag{Name: "Foo", Description: ""}) var info *swagger.HandlerInfo info = swagger.NewHandlerInfo(s.List) ucon.Handle("GET", "/api/foo/{id}", info) info.Description, info.Tags = "FooΛ1݅औಘ͢Δ", []string{tag.Name}…type IntIDRequest struct { ID int64 `json:"id,string"` } func (s *fooService) Get(r *http.Request, req *IntIDRequest) (*FooJSON, error) {…}
swagger plugin usageswPlugin := swagger.NewPlugin(…) ucon.Plugin(swPlugin)s := &fooService{} tag := swPlugin.AddTag(&swagger.Tag{Name: "Foo", Description: ""}) var info *swagger.HandlerInfo info = swagger.NewHandlerInfo(s.List) ucon.Handle("GET", "/api/foo/{id}", info) info.Description, info.Tags = "FooΛ1݅औಘ͢Δ", []string{tag.Name}…type IntIDRequest struct { ID int64 `json:"id,string"` } func (s *fooService) Get(r *http.Request, req *IntIDRequest) (*FooJSON, error) {…}go-endpointsͱͷޓੑ!
ίʔυنͷ• ຊମ 1339ߦ• ls | grep .go | grep -v _test.go | xargs wc -l• swaggerϓϥάΠϯ 1138ߦ• find ./swagger -type f | grep .go | grep -v sample | grep -v _test.go | xargs wc -l
એ
6/25 () 11:00ʙ17:00ळ༿ݪ ௨ӡձٕؗज़ॻΦϯϦʔଈചձݸਓ੍࡞ͷٕज़ॻ✕56ஂମtechbookfest.org
ࣗલϥΠϒϥϦհͬͯΈͯͶʂ
GAE༻ϥΠϒϥϦ࡞ͬͯ·͢• testerator github.com/favclip/testrator• UnitTestߴԽ• qbg github.com/favclip/qbg• Datastore༻TypeSafeΫΤϦϏϧμ• smg github.com/favclip/smg• Search API༻TypeSafeϥούproductionͰར༻தʂ
ͦͷଞϥΠϒϥϦ࡞ͬͯ·͢• jwg github.com/favclip/jwg• JSON༻ͷTagͱ͔ࣗಈͰΔͭଞ• golidator github.com/favclip/golidator• Validatorศར