Update template go

This commit is contained in:
2025-08-14 09:08:34 +07:00
parent 8c6fb0ce48
commit a64cbf4438
23 changed files with 4297 additions and 137 deletions

111
tools/HANDLER.md Normal file
View File

@@ -0,0 +1,111 @@
# Handler Generator CLI Tool
CLI tool untuk generate handler baru secara otomatis dengan swagger documentation.
## Cara Penggunaan
### Windows
```bash
# Buka terminal di folder tools
generate.bat <nama-handler> [methods]
# Contoh:
generate.bat user get post
generate.bat product get post put delete
```
### Linux/Mac
```bash
# Buka terminal di folder tools
./generate.sh <nama-handler> [methods]
# Contoh:
./generate.sh user get post
./generate.sh product get post put delete
```
### Langsung dengan Go
```bash
# Dari root project
# .. run tools/generate-handler.go : Perintahnya
# .. user nama module nya dan handlernya
# .. get post put delete metod yang di gunakan
go run tools/generate-handler.go user get post put delete
```
## Method yang Tersedia
- `get` - GET endpoint untuk list data
- `post` - POST endpoint untuk create data
- `put` - PUT endpoint untuk update data
- `delete` - DELETE endpoint untuk delete data
## File yang Dibuat Otomatis
1. **Handler**: `internal/handlers/<nama>.go`
2. **Models**: `internal/models/<nama>.go`
3. **Routes**: Update otomatis di `internal/routes/v1/routes.go`
## Contoh Penggunaan
### 1. Generate Handler dengan GET dan POST
```bash
./generate.sh user get post
```
### 2. Generate Handler dengan semua method
```bash
./generate.sh product get post put delete
```
### 3. Generate Handler dengan custom method
```bash
./generate.sh order get post delete
```
## Langkah Setelah Generate
1. Jalankan swagger generator:
```bash
swag init -g cmd/api/main.go --output cmd/api/docs
```
2. Jalankan aplikasi:
```bash
go run cmd/api/main.go
```
3. Akses swagger UI:
```
http://localhost:8080/swagger/index.html
```
## Struktur File yang Dibuat
### Handler File (`internal/handlers/<nama>.go`)
- Struct handler
- Constructor function
- Endpoint methods dengan swagger documentation
- Error handling
### Model File (`internal/models/<nama>.go`)
- Request models
- Response models
- Error response models
### Routes Update
- Otomatis menambahkan routes ke `/api/v1/<nama-plural>`
- Support parameter ID untuk endpoint spesifik
## Contoh Output
Untuk command: `./generate.sh user get post`
### Handler yang dibuat:
- `GET /api/v1/users` - List users
- `GET /api/v1/users/:id` - Get user by ID
- `POST /api/v1/users` - Create new user
### Swagger Documentation
Semua endpoint otomatis memiliki swagger documentation yang bisa diakses di:
```
http://localhost:8080/swagger/index.html

364
tools/generate-handler.go Normal file
View File

@@ -0,0 +1,364 @@
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)
}

28
tools/generate.bat Normal file
View File

@@ -0,0 +1,28 @@
@echo off
REM Handler Generator Script for Windows
REM Usage: generate.bat <handler-name> [methods]
if "%~1"=="" (
echo Usage: generate.bat ^<handler-name^> [methods]
echo Example: generate.bat user get post put delete
echo Methods: get, post, put, delete (optional, default: get post)
pause
exit /b 1
)
set HANDLER_NAME=%~1
shift
set METHODS=%*
if "%METHODS%"=="" set METHODS=get post
echo Generating handler: %HANDLER_NAME% with methods: %METHODS%
echo.
cd /d "%~dp0.."
go run tools/generate-handler.go %HANDLER_NAME% %METHODS%
echo.
echo Handler generated successfully!
echo Don't forget to run: swag init -g cmd/api/main.go
pause

33
tools/generate.sh Normal file
View File

@@ -0,0 +1,33 @@
#!/bin/bash
# Handler Generator Script for Unix/Linux/Mac
# Usage: ./generate.sh <handler-name> [methods]
set -e
if [ $# -lt 1 ]; then
echo "Usage: $0 <handler-name> [methods]"
echo "Example: $0 user get post put delete"
echo "Methods: get, post, put, delete (optional, default: get post)"
exit 1
fi
HANDLER_NAME=$1
shift
METHODS=$@
if [ -z "$METHODS" ]; then
METHODS="get post"
fi
echo "Generating handler: $HANDLER_NAME with methods: $METHODS"
echo
# Change to project root directory
cd "$(dirname "$0")/.."
# Run the generator
go run tools/generate-handler.go "$HANDLER_NAME" $METHODS
echo
echo "Handler generated successfully!"
echo "Don't forget to run: swag init -g cmd/api/main.go"