Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
Go-Swagger in production
Ilya Kaznacheev
May 21, 2020
Programming
1
220
Go-Swagger in production
Ilya Kaznacheev
May 21, 2020
Tweet
Share
More Decks by Ilya Kaznacheev
See All by Ilya Kaznacheev
Golang: прошлое и будущее
dreamworm
0
68
Godoc: хороший, плохой, злой
dreamworm
0
18
Godoc: the Good, the Bad and the Ugly
dreamworm
0
96
Go-Swagger in production
dreamworm
0
200
Организация локальных сообществ: как, зачем и почему
dreamworm
0
28
Go-Swagger в продуктиве
dreamworm
0
110
Что OpenTelemetry нам готовит?
dreamworm
0
96
Поворот на 90°
dreamworm
1
150
Данилин Андрей - Построение offline приложения в SAP UI5
dreamworm
1
98
Other Decks in Programming
See All in Programming
あなたの会社の古いシステム、なんとかしませんか?~システム刷新から考えるDX化への道筋とバリエーション~/webinar20220420-systems
grapecity_dev
0
130
Oculus Interaction SDK 概説 / xrdnk-caunity-LT4
xrdnk
0
300
Loom is Blooming
josepaumard
3
570
byte列のbit表現を得るencodingライブラリ作った
convto
1
210
mrubyを1300円のボードで動かそう
yuuu
0
190
脱オブジェクト指向講座(5分LT資料)
kishida
8
11k
WindowsコンテナDojo:第3回 ASP.NET 入門、これまでの技術とCore Blazor
oniak3ibm
PRO
0
100
GraphQL+KMM開発でわかったこと / What we learned from GraphQL+KMM development
kubode
0
140
熊でもわかるCI/CD/モダンインフラVol1:用語を覚えよう編
srockstyle
0
110
近況PHP / PHP in now a days
uzulla
4
2k
Quartoを使ってみませんか / quarto_get_started
s_uryu
2
440
Swift Concurrencyによる安全で快適な非同期処理
tattn
2
340
Featured
See All Featured
The Pragmatic Product Professional
lauravandoore
19
2.9k
Ruby is Unlike a Banana
tanoku
91
9.2k
Designing for humans not robots
tammielis
241
23k
Testing 201, or: Great Expectations
jmmastey
21
5.4k
Imperfection Machines: The Place of Print at Facebook
scottboms
253
11k
It's Worth the Effort
3n
172
25k
Done Done
chrislema
174
14k
The Brand Is Dead. Long Live the Brand.
mthomps
45
2.7k
Web development in the modern age
philhawksworth
197
9.3k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
655
120k
Documentation Writing (for coders)
carmenhchung
48
2.5k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
181
15k
Transcript
Go-Swagger in production wins and pitfalls
Ilya Kaznacheev Remote Backend SWE Founder of Golang Voronezh Host
of Z-Namespace podcast Organizer of conference and meetups Coffee geek
Golang Voronezh - ~30 active members - meetups - events
for beginners - open & friendly
what swagger is?
None
SOAP JSON-PRC GraphQL gRPC OData REST
Representational state transfer (REST) is a software architectural style that
defines a set of constraints to be used for creating Web services Wikipedia
None
swagger: "2.0" info: title: Pet API version: "1.0.0" basePath: /api
schemes: - http paths: /pets: get: summary: List all pets parameters: - name: limit in: query description: "How many items to return at one time" required: true type: integer responses: 200: description: an paged array of pets 400: description: unexpected error
None
None
why do we use swagger?
my team trying to sync API changes...
None
go-swagger
code generation swagger generate server -t internal/api --exclude-main
generated code structure internal/api ├ models │ └ ... └
restapi ├ operations │ └ ... ├ configure_<your_service_name>.go ├ doc.go ├ embedded_spec.go └ server.go
our code generation rm -rf internal/api && mkdir -p internal/api
swagger generate server -t internal/api --exclude-main go mod tidy
and we're all set ?
NO
there are some problems - go-swagger is a framework, not
a library - plenty of generated types for everything - incompatible with popular http-libraries
let’s fix ’em all!
serving net/http handlers type CustomResponder func(http.ResponseWriter, runtime.Producer) func (c CustomResponder)
WriteResponse(w http.ResponseWriter, p runtime.Producer) { c(w, p) } func MetricsHandler(p instruments.GetMetricsParams) middleware.Responder { return CustomResponder(func(w http.ResponseWriter, _ runtime.Producer) { promhttp.Handler().ServeHTTP(w, p.HTTPRequest) }) }
simple middleware api := operations.NewSwaggerPetstoreAPI(swaggerSpec) api.InstrumentsGetMetricsHandler = instruments.GetMetricsHandlerFunc(MetricsHandler) api.AddMiddlewareFor("GET", "/metrics",
SomeMiddleware) srv := restapi.NewServer(api) srv.Serve()
middleware with custom handler h := api.Serve(nil) r := chi.NewRouter()
r.Use( middleware.Recoverer, ) r.With(AuthMiddleware).Group(func(r chi.Router) { r.Handle("/user/*", h) }) r.Mount("/", h) srv.ConfigureAPI() srv.SetHandler(r) srv.Serve()
setup outside of configure_<your_service_name>.go api.Logger = log.Printf api.HTMLProducer = runtime.TextProducer()
srv := restapi.NewServer(api) srv.EnabledListeners = []string{"http"} srv.Port = conf.HTTPPort srv.Host = conf.HTTPAddr
custom method names /store/order/{orderId}/items: get: tags: - store summary: Find
purchase order items parameters: - name: orderId in: path required: true type: integer func GetOrderItems( param store.GetStoreOrderOrderIDItemsParams, ) middleware.Responder { items, err := getOrderItems(param.OrderID) if err != nil { return store.NewGetStoreOrderOrderIDItemsNotFound() } res := &models.OrderItems{} // // fill resopnse // return store.NewGetStoreOrderOrderIDItemsOK(). WithPayload(res) }
custom method names /store/order/{orderId}/items: get: tags: - store summary: Find
purchase order items operationId: getOrderItems parameters: - name: orderId in: path required: true type: integer func GetOrderItems( param store.GetOrderItemsParams, ) middleware.Responder { items, err := getOrderItems(param.OrderID) if err != nil { return store.NewGetOrderItemsNotFound() } res := &models.OrderItems{} // // fill resopnse // return store.NewGetOrderItemsOK(). WithPayload(res) }
validity checks OrderItems: type: object properties: message: type: string maximum:
3 # swg/internal/api/models internal/api/models/order_items.go:45:55: cannot convert m.Message (type string) to type float64
validity check cheat sheet numbers and integers - multipleOf -
maximum - minimum - exclusiveMaximum - exclusiveMinimum strings - maxLength - minLength - pattern arrays - maxItems - minItems - uniqueItems - maxContains - minContains objects - maxProperties - minProperties - required - dependentRequired any type - type - enum - const
extensions (tricks) x-omitempty x-nullable x-isnullable x-order x-go-custom-tag x-schemes x-go-name x-go-type
x-go-json-string x-go-enum-ci
unit tests func GetOrderByID(param store.GetOrderByIDParams) middleware.Responder { order := models.Order{
ID: 123, PetID: 456, Quantity: 20, Status: "approved", } if param.OrderID != order.ID { return store.NewGetOrderByIDNotFound().WithPayload(&models.ErrorMessage{ Code: http.StatusNotFound, Message: http.StatusText(http.StatusNotFound), }) } return store.NewGetOrderByIDOK().WithPayload(&order) }
unit tests tests := []struct { name string req store.GetOrderByIDParams
code int want string }{ { name: "good test", req: store.GetOrderByIDParams{OrderID: 123}, code: 200, want: `{"id":123,"petId":456,"quantity":20,"status":"approved"}`, }, { name: "bad test", req: store.GetOrderByIDParams{OrderID: 456}, code: 404, want: `{"message":"Not Found", "code":404}`, }, }
unit tests for _, tt := range tests { t.Run(tt.name,
func(t *testing.T) { rr := httptest.NewRecorder() GetOrderByID(tt.req).WriteResponse(rr, runtime.JSONProducer()) assert.JSONEq(t, tt.want, rr.Body.String(), "wrong response body") assert.Equal(t, tt.code, rr.Code, "wrong response code") }) }
None
helpful links json-schema.org/specification.html swagger.io/docs/specification/2-0 goswagger.io
bonus A pluggable go-swagger (in development) github.com/ilyakaznacheev/go-plugger
bonus 2 Insomnia Designer insomnia.rest/products/designer
None
ilyakaznacheev