Files
antrean-anjungan/tools/generate-handler.go
2025-08-14 09:08:34 +07:00

365 lines
10 KiB
Go

package main
import (
"fmt"
"os"
"strings"
"text/template"
"time"
)
// HandlerData contains template data for handler generation
type HandlerData struct {
Name string
NameLower string
NamePlural string
ModuleName string
HasGet bool
HasPost bool
HasPut bool
HasDelete bool
HasParam bool
HasRequest bool
HasResponse bool
Timestamp string
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: go run generate-handler.go <handler-name> [methods]")
fmt.Println("Example: go run generate-handler.go user get post put delete")
fmt.Println("Methods: get, post, put, delete (optional, default: get post)")
os.Exit(1)
}
handlerName := strings.Title(os.Args[1])
methods := []string{"get", "post"}
if len(os.Args) > 2 {
methods = os.Args[2:]
}
// Convert to lowercase for file names
handlerLower := strings.ToLower(handlerName)
handlerPlural := handlerLower + "s"
data := HandlerData{
Name: handlerName,
NameLower: handlerLower,
NamePlural: handlerPlural,
ModuleName: "api-service",
Timestamp: time.Now().Format("2006-01-02 15:04:05"),
}
// Check which methods are requested
for _, method := range methods {
switch strings.ToLower(method) {
case "get":
data.HasGet = true
case "post":
data.HasPost = true
case "put":
data.HasPut = true
case "delete":
data.HasDelete = true
}
}
// Check if we need request/response models
data.HasRequest = data.HasPost || data.HasPut
data.HasResponse = true
data.HasParam = data.HasGet || data.HasPut || data.HasDelete
fmt.Printf("Generating handler: %s with methods: %v\n", handlerName, methods)
// Create directories if they don't exist
os.MkdirAll("internal/handlers", 0755)
os.MkdirAll("internal/models", 0755)
// Generate files
generateHandlerFile(data)
generateModelFile(data)
updateRoutesFile(data)
fmt.Printf("Successfully generated handler: %s\n", handlerName)
fmt.Println("Don't forget to run: swag init -g cmd/api/main.go")
}
func generateHandlerFile(data HandlerData) {
handlerTemplate := `package handlers
import (
"net/http"
"strings"
"{{.ModuleName}}/internal/models"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
// {{.Name}}Handler handles {{.NameLower}} services
type {{.Name}}Handler struct{}
// New{{.Name}}Handler creates a new {{.Name}}Handler
func New{{.Name}}Handler() *{{.Name}}Handler {
return &{{.Name}}Handler{}
}
{{if .HasGet}}
// Get{{.Name}} godoc
// @Summary Get {{.NameLower}}
// @Description Returns a list of {{.NamePlural}}
// @Tags {{.NameLower}}
// @Accept json
// @Produce json
// @Success 200 {object} models.{{.Name}}GetResponse "{{.Name}} GET response"
// @Router /api/v1/{{.NamePlural}} [get]
func (h *{{.Name}}Handler) Get{{.Name}}(c *gin.Context) {
response := models.{{.Name}}GetResponse{
Message: "List of {{.NamePlural}}",
Data: []string{"{{.Name}} 1", "{{.Name}} 2"},
}
c.JSON(http.StatusOK, response)
}
{{end}}
{{if .HasGet}}
// Get{{.Name}}ByID godoc
// @Summary Get {{.NameLower}} by ID
// @Description Returns a single {{.NameLower}} by ID
// @Tags {{.NameLower}}
// @Accept json
// @Produce json
// @Param id path string true "{{.Name}} ID"
// @Success 200 {object} models.{{.Name}}GetByIDResponse "{{.Name}} GET by ID response"
// @Failure 404 {object} models.ErrorResponse "{{.Name}} not found"
// @Router /api/v1/{{.NamePlural}}/{id} [get]
func (h *{{.Name}}Handler) Get{{.Name}}ByID(c *gin.Context) {
id := c.Param("id")
response := models.{{.Name}}GetByIDResponse{
ID: id,
Message: "{{.Name}} details",
}
c.JSON(http.StatusOK, response)
}
{{end}}
{{if .HasPost}}
// Create{{.Name}} godoc
// @Summary Create {{.NameLower}}
// @Description Creates a new {{.NameLower}}
// @Tags {{.NameLower}}
// @Accept json
// @Produce json
// @Param request body models.{{.Name}}CreateRequest true "{{.Name}} creation request"
// @Success 201 {object} models.{{.Name}}CreateResponse "{{.Name}} created successfully"
// @Failure 400 {object} models.ErrorResponse "Bad request"
// @Router /api/v1/{{.NamePlural}} [post]
func (h *{{.Name}}Handler) Create{{.Name}}(c *gin.Context) {
var req models.{{.Name}}CreateRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
response := models.{{.Name}}CreateResponse{
ID: uuid.NewString(),
Message: "{{.Name}} created successfully",
Data: req,
}
c.JSON(http.StatusCreated, response)
}
{{end}}
{{if .HasPut}}
// Update{{.Name}} godoc
// @Summary Update {{.NameLower}}
// @Description Updates an existing {{.NameLower}}
// @Tags {{.NameLower}}
// @Accept json
// @Produce json
// @Param id path string true "{{.Name}} ID"
// @Param request body models.{{.Name}}UpdateRequest true "{{.Name}} update request"
// @Success 200 {object} models.{{.Name}}UpdateResponse "{{.Name}} updated successfully"
// @Failure 400 {object} models.ErrorResponse "Bad request"
// @Failure 404 {object} models.ErrorResponse "{{.Name}} not found"
// @Router /api/v1/{{.NamePlural}}/{id} [put]
func (h *{{.Name}}Handler) Update{{.Name}}(c *gin.Context) {
id := c.Param("id")
var req models.{{.Name}}UpdateRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
response := models.{{.Name}}UpdateResponse{
ID: id,
Message: "{{.Name}} updated successfully",
Data: req,
}
c.JSON(http.StatusOK, response)
}
{{end}}
{{if .HasDelete}}
// Delete{{.Name}} godoc
// @Summary Delete {{.NameLower}}
// @Description Deletes a {{.NameLower}} by ID
// @Tags {{.NameLower}}
// @Accept json
// @Produce json
// @Param id path string true "{{.Name}} ID"
// @Success 200 {object} models.{{.Name}}DeleteResponse "{{.Name}} deleted successfully"
// @Failure 404 {object} models.ErrorResponse "{{.Name}} not found"
// @Router /api/v1/{{.NamePlural}}/{id} [delete]
func (h *{{.Name}}Handler) Delete{{.Name}}(c *gin.Context) {
id := c.Param("id")
response := models.{{.Name}}DeleteResponse{
ID: id,
Message: "{{.Name}} deleted successfully",
}
c.JSON(http.StatusOK, response)
}
{{end}}
`
writeFile("internal/handlers/"+data.NameLower+".go", handlerTemplate, data)
}
func generateModelFile(data HandlerData) {
modelTemplate := `package models
{{if .HasGet}}
// {{.Name}}GetResponse represents the response for GET {{.NamePlural}}
type {{.Name}}GetResponse struct {
Message string {{.Backtick}}json:"message"{{.Backtick}}
Data interface{} {{.Backtick}}json:"data"{{.Backtick}}
}
{{end}}
{{if .HasGet}}
// {{.Name}}GetByIDResponse represents the response for GET {{.NameLower}} by ID
type {{.Name}}GetByIDResponse struct {
ID string {{.Backtick}}json:"id"{{.Backtick}}
Message string {{.Backtick}}json:"message"{{.Backtick}}
}
{{end}}
{{if .HasPost}}
// {{.Name}}CreateRequest represents the request for creating {{.NameLower}}
type {{.Name}}CreateRequest struct {
Name string {{.Backtick}}json:"name" binding:"required"{{.Backtick}}
// Add more fields as needed
}
// {{.Name}}CreateResponse represents the response for creating {{.NameLower}}
type {{.Name}}CreateResponse struct {
ID string {{.Backtick}}json:"id"{{.Backtick}}
Message string {{.Backtick}}json:"message"{{.Backtick}}
Data interface{} {{.Backtick}}json:"data"{{.Backtick}}
}
{{end}}
{{if .HasPut}}
// {{.Name}}UpdateRequest represents the request for updating {{.NameLower}}
type {{.Name}}UpdateRequest struct {
Name string {{.Backtick}}json:"name" binding:"required"{{.Backtick}}
// Add more fields as needed
}
// {{.Name}}UpdateResponse represents the response for updating {{.NameLower}}
type {{.Name}}UpdateResponse struct {
ID string {{.Backtick}}json:"id"{{.Backtick}}
Message string {{.Backtick}}json:"message"{{.Backtick}}
Data interface{} {{.Backtick}}json:"data"{{.Backtick}}
}
{{end}}
{{if .HasDelete}}
// {{.Name}}DeleteResponse represents the response for deleting {{.NameLower}}
type {{.Name}}DeleteResponse struct {
ID string {{.Backtick}}json:"id"{{.Backtick}}
Message string {{.Backtick}}json:"message"{{.Backtick}}
}
{{end}}
// ErrorResponse represents an error response
type ErrorResponse struct {
Error string {{.Backtick}}json:"error"{{.Backtick}}
}
`
// Replace backtick with actual backtick
modelTemplate = strings.ReplaceAll(modelTemplate, "{{.Backtick}}", "`")
writeFile("internal/models/"+data.NameLower+".go", modelTemplate, data)
}
func updateRoutesFile(data HandlerData) {
routesFile := "internal/routes/v1/routes.go"
// Read existing routes file
content, err := os.ReadFile(routesFile)
if err != nil {
fmt.Printf("Error reading routes file: %v\n", err)
return
}
// Convert to string
routesContent := string(content)
// Find the place to insert new routes
insertMarker := "\t\t// Example endpoints"
// Generate new routes
newRoutes := fmt.Sprintf("\t\t// %s endpoints\n", data.Name)
if data.HasGet {
newRoutes += fmt.Sprintf("\t\t%sHandler := handlers.New%sHandler()\n", data.NameLower, data.Name)
newRoutes += fmt.Sprintf("\t\tv1.GET(\"/%s\", %sHandler.Get%s)\n", data.NamePlural, data.NameLower, data.Name)
}
if data.HasGet {
newRoutes += fmt.Sprintf("\t\tv1.GET(\"/%s/:id\", %sHandler.Get%sByID)\n", data.NamePlural, data.NameLower, data.Name)
}
if data.HasPost {
if !data.HasGet {
newRoutes += fmt.Sprintf("\t\t%sHandler := handlers.New%sHandler()\n", data.NameLower, data.Name)
}
newRoutes += fmt.Sprintf("\t\tv1.POST(\"/%s\", %sHandler.Create%s)\n", data.NamePlural, data.NameLower, data.Name)
}
if data.HasPut {
newRoutes += fmt.Sprintf("\t\tv1.PUT(\"/%s/:id\", %sHandler.Update%s)\n", data.NamePlural, data.NameLower, data.Name)
}
if data.HasDelete {
newRoutes += fmt.Sprintf("\t\tv1.DELETE(\"/%s/:id\", %sHandler.Delete%s)\n", data.NamePlural, data.NameLower, data.Name)
}
newRoutes += "\n"
// Insert new routes after the marker
newContent := strings.Replace(routesContent, insertMarker, insertMarker+"\n"+newRoutes, 1)
// Write back to file
err = os.WriteFile(routesFile, []byte(newContent), 0644)
if err != nil {
fmt.Printf("Error writing routes file: %v\n", err)
return
}
}
func writeFile(filename, templateStr string, data HandlerData) {
tmpl, err := template.New("template").Parse(templateStr)
if err != nil {
fmt.Printf("Error parsing template: %v\n", err)
return
}
file, err := os.Create(filename)
if err != nil {
fmt.Printf("Error creating file %s: %v\n", filename, err)
return
}
defer file.Close()
err = tmpl.Execute(file, data)
if err != nil {
fmt.Printf("Error executing template: %v\n", err)
return
}
fmt.Printf("Generated: %s\n", filename)
}