Update template go
This commit is contained in:
111
tools/HANDLER.md
Normal file
111
tools/HANDLER.md
Normal 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
364
tools/generate-handler.go
Normal 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
28
tools/generate.bat
Normal 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
33
tools/generate.sh
Normal 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"
|
||||
Reference in New Issue
Block a user