-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Mount example doesn't work. what's the right way to have both v1 and v2 on a server? #190
Comments
Thank you for raising this, this example is now fixed in latest |
Thank you for the quick update. I've tried it and no more errors. apiV1.Post("/sum", sum())
s.Mount("/api/v1", apiV1) seems providing same function as using s.Route("/api/v1", func(r chi.Router) {
r.Group(func(r chi.Router) {
r.Use(sessMW, sessDoc)
r.Method(http.MethodGet, "/sum", nethttp.NewHandler(sum()))
})
}) it just adds pattern what I want to do is to have server version options selectable and make routes mapping correctly in swagger GUI. r := openapi3.NewReflector()
r.Spec.WithServers(
openapi31.Server{
URL: "/api/v1",
},
openapi31.Server{
URL: "/api/v2",
}
)
s := web.NewService(r) and if choose |
Hi, I think you need both individual spec configuration for each versioned API and Swagger UI setup that allows selection from multiple API specs. Please check an example https://github.com/swaggest/rest/blob/master/_examples/multi-api/main.go // Package main implements an example where two versioned API revisions are mounted into root web service
// and are available through a service selector in Swagger UI.
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/go-chi/chi/v5/middleware"
"github.com/swaggest/openapi-go"
"github.com/swaggest/openapi-go/openapi3"
"github.com/swaggest/rest/nethttp"
"github.com/swaggest/rest/web"
swg "github.com/swaggest/swgui"
swgui "github.com/swaggest/swgui/v5emb"
"github.com/swaggest/usecase"
)
func main() {
fmt.Println("Swagger UI at http://localhost:8010/api/docs.")
if err := http.ListenAndServe("localhost:8010", service()); err != nil {
log.Fatal(err)
}
}
func service() *web.Service {
// Creating root service, to host versioned APIs.
s := web.NewService(openapi3.NewReflector())
s.OpenAPISchema().SetTitle("Security and Mount Example")
// Each versioned API is exposed with its own OpenAPI schema.
v1r := openapi3.NewReflector()
v1r.SpecEns().WithServers(openapi3.Server{URL: "/api/v1/"}).WithInfo(openapi3.Info{Title: "My API of version 1"})
apiV1 := web.NewService(v1r)
v2r := openapi3.NewReflector()
v2r.SpecEns().WithServers(openapi3.Server{URL: "/api/v2/"})
apiV2 := web.NewService(v2r)
// Versioned APIs may or may not have their own middlewares and wraps.
apiV1.Wrap(
middleware.BasicAuth("Admin Access", map[string]string{"admin": "admin"}),
nethttp.HTTPBasicSecurityMiddleware(s.OpenAPICollector, "Admin", "Admin access"),
nethttp.OpenAPIAnnotationsMiddleware(s.OpenAPICollector, func(oc openapi.OperationContext) error {
oc.SetTags(append(oc.Tags(), "V1")...)
return nil
}),
)
apiV1.Post("/sum", sum())
apiV1.Post("/mul", mul())
// Once all API use cases are added, schema can be served too.
apiV1.Method(http.MethodGet, "/openapi.json", specHandler(apiV1.OpenAPICollector.SpecSchema()))
apiV2.Post("/summarization", sum())
apiV2.Post("/multiplication", mul())
apiV2.Method(http.MethodGet, "/openapi.json", specHandler(apiV2.OpenAPICollector.SpecSchema()))
// Prepared versioned API services are mounted with their base URLs into root service.
s.Mount("/api/v1", apiV1)
s.Mount("/api/v2", apiV2)
// Root docs needs a bit of hackery to expose versioned APIs as separate services.
s.Docs("/api/docs", swgui.NewWithConfig(swg.Config{
ShowTopBar: true,
SettingsUI: map[string]string{
// When "urls" are configured, Swagger UI ignores "url" and switches to multi API mode.
"urls": `[
{"url": "/api/v1/openapi.json", "name": "APIv1"},
{"url": "/api/v2/openapi.json", "name": "APIv2"}
]`,
`"urls.primaryName"`: `"APIv2"`, // Using APIv2 as default.
},
}))
// Blanket handler, for example to serve static content.
s.Mount("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("blanket handler got a request: " + r.URL.String()))
}))
return s
}
func specHandler(s openapi.SpecSchema) http.Handler {
j, err := json.Marshal(s)
if err != nil {
panic(err)
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(j)
})
}
func mul() usecase.Interactor {
return usecase.NewInteractor(func(ctx context.Context, input []int, output *int) error {
*output = 1
for _, v := range input {
*output *= v
}
return nil
})
}
func sum() usecase.Interactor {
return usecase.NewInteractor(func(ctx context.Context, input []int, output *int) error {
for _, v := range input {
*output += v
}
return nil
})
} |
@vearutop Thank you for the example code update, it does solved most of the problem, I can now use banner to switch json profile and gui reflects endpoint pattern correctly. s.Route("/data", func(r chi.Router) {
r.Group(func(r chi.Router) {
r.Use(sessMW, sessDoc)
r.Method(http.MethodGet, "/sum", nethttp.NewHandler(sum()))
})
}) the api is functioning correctly and accepting auth, but GUI doesn't have auth options/icon displayed, and the example code apiV1.Wrap(
middleware.BasicAuth("Admin Access", map[string]string{"admin": "admin"}),
nethttp.HTTPBasicSecurityMiddleware(s.OpenAPICollector, "Admin", "Admin access"),
nethttp.OpenAPIAnnotationsMiddleware(s.OpenAPICollector, func(oc openapi.OperationContext) error {
oc.SetTags(append(oc.Tags(), "V1")...)
return nil
}),
) causes the whole apiV1 page becomes BasicAuth |
Ok, I kind of got it work by apiV1.Route("/data", func(r chi.Router) {
r.Group(func(r chi.Router) {
r.Use(serviceTokenAuth, serviceTokenDoc, checkSize)
r.Method(http.MethodGet, "/sum", nethttp.NewHandler(sum()))
})
})
// Swagger GUI to have authorization schema and input
apiV1.OpenAPISchema().SetAPIKeySecurity("apiKey", "Authorization", oapi.InHeader, "API Key.")
// to add authorization schema under route group so that Swagger GUI example curl can call
for _, pi := range v1r.Spec.Paths.MapOfPathItemValues {
pi.Post.Security = []map[string][]string{
{
"apiKey": []string{},
},
}
}
apiV1.Method(http.MethodGet, "/docs/openapi.json", specHandler(apiV1.OpenAPICollector.SpecSchema())) but it's not ideal, I'd prefer |
Hey guys,
Very good project for swagger 3.x, really appreciated your work.
I'd like to create a server with v1 and v2 server url, so was trying to follow https://github.com/swaggest/rest/blob/master/_examples/mount/main.go example to mount endpoints under
api/v1
, but has following error withservice.Mount("/api/v1", apiV1)
:I also tried to use
with this code I can see server url options in the swagger gui, but the actual endpoint logic is not correctly mapped to server selection. I'd expect to be able to call endpoint
<url>/api/v1/data
, but the server is actually only listening on<url>/data
, the swagger GUI call test does show correct curl example<url>/api/v1/data
though.The text was updated successfully, but these errors were encountered: