From 7280ebacf7b86777eb1975aabcc752dc50d661da Mon Sep 17 00:00:00 2001 From: Gustavo Detoni Date: Tue, 12 May 2026 23:36:31 -0300 Subject: [PATCH] Adding swagger advanced feature --- README.md | 9 ++- cmd/flags/advancedFeatures.go | 3 +- cmd/program/program.go | 71 +++++++++++++++++ cmd/steps/steps.go | 5 ++ .../advanced/files/swagger/imports/echo.tmpl | 2 + .../advanced/files/swagger/imports/fiber.tmpl | 2 + .../advanced/files/swagger/imports/gin.tmpl | 3 + .../swagger/imports/standard_library.tmpl | 2 + .../advanced/files/swagger/routes/chi.tmpl | 1 + .../advanced/files/swagger/routes/echo.tmpl | 1 + .../advanced/files/swagger/routes/fiber.tmpl | 1 + .../advanced/files/swagger/routes/gin.tmpl | 1 + .../files/swagger/routes/gorilla.tmpl | 1 + .../files/swagger/routes/http_router.tmpl | 1 + .../swagger/routes/standard_library.tmpl | 1 + cmd/template/advanced/routes.go | 77 +++++++++++++++++++ cmd/template/framework/chiRoutes.go | 8 ++ cmd/template/framework/echoRoutes.go | 8 ++ cmd/template/framework/fiberServer.go | 8 ++ cmd/template/framework/files/README.md.tmpl | 12 +++ .../framework/files/main/fiber_main.go.tmpl | 5 ++ .../framework/files/main/main.go.tmpl | 5 ++ cmd/template/framework/files/makefile.tmpl | 8 +- .../framework/files/routes/chi.go.tmpl | 15 +++- .../framework/files/routes/echo.go.tmpl | 15 +++- .../framework/files/routes/fiber.go.tmpl | 15 +++- .../framework/files/routes/gin.go.tmpl | 15 +++- .../framework/files/routes/gorilla.go.tmpl | 15 +++- .../files/routes/http_router.go.tmpl | 15 +++- .../files/routes/standard_library.go.tmpl | 15 +++- cmd/template/framework/ginRoutes.go | 8 ++ cmd/template/framework/gorillaRoutes.go | 8 ++ cmd/template/framework/httpRoutes.go | 8 ++ cmd/template/framework/routerRoutes.go | 8 ++ docs/docs/advanced-flag/advanced-flag.md | 5 +- docs/docs/advanced-flag/swagger.md | 19 +++++ docs/docs/creating-project/project-init.md | 9 ++- docs/mkdocs.yml | 1 + 38 files changed, 393 insertions(+), 13 deletions(-) create mode 100644 cmd/template/advanced/files/swagger/imports/echo.tmpl create mode 100644 cmd/template/advanced/files/swagger/imports/fiber.tmpl create mode 100644 cmd/template/advanced/files/swagger/imports/gin.tmpl create mode 100644 cmd/template/advanced/files/swagger/imports/standard_library.tmpl create mode 100644 cmd/template/advanced/files/swagger/routes/chi.tmpl create mode 100644 cmd/template/advanced/files/swagger/routes/echo.tmpl create mode 100644 cmd/template/advanced/files/swagger/routes/fiber.tmpl create mode 100644 cmd/template/advanced/files/swagger/routes/gin.tmpl create mode 100644 cmd/template/advanced/files/swagger/routes/gorilla.tmpl create mode 100644 cmd/template/advanced/files/swagger/routes/http_router.tmpl create mode 100644 cmd/template/advanced/files/swagger/routes/standard_library.tmpl create mode 100644 docs/docs/advanced-flag/swagger.md diff --git a/README.md b/README.md index 652b7fee9..b7ea0400e 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ You can now use the `--advanced` flag when running the `create` command to get a - [Tailwind](https://tailwindcss.com/) css framework - Docker configuration for go project - [React](https://react.dev/) frontend written in TypeScript, including an example fetch request to the backend +- Swagger UI and API documentation using [Swaggo](https://github.com/swaggo/swag) Note: Selecting Tailwind option will automatically select HTMX unless React is explicitly selected @@ -214,10 +215,16 @@ React: go-blueprint create --advanced --feature react ``` +Swagger: + +```bash +go-blueprint create --advanced --feature swagger +``` + Or all features at once: ```bash -go-blueprint create --name my-project --framework chi --driver mysql --advanced --feature htmx --feature githubaction --feature websocket --feature tailwind --feature docker --git commit --feature react +go-blueprint create --name my-project --framework chi --driver mysql --advanced --feature htmx --feature githubaction --feature websocket --feature tailwind --feature docker --feature swagger --git commit --feature react ```

diff --git a/cmd/flags/advancedFeatures.go b/cmd/flags/advancedFeatures.go index f97d629e3..890fd91ac 100644 --- a/cmd/flags/advancedFeatures.go +++ b/cmd/flags/advancedFeatures.go @@ -14,9 +14,10 @@ const ( Tailwind string = "tailwind" React string = "react" Docker string = "docker" + Swagger string = "swagger" ) -var AllowedAdvancedFeatures = []string{string(React), string(Htmx), string(GoProjectWorkflow), string(Websocket), string(Tailwind), string(Docker)} +var AllowedAdvancedFeatures = []string{string(React), string(Htmx), string(GoProjectWorkflow), string(Websocket), string(Tailwind), string(Docker), string(Swagger)} func (f AdvancedFeatures) String() string { return strings.Join(f, ",") diff --git a/cmd/program/program.go b/cmd/program/program.go index a615b5aa9..5bb886d2f 100644 --- a/cmd/program/program.go +++ b/cmd/program/program.go @@ -73,6 +73,8 @@ type Templater interface { HtmxTemplRoutes() []byte HtmxTemplImports() []byte WebsocketImports() []byte + SwaggerRoutes() []byte + SwaggerImports() []byte } type DBDriverTemplater interface { @@ -109,6 +111,11 @@ var ( godotenvPackage = []string{"github.com/joho/godotenv"} templPackage = []string{"github.com/a-h/templ"} + swagPackage = []string{"github.com/swaggo/swag/cmd/swag@latest"} + ginSwagger = []string{"github.com/swaggo/files", "github.com/swaggo/gin-swagger"} + echoSwagger = []string{"github.com/swaggo/echo-swagger"} + fiberSwagger = []string{"github.com/swaggo/fiber-swagger"} + httpSwagger = []string{"github.com/swaggo/http-swagger/v2"} ) const ( @@ -597,6 +604,12 @@ func (p *Project) CreateMainFile() error { p.CreateWebsocketImports(projectPath) } + if p.AdvancedOptions[string(flags.Swagger)] { + if err := p.CreateSwaggerTemplates(projectPath); err != nil { + return err + } + } + if p.AdvancedOptions[string(flags.Docker)] { Dockerfile, err := os.Create(filepath.Join(projectPath, "Dockerfile")) if err != nil { @@ -651,6 +664,12 @@ func (p *Project) CreateMainFile() error { return err } + if p.AdvancedOptions[string(flags.Swagger)] { + if err := p.GenerateSwaggerDocs(projectPath); err != nil { + return err + } + } + // Create gitignore gitignoreFile, err := os.Create(filepath.Join(projectPath, ".gitignore")) if err != nil { @@ -991,6 +1010,58 @@ func (p *Project) CreateWebsocketImports(appDir string) { p.AdvancedTemplates.TemplateImports = newImports } +func (p *Project) CreateSwaggerTemplates(appDir string) error { + if err := utils.GoGetPackage(appDir, p.swaggerPackages()); err != nil { + return fmt.Errorf("could not install swagger dependency: %w", err) + } + + routesPlaceHolder := string(p.FrameworkMap[p.ProjectType].templater.SwaggerRoutes()) + importsPlaceHolder := string(p.FrameworkMap[p.ProjectType].templater.SwaggerImports()) + + routeTmpl, err := template.New("swagger-routes").Parse(routesPlaceHolder) + if err != nil { + return err + } + importTmpl, err := template.New("swagger-imports").Parse(importsPlaceHolder) + if err != nil { + return err + } + + var routeBuffer bytes.Buffer + var importBuffer bytes.Buffer + if err := routeTmpl.Execute(&routeBuffer, p); err != nil { + return err + } + if err := importTmpl.Execute(&importBuffer, p); err != nil { + return err + } + + p.AdvancedTemplates.TemplateRoutes = strings.Join([]string{p.AdvancedTemplates.TemplateRoutes, routeBuffer.String()}, "\n") + p.AdvancedTemplates.TemplateImports = strings.Join([]string{p.AdvancedTemplates.TemplateImports, importBuffer.String()}, "\n") + return nil +} + +func (p *Project) GenerateSwaggerDocs(appDir string) error { + if err := utils.ExecuteCmd("go", []string{"run", swagPackage[0], "init", "-g", "cmd/api/main.go", "--parseInternal"}, appDir); err != nil { + return fmt.Errorf("could not generate swagger docs: %w", err) + } + + return nil +} + +func (p *Project) swaggerPackages() []string { + switch p.ProjectType { + case flags.Gin: + return ginSwagger + case flags.Echo: + return echoSwagger + case flags.Fiber: + return fiberSwagger + default: + return httpSwagger + } +} + func checkNpmInstalled() error { cmd := exec.Command("npm", "--version") if err := cmd.Run(); err != nil { diff --git a/cmd/steps/steps.go b/cmd/steps/steps.go index eacb874bc..3b076597d 100644 --- a/cmd/steps/steps.go +++ b/cmd/steps/steps.go @@ -126,6 +126,11 @@ func InitSteps(projectType flags.Framework, databaseType flags.Database) *Steps Title: "Docker", Desc: "Dockerfile and docker-compose generic configuration for go project", }, + { + Flag: "Swagger", + Title: "Swagger", + Desc: "Add Swagger UI and generated API documentation using Swaggo", + }, }, }, "git": { diff --git a/cmd/template/advanced/files/swagger/imports/echo.tmpl b/cmd/template/advanced/files/swagger/imports/echo.tmpl new file mode 100644 index 000000000..69108593d --- /dev/null +++ b/cmd/template/advanced/files/swagger/imports/echo.tmpl @@ -0,0 +1,2 @@ +echoSwagger "github.com/swaggo/echo-swagger" +_ "{{.ProjectName}}/docs" diff --git a/cmd/template/advanced/files/swagger/imports/fiber.tmpl b/cmd/template/advanced/files/swagger/imports/fiber.tmpl new file mode 100644 index 000000000..daee8996e --- /dev/null +++ b/cmd/template/advanced/files/swagger/imports/fiber.tmpl @@ -0,0 +1,2 @@ +fiberSwagger "github.com/swaggo/fiber-swagger" +_ "{{.ProjectName}}/docs" diff --git a/cmd/template/advanced/files/swagger/imports/gin.tmpl b/cmd/template/advanced/files/swagger/imports/gin.tmpl new file mode 100644 index 000000000..0c2df458f --- /dev/null +++ b/cmd/template/advanced/files/swagger/imports/gin.tmpl @@ -0,0 +1,3 @@ +swaggerFiles "github.com/swaggo/files" +ginSwagger "github.com/swaggo/gin-swagger" +_ "{{.ProjectName}}/docs" diff --git a/cmd/template/advanced/files/swagger/imports/standard_library.tmpl b/cmd/template/advanced/files/swagger/imports/standard_library.tmpl new file mode 100644 index 000000000..a0602eea6 --- /dev/null +++ b/cmd/template/advanced/files/swagger/imports/standard_library.tmpl @@ -0,0 +1,2 @@ +httpSwagger "github.com/swaggo/http-swagger/v2" +_ "{{.ProjectName}}/docs" diff --git a/cmd/template/advanced/files/swagger/routes/chi.tmpl b/cmd/template/advanced/files/swagger/routes/chi.tmpl new file mode 100644 index 000000000..e2d62f0b4 --- /dev/null +++ b/cmd/template/advanced/files/swagger/routes/chi.tmpl @@ -0,0 +1 @@ +r.Get("/swagger/*", httpSwagger.WrapHandler) diff --git a/cmd/template/advanced/files/swagger/routes/echo.tmpl b/cmd/template/advanced/files/swagger/routes/echo.tmpl new file mode 100644 index 000000000..cde6e500a --- /dev/null +++ b/cmd/template/advanced/files/swagger/routes/echo.tmpl @@ -0,0 +1 @@ +e.GET("/swagger/*", echoSwagger.WrapHandler) diff --git a/cmd/template/advanced/files/swagger/routes/fiber.tmpl b/cmd/template/advanced/files/swagger/routes/fiber.tmpl new file mode 100644 index 000000000..a7d6279cf --- /dev/null +++ b/cmd/template/advanced/files/swagger/routes/fiber.tmpl @@ -0,0 +1 @@ +s.App.Get("/swagger/*", fiberSwagger.WrapHandler) diff --git a/cmd/template/advanced/files/swagger/routes/gin.tmpl b/cmd/template/advanced/files/swagger/routes/gin.tmpl new file mode 100644 index 000000000..258c5ecc3 --- /dev/null +++ b/cmd/template/advanced/files/swagger/routes/gin.tmpl @@ -0,0 +1 @@ +r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) diff --git a/cmd/template/advanced/files/swagger/routes/gorilla.tmpl b/cmd/template/advanced/files/swagger/routes/gorilla.tmpl new file mode 100644 index 000000000..ff9c9a721 --- /dev/null +++ b/cmd/template/advanced/files/swagger/routes/gorilla.tmpl @@ -0,0 +1 @@ +r.PathPrefix("/swagger/").Handler(httpSwagger.WrapHandler) diff --git a/cmd/template/advanced/files/swagger/routes/http_router.tmpl b/cmd/template/advanced/files/swagger/routes/http_router.tmpl new file mode 100644 index 000000000..e79ef002f --- /dev/null +++ b/cmd/template/advanced/files/swagger/routes/http_router.tmpl @@ -0,0 +1 @@ +r.HandlerFunc(http.MethodGet, "/swagger/*filepath", httpSwagger.WrapHandler) diff --git a/cmd/template/advanced/files/swagger/routes/standard_library.tmpl b/cmd/template/advanced/files/swagger/routes/standard_library.tmpl new file mode 100644 index 000000000..c45a9beb7 --- /dev/null +++ b/cmd/template/advanced/files/swagger/routes/standard_library.tmpl @@ -0,0 +1 @@ +mux.HandleFunc("/swagger/", httpSwagger.WrapHandler) diff --git a/cmd/template/advanced/routes.go b/cmd/template/advanced/routes.go index a671b5e7f..29480bbdf 100644 --- a/cmd/template/advanced/routes.go +++ b/cmd/template/advanced/routes.go @@ -79,6 +79,39 @@ var fiberHtmxTemplImports []byte //go:embed files/websocket/imports/fiber.tmpl var fiberWebsocketTemplImports []byte +//go:embed files/swagger/imports/standard_library.tmpl +var stdLibSwaggerImports []byte + +//go:embed files/swagger/imports/gin.tmpl +var ginSwaggerImports []byte + +//go:embed files/swagger/imports/echo.tmpl +var echoSwaggerImports []byte + +//go:embed files/swagger/imports/fiber.tmpl +var fiberSwaggerImports []byte + +//go:embed files/swagger/routes/standard_library.tmpl +var stdLibSwaggerRoutes []byte + +//go:embed files/swagger/routes/chi.tmpl +var chiSwaggerRoutes []byte + +//go:embed files/swagger/routes/gorilla.tmpl +var gorillaSwaggerRoutes []byte + +//go:embed files/swagger/routes/http_router.tmpl +var httpRouterSwaggerRoutes []byte + +//go:embed files/swagger/routes/gin.tmpl +var ginSwaggerRoutes []byte + +//go:embed files/swagger/routes/echo.tmpl +var echoSwaggerRoutes []byte + +//go:embed files/swagger/routes/fiber.tmpl +var fiberSwaggerRoutes []byte + func EchoHtmxTemplRoutesTemplate() []byte { return echoHtmxTemplRoutes } @@ -178,3 +211,47 @@ func FiberWebsocketTemplImportsTemplate() []byte { func GinHtmxTemplImportsTemplate() []byte { return ginHtmxTemplImports } + +func StdLibSwaggerImportsTemplate() []byte { + return stdLibSwaggerImports +} + +func GinSwaggerImportsTemplate() []byte { + return ginSwaggerImports +} + +func EchoSwaggerImportsTemplate() []byte { + return echoSwaggerImports +} + +func FiberSwaggerImportsTemplate() []byte { + return fiberSwaggerImports +} + +func StdLibSwaggerRoutesTemplate() []byte { + return stdLibSwaggerRoutes +} + +func ChiSwaggerRoutesTemplate() []byte { + return chiSwaggerRoutes +} + +func GorillaSwaggerRoutesTemplate() []byte { + return gorillaSwaggerRoutes +} + +func HttpRouterSwaggerRoutesTemplate() []byte { + return httpRouterSwaggerRoutes +} + +func GinSwaggerRoutesTemplate() []byte { + return ginSwaggerRoutes +} + +func EchoSwaggerRoutesTemplate() []byte { + return echoSwaggerRoutes +} + +func FiberSwaggerRoutesTemplate() []byte { + return fiberSwaggerRoutes +} diff --git a/cmd/template/framework/chiRoutes.go b/cmd/template/framework/chiRoutes.go index 00fbda874..7f174faf2 100644 --- a/cmd/template/framework/chiRoutes.go +++ b/cmd/template/framework/chiRoutes.go @@ -43,3 +43,11 @@ func (c ChiTemplates) HtmxTemplRoutes() []byte { func (c ChiTemplates) WebsocketImports() []byte { return advanced.StdLibWebsocketTemplImportsTemplate() } + +func (c ChiTemplates) SwaggerRoutes() []byte { + return advanced.ChiSwaggerRoutesTemplate() +} + +func (c ChiTemplates) SwaggerImports() []byte { + return advanced.StdLibSwaggerImportsTemplate() +} diff --git a/cmd/template/framework/echoRoutes.go b/cmd/template/framework/echoRoutes.go index cb25939d9..6667dae67 100644 --- a/cmd/template/framework/echoRoutes.go +++ b/cmd/template/framework/echoRoutes.go @@ -42,3 +42,11 @@ func (e EchoTemplates) HtmxTemplRoutes() []byte { func (e EchoTemplates) WebsocketImports() []byte { return advanced.StdLibWebsocketTemplImportsTemplate() } + +func (e EchoTemplates) SwaggerRoutes() []byte { + return advanced.EchoSwaggerRoutesTemplate() +} + +func (e EchoTemplates) SwaggerImports() []byte { + return advanced.EchoSwaggerImportsTemplate() +} diff --git a/cmd/template/framework/fiberServer.go b/cmd/template/framework/fiberServer.go index 961e4d919..39798b2e5 100644 --- a/cmd/template/framework/fiberServer.go +++ b/cmd/template/framework/fiberServer.go @@ -48,3 +48,11 @@ func (f FiberTemplates) HtmxTemplRoutes() []byte { func (f FiberTemplates) WebsocketImports() []byte { return advanced.FiberWebsocketTemplImportsTemplate() } + +func (f FiberTemplates) SwaggerRoutes() []byte { + return advanced.FiberSwaggerRoutesTemplate() +} + +func (f FiberTemplates) SwaggerImports() []byte { + return advanced.FiberSwaggerImportsTemplate() +} diff --git a/cmd/template/framework/files/README.md.tmpl b/cmd/template/framework/files/README.md.tmpl index 8e554cedc..893951165 100644 --- a/cmd/template/framework/files/README.md.tmpl +++ b/cmd/template/framework/files/README.md.tmpl @@ -50,6 +50,18 @@ Run the test suite: make test ``` +{{- if .AdvancedOptions.swagger }} +Generate Swagger documentation: +```bash +make swagger +``` + +Swagger UI is available at: +```bash +http://localhost:8080/swagger/index.html +``` +{{- end }} + Clean up binary from the last build: ```bash make clean diff --git a/cmd/template/framework/files/main/fiber_main.go.tmpl b/cmd/template/framework/files/main/fiber_main.go.tmpl index 52d253e49..cf26fb995 100644 --- a/cmd/template/framework/files/main/fiber_main.go.tmpl +++ b/cmd/template/framework/files/main/fiber_main.go.tmpl @@ -39,6 +39,11 @@ func gracefulShutdown(fiberServer *server.FiberServer, done chan bool) { done <- true } +// @title {{.ProjectName}} API +// @version 1.0 +// @description API generated by Go Blueprint. +// @host localhost:8080 +// @BasePath / func main() { server := server.New() diff --git a/cmd/template/framework/files/main/main.go.tmpl b/cmd/template/framework/files/main/main.go.tmpl index dbe97e31f..3b4879251 100644 --- a/cmd/template/framework/files/main/main.go.tmpl +++ b/cmd/template/framework/files/main/main.go.tmpl @@ -37,6 +37,11 @@ func gracefulShutdown(apiServer *http.Server, done chan bool) { done <- true } +// @title {{.ProjectName}} API +// @version 1.0 +// @description API generated by Go Blueprint. +// @host localhost:8080 +// @BasePath / func main() { server := server.NewServer() diff --git a/cmd/template/framework/files/makefile.tmpl b/cmd/template/framework/files/makefile.tmpl index 7e10b2548..8aa6374c5 100644 --- a/cmd/template/framework/files/makefile.tmpl +++ b/cmd/template/framework/files/makefile.tmpl @@ -103,6 +103,12 @@ itest: @go test ./internal/database -v {{- end }} +{{- if .AdvancedOptions.swagger }} +# Generate Swagger documentation +swagger: + @go run github.com/swaggo/swag/cmd/swag@latest init -g cmd/api/main.go --parseInternal +{{- end }} + # Clean the binary clean: @echo "Cleaning..." @@ -138,4 +144,4 @@ watch: }" {{- end }} -.PHONY: all build run test clean watch{{- if and (not .AdvancedOptions.react) .AdvancedOptions.tailwind }} tailwind-install{{- end }}{{- if and (ne .DBDriver "none") (ne .DBDriver "sqlite") }} docker-run docker-down itest{{- end }}{{- if and (or .AdvancedOptions.htmx .AdvancedOptions.tailwind) (not .AdvancedOptions.react) }} templ-install{{- end }} +.PHONY: all build run test clean watch{{- if .AdvancedOptions.swagger }} swagger{{- end }}{{- if and (not .AdvancedOptions.react) .AdvancedOptions.tailwind }} tailwind-install{{- end }}{{- if and (ne .DBDriver "none") (ne .DBDriver "sqlite") }} docker-run docker-down itest{{- end }}{{- if and (or .AdvancedOptions.htmx .AdvancedOptions.tailwind) (not .AdvancedOptions.react) }} templ-install{{- end }} diff --git a/cmd/template/framework/files/routes/chi.go.tmpl b/cmd/template/framework/files/routes/chi.go.tmpl index 73e46def7..8e2c7ea34 100644 --- a/cmd/template/framework/files/routes/chi.go.tmpl +++ b/cmd/template/framework/files/routes/chi.go.tmpl @@ -40,6 +40,13 @@ func (s *Server) RegisterRoutes() http.Handler { return r } +// HelloWorldHandler returns a hello world response. +// @Summary Hello world +// @Description Returns a hello world message. +// @Tags server +// @Produce json +// @Success 200 {object} map[string]string +// @Router / [get] func (s *Server) HelloWorldHandler(w http.ResponseWriter, r *http.Request) { resp := make(map[string]string) resp["message"] = "Hello World" @@ -53,6 +60,13 @@ func (s *Server) HelloWorldHandler(w http.ResponseWriter, r *http.Request) { } {{if ne .DBDriver "none"}} +// healthHandler returns database health information. +// @Summary Database health +// @Description Returns database connection health and pool statistics. +// @Tags health +// @Produce json +// @Success 200 {object} map[string]string +// @Router /health [get] func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) { jsonResp, _ := json.Marshal(s.db.Health()) _, _ = w.Write(jsonResp) @@ -85,4 +99,3 @@ func (s *Server) websocketHandler(w http.ResponseWriter, r *http.Request) { } } {{end}} - diff --git a/cmd/template/framework/files/routes/echo.go.tmpl b/cmd/template/framework/files/routes/echo.go.tmpl index eed481a97..f3a1277b7 100644 --- a/cmd/template/framework/files/routes/echo.go.tmpl +++ b/cmd/template/framework/files/routes/echo.go.tmpl @@ -38,6 +38,13 @@ func (s *Server) RegisterRoutes() http.Handler { return e } +// HelloWorldHandler returns a hello world response. +// @Summary Hello world +// @Description Returns a hello world message. +// @Tags server +// @Produce json +// @Success 200 {object} map[string]string +// @Router / [get] func (s *Server) HelloWorldHandler(c echo.Context) error { resp := map[string]string{ "message": "Hello World", @@ -47,6 +54,13 @@ func (s *Server) HelloWorldHandler(c echo.Context) error { } {{if ne .DBDriver "none"}} +// healthHandler returns database health information. +// @Summary Database health +// @Description Returns database connection health and pool statistics. +// @Tags health +// @Produce json +// @Success 200 {object} map[string]string +// @Router /health [get] func (s *Server) healthHandler(c echo.Context) error { return c.JSON(http.StatusOK, s.db.Health()) } @@ -81,4 +95,3 @@ func (s *Server) websocketHandler(c echo.Context) error { return nil } {{end}} - diff --git a/cmd/template/framework/files/routes/fiber.go.tmpl b/cmd/template/framework/files/routes/fiber.go.tmpl index 950d3be1d..8399c2cca 100644 --- a/cmd/template/framework/files/routes/fiber.go.tmpl +++ b/cmd/template/framework/files/routes/fiber.go.tmpl @@ -33,6 +33,13 @@ func (s *FiberServer) RegisterFiberRoutes() { {{.AdvancedTemplates.TemplateRoutes}} } +// HelloWorldHandler returns a hello world response. +// @Summary Hello world +// @Description Returns a hello world message. +// @Tags server +// @Produce json +// @Success 200 {object} map[string]string +// @Router / [get] func (s *FiberServer) HelloWorldHandler(c *fiber.Ctx) error { resp := fiber.Map{ "message": "Hello World", @@ -42,6 +49,13 @@ func (s *FiberServer) HelloWorldHandler(c *fiber.Ctx) error { } {{if ne .DBDriver "none"}} +// healthHandler returns database health information. +// @Summary Database health +// @Description Returns database connection health and pool statistics. +// @Tags health +// @Produce json +// @Success 200 {object} map[string]string +// @Router /health [get] func (s *FiberServer) healthHandler(c *fiber.Ctx) error { return c.JSON(s.db.Health()) } @@ -77,4 +91,3 @@ func (s *FiberServer) websocketHandler(con *websocket.Conn) { } } {{end}} - diff --git a/cmd/template/framework/files/routes/gin.go.tmpl b/cmd/template/framework/files/routes/gin.go.tmpl index 774c844cf..f3bafb061 100644 --- a/cmd/template/framework/files/routes/gin.go.tmpl +++ b/cmd/template/framework/files/routes/gin.go.tmpl @@ -37,6 +37,13 @@ func (s *Server) RegisterRoutes() http.Handler { return r } +// HelloWorldHandler returns a hello world response. +// @Summary Hello world +// @Description Returns a hello world message. +// @Tags server +// @Produce json +// @Success 200 {object} map[string]string +// @Router / [get] func (s *Server) HelloWorldHandler(c *gin.Context) { resp := make(map[string]string) resp["message"] = "Hello World" @@ -45,6 +52,13 @@ func (s *Server) HelloWorldHandler(c *gin.Context) { } {{if ne .DBDriver "none"}} +// healthHandler returns database health information. +// @Summary Database health +// @Description Returns database connection health and pool statistics. +// @Tags health +// @Produce json +// @Success 200 {object} map[string]string +// @Router /health [get] func (s *Server) healthHandler(c *gin.Context) { c.JSON(http.StatusOK, s.db.Health()) } @@ -78,4 +92,3 @@ func (s *Server) websocketHandler(c *gin.Context) { } } {{end}} - diff --git a/cmd/template/framework/files/routes/gorilla.go.tmpl b/cmd/template/framework/files/routes/gorilla.go.tmpl index 90dc5aeb6..434fdbc22 100644 --- a/cmd/template/framework/files/routes/gorilla.go.tmpl +++ b/cmd/template/framework/files/routes/gorilla.go.tmpl @@ -51,6 +51,13 @@ func (s *Server) corsMiddleware(next http.Handler) http.Handler { }) } +// HelloWorldHandler returns a hello world response. +// @Summary Hello world +// @Description Returns a hello world message. +// @Tags server +// @Produce json +// @Success 200 {object} map[string]string +// @Router / [get] func (s *Server) HelloWorldHandler(w http.ResponseWriter, r *http.Request) { resp := make(map[string]string) resp["message"] = "Hello World" @@ -64,6 +71,13 @@ func (s *Server) HelloWorldHandler(w http.ResponseWriter, r *http.Request) { } {{if ne .DBDriver "none"}} +// healthHandler returns database health information. +// @Summary Database health +// @Description Returns database connection health and pool statistics. +// @Tags health +// @Produce json +// @Success 200 {object} map[string]string +// @Router /health [get] func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) { jsonResp, err := json.Marshal(s.db.Health()) @@ -101,4 +115,3 @@ func (s *Server) websocketHandler(w http.ResponseWriter, r *http.Request) { } } {{end}} - diff --git a/cmd/template/framework/files/routes/http_router.go.tmpl b/cmd/template/framework/files/routes/http_router.go.tmpl index 5a494d9eb..5c3e2bd37 100644 --- a/cmd/template/framework/files/routes/http_router.go.tmpl +++ b/cmd/template/framework/files/routes/http_router.go.tmpl @@ -50,6 +50,13 @@ func (s *Server) corsMiddleware(next http.Handler) http.Handler { }) } +// HelloWorldHandler returns a hello world response. +// @Summary Hello world +// @Description Returns a hello world message. +// @Tags server +// @Produce json +// @Success 200 {object} map[string]string +// @Router / [get] func (s *Server) HelloWorldHandler(w http.ResponseWriter, r *http.Request) { resp := make(map[string]string) resp["message"] = "Hello World" @@ -63,6 +70,13 @@ func (s *Server) HelloWorldHandler(w http.ResponseWriter, r *http.Request) { } {{if ne .DBDriver "none"}} +// healthHandler returns database health information. +// @Summary Database health +// @Description Returns database connection health and pool statistics. +// @Tags health +// @Produce json +// @Success 200 {object} map[string]string +// @Router /health [get] func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) { jsonResp, err := json.Marshal(s.db.Health()) @@ -100,4 +114,3 @@ func (s *Server) websocketHandler(w http.ResponseWriter, r *http.Request) { } } {{end}} - diff --git a/cmd/template/framework/files/routes/standard_library.go.tmpl b/cmd/template/framework/files/routes/standard_library.go.tmpl index 34564f0cf..82c939455 100644 --- a/cmd/template/framework/files/routes/standard_library.go.tmpl +++ b/cmd/template/framework/files/routes/standard_library.go.tmpl @@ -48,6 +48,13 @@ func (s *Server) corsMiddleware(next http.Handler) http.Handler { }) } +// HelloWorldHandler returns a hello world response. +// @Summary Hello world +// @Description Returns a hello world message. +// @Tags server +// @Produce json +// @Success 200 {object} map[string]string +// @Router / [get] func (s *Server) HelloWorldHandler(w http.ResponseWriter, r *http.Request) { resp := map[string]string{"message": "Hello World"} jsonResp, err := json.Marshal(resp) @@ -62,6 +69,13 @@ func (s *Server) HelloWorldHandler(w http.ResponseWriter, r *http.Request) { } {{if ne .DBDriver "none"}} +// healthHandler returns database health information. +// @Summary Database health +// @Description Returns database connection health and pool statistics. +// @Tags health +// @Produce json +// @Success 200 {object} map[string]string +// @Router /health [get] func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) { resp, err := json.Marshal(s.db.Health()) if err != nil { @@ -97,4 +111,3 @@ func (s *Server) websocketHandler(w http.ResponseWriter, r *http.Request) { } } {{end}} - diff --git a/cmd/template/framework/ginRoutes.go b/cmd/template/framework/ginRoutes.go index 1a952d5ea..096ce0cf7 100644 --- a/cmd/template/framework/ginRoutes.go +++ b/cmd/template/framework/ginRoutes.go @@ -43,3 +43,11 @@ func (g GinTemplates) HtmxTemplRoutes() []byte { func (g GinTemplates) WebsocketImports() []byte { return advanced.StdLibWebsocketTemplImportsTemplate() } + +func (g GinTemplates) SwaggerRoutes() []byte { + return advanced.GinSwaggerRoutesTemplate() +} + +func (g GinTemplates) SwaggerImports() []byte { + return advanced.GinSwaggerImportsTemplate() +} diff --git a/cmd/template/framework/gorillaRoutes.go b/cmd/template/framework/gorillaRoutes.go index aa2711ffe..8d83d7517 100644 --- a/cmd/template/framework/gorillaRoutes.go +++ b/cmd/template/framework/gorillaRoutes.go @@ -43,3 +43,11 @@ func (g GorillaTemplates) HtmxTemplRoutes() []byte { func (g GorillaTemplates) WebsocketImports() []byte { return advanced.StdLibWebsocketTemplImportsTemplate() } + +func (g GorillaTemplates) SwaggerRoutes() []byte { + return advanced.GorillaSwaggerRoutesTemplate() +} + +func (g GorillaTemplates) SwaggerImports() []byte { + return advanced.StdLibSwaggerImportsTemplate() +} diff --git a/cmd/template/framework/httpRoutes.go b/cmd/template/framework/httpRoutes.go index b0170a822..34957130e 100644 --- a/cmd/template/framework/httpRoutes.go +++ b/cmd/template/framework/httpRoutes.go @@ -46,3 +46,11 @@ func (s StandardLibTemplate) HtmxTemplRoutes() []byte { func (s StandardLibTemplate) WebsocketImports() []byte { return advanced.StdLibWebsocketTemplImportsTemplate() } + +func (s StandardLibTemplate) SwaggerRoutes() []byte { + return advanced.StdLibSwaggerRoutesTemplate() +} + +func (s StandardLibTemplate) SwaggerImports() []byte { + return advanced.StdLibSwaggerImportsTemplate() +} diff --git a/cmd/template/framework/routerRoutes.go b/cmd/template/framework/routerRoutes.go index f9e921332..ad734d4cb 100644 --- a/cmd/template/framework/routerRoutes.go +++ b/cmd/template/framework/routerRoutes.go @@ -42,3 +42,11 @@ func (r RouterTemplates) HtmxTemplRoutes() []byte { func (r RouterTemplates) WebsocketImports() []byte { return advanced.StdLibWebsocketTemplImportsTemplate() } + +func (r RouterTemplates) SwaggerRoutes() []byte { + return advanced.HttpRouterSwaggerRoutesTemplate() +} + +func (r RouterTemplates) SwaggerImports() []byte { + return advanced.StdLibSwaggerImportsTemplate() +} diff --git a/docs/docs/advanced-flag/advanced-flag.md b/docs/docs/advanced-flag/advanced-flag.md index 124ef6b91..d2a2e0ea6 100644 --- a/docs/docs/advanced-flag/advanced-flag.md +++ b/docs/docs/advanced-flag/advanced-flag.md @@ -20,6 +20,9 @@ Docker configuration for go project. - **React:** Frontend written in TypeScript, including an example fetch request to the backend. +- **Swagger:** +Adds Swagger UI and API documentation using Swaggo. + To utilize the `--advanced` flag, use the following command: @@ -37,5 +40,5 @@ go-blueprint create --name my-project --framework chi --driver mysql --advanced Non-Interactive Setup is also possible: ```bash -go-blueprint create --name my-project --framework chi --driver mysql --advanced --feature htmx --feature githubaction --feature websocket --feature tailwind +go-blueprint create --name my-project --framework chi --driver mysql --advanced --feature htmx --feature githubaction --feature websocket --feature tailwind --feature swagger ``` diff --git a/docs/docs/advanced-flag/swagger.md b/docs/docs/advanced-flag/swagger.md new file mode 100644 index 000000000..dcebc9440 --- /dev/null +++ b/docs/docs/advanced-flag/swagger.md @@ -0,0 +1,19 @@ +# Swagger + +The Swagger advanced feature adds Swaggo-generated API documentation and serves Swagger UI from the generated application. + +```bash +go-blueprint create --advanced --feature swagger +``` + +After starting the generated API, open: + +```bash +http://localhost:8080/swagger/index.html +``` + +To regenerate the docs after changing route comments: + +```bash +make swagger +``` diff --git a/docs/docs/creating-project/project-init.md b/docs/docs/creating-project/project-init.md index 6ad0b23c6..41e2eeead 100644 --- a/docs/docs/creating-project/project-init.md +++ b/docs/docs/creating-project/project-init.md @@ -29,7 +29,7 @@ Customize the flags according to your project requirements. ## Advanced Flag -By including the `--advanced` flag, users can choose one or all of the advanced features, HTMX, GitHub Actions for CI/CD, Websocket, Docker and TailwindCSS support, during the project creation process. The flag enhances the simplicity of Blueprint while offering flexibility for users who require additional functionality. +By including the `--advanced` flag, users can choose one or all of the advanced features, HTMX, GitHub Actions for CI/CD, Websocket, Docker, Swagger and TailwindCSS support, during the project creation process. The flag enhances the simplicity of Blueprint while offering flexibility for users who require additional functionality. ```bash go-blueprint create --advanced @@ -70,7 +70,12 @@ Docker: go-blueprint create --advanced --feature docker ``` +Swagger: +```bash +go-blueprint create --advanced --feature swagger +``` + Or all features at once: ```bash -go-blueprint create --name my-project --framework chi --driver mysql --git commit --advanced --feature htmx --feature githubaction --feature websocket --feature tailwind --feature docker +go-blueprint create --name my-project --framework chi --driver mysql --git commit --advanced --feature htmx --feature githubaction --feature websocket --feature tailwind --feature docker --feature swagger ``` diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index b9ece5be4..d22220e39 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -46,6 +46,7 @@ nav: - GoReleaser & GoTest CI: advanced-flag/goreleaser.md - Websocket: advanced-flag/websocket.md - Docker: advanced-flag/docker.md + - Swagger: advanced-flag/swagger.md - React & Vite (TypeScript): advanced-flag/react-vite.md - Testing endpoints: - Server: endpoints-test/server.md