-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in 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: