Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
```

<p align="center">
Expand Down
3 changes: 2 additions & 1 deletion cmd/flags/advancedFeatures.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, ",")
Expand Down
71 changes: 71 additions & 0 deletions cmd/program/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ type Templater interface {
HtmxTemplRoutes() []byte
HtmxTemplImports() []byte
WebsocketImports() []byte
SwaggerRoutes() []byte
SwaggerImports() []byte
}

type DBDriverTemplater interface {
Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
5 changes: 5 additions & 0 deletions cmd/steps/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
2 changes: 2 additions & 0 deletions cmd/template/advanced/files/swagger/imports/echo.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
echoSwagger "github.com/swaggo/echo-swagger"
_ "{{.ProjectName}}/docs"
2 changes: 2 additions & 0 deletions cmd/template/advanced/files/swagger/imports/fiber.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fiberSwagger "github.com/swaggo/fiber-swagger"
_ "{{.ProjectName}}/docs"
3 changes: 3 additions & 0 deletions cmd/template/advanced/files/swagger/imports/gin.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
_ "{{.ProjectName}}/docs"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
httpSwagger "github.com/swaggo/http-swagger/v2"
_ "{{.ProjectName}}/docs"
1 change: 1 addition & 0 deletions cmd/template/advanced/files/swagger/routes/chi.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
r.Get("/swagger/*", httpSwagger.WrapHandler)
1 change: 1 addition & 0 deletions cmd/template/advanced/files/swagger/routes/echo.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
e.GET("/swagger/*", echoSwagger.WrapHandler)
1 change: 1 addition & 0 deletions cmd/template/advanced/files/swagger/routes/fiber.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
s.App.Get("/swagger/*", fiberSwagger.WrapHandler)
1 change: 1 addition & 0 deletions cmd/template/advanced/files/swagger/routes/gin.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
1 change: 1 addition & 0 deletions cmd/template/advanced/files/swagger/routes/gorilla.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
r.PathPrefix("/swagger/").Handler(httpSwagger.WrapHandler)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
r.HandlerFunc(http.MethodGet, "/swagger/*filepath", httpSwagger.WrapHandler)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mux.HandleFunc("/swagger/", httpSwagger.WrapHandler)
77 changes: 77 additions & 0 deletions cmd/template/advanced/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
8 changes: 8 additions & 0 deletions cmd/template/framework/chiRoutes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
8 changes: 8 additions & 0 deletions cmd/template/framework/echoRoutes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
8 changes: 8 additions & 0 deletions cmd/template/framework/fiberServer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
12 changes: 12 additions & 0 deletions cmd/template/framework/files/README.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions cmd/template/framework/files/main/fiber_main.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
5 changes: 5 additions & 0 deletions cmd/template/framework/files/main/main.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
8 changes: 7 additions & 1 deletion cmd/template/framework/files/makefile.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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..."
Expand Down Expand Up @@ -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 }}
Loading
Loading