From f81f536e133c3d4730ed19b1057be2115cdef9b0 Mon Sep 17 00:00:00 2001 From: Meninjar Mulyono Date: Mon, 29 Sep 2025 17:41:21 +0700 Subject: [PATCH] update perubahan --- internal/handlers/peserta/peserta.go | 45 +- internal/models/models.go | 24 + tools/bpjs/generate-handler.go | 817 +++++++++++++++++++++------ tools/bpjs/services-config-bpjs.yaml | 6 + 4 files changed, 711 insertions(+), 181 deletions(-) diff --git a/internal/handlers/peserta/peserta.go b/internal/handlers/peserta/peserta.go index 8ba77e8..585acf5 100644 --- a/internal/handlers/peserta/peserta.go +++ b/internal/handlers/peserta/peserta.go @@ -7,7 +7,6 @@ import ( "encoding/json" "fmt" "net/http" - "reflect" "strconv" "strings" "sync" @@ -231,26 +230,26 @@ func cleanResponse(resp string) string { } // extractCode extracts the code field from metaData using reflection -func extractCode(metaData interface{}) interface{} { - v := reflect.ValueOf(metaData) - switch v.Kind() { - case reflect.Struct: - codeField := v.FieldByName("Code") - if codeField.IsValid() { - return codeField.Interface() - } - case reflect.Map: - if m, ok := metaData.(map[string]interface{}); ok { - return m["code"] - } - case reflect.String: - var metaMap map[string]interface{} - if err := json.Unmarshal([]byte(metaData.(string)), &metaMap); err == nil { - return metaMap["code"] - } - } - return nil -} +// func extractCode(metaData interface{}) interface{} { +// v := reflect.ValueOf(metaData) +// switch v.Kind() { +// case reflect.Struct: +// codeField := v.FieldByName("Code") +// if codeField.IsValid() { +// return codeField.Interface() +// } +// case reflect.Map: +// if m, ok := metaData.(map[string]interface{}); ok { +// return m["code"] +// } +// case reflect.String: +// var metaMap map[string]interface{} +// if err := json.Unmarshal([]byte(metaData.(string)), &metaMap); err == nil { +// return metaMap["code"] +// } +// } +// return nil +// } // parseHTTPStatusCode extracts HTTP status code from error message func parseHTTPStatusCode(errMsg string) int { @@ -433,7 +432,7 @@ func (h *PesertaHandler) GetBynik(c *gin.Context) { response.RequestID = requestID // Ambil status code dari metaData.code var statusCode int - code := extractCode(response.MetaData) + code := models.ExtractCode(response.MetaData) if code != nil { statusCode = models.GetStatusCodeFromMeta(code) } else { @@ -595,7 +594,7 @@ func (h *PesertaHandler) GetBynokartu(c *gin.Context) { response.RequestID = requestID // Ambil status code dari metaData.code var statusCode int - code := extractCode(response.MetaData) + code := models.ExtractCode(response.MetaData) if code != nil { statusCode = models.GetStatusCodeFromMeta(code) } else { diff --git a/internal/models/models.go b/internal/models/models.go index 2643ef8..b764465 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -3,7 +3,9 @@ package models import ( "database/sql" "database/sql/driver" + "encoding/json" "net/http" + "reflect" "strconv" "time" ) @@ -199,6 +201,28 @@ func GetStatusCodeFromMeta(metaCode interface{}) int { return statusCode } +// extractCode extracts the code field from metaData using reflection +func ExtractCode(metaData interface{}) interface{} { + v := reflect.ValueOf(metaData) + switch v.Kind() { + case reflect.Struct: + codeField := v.FieldByName("Code") + if codeField.IsValid() { + return codeField.Interface() + } + case reflect.Map: + if m, ok := metaData.(map[string]interface{}); ok { + return m["code"] + } + case reflect.String: + var metaMap map[string]interface{} + if err := json.Unmarshal([]byte(metaData.(string)), &metaMap); err == nil { + return metaMap["code"] + } + } + return nil +} + // Validation constants const ( StatusDraft = "draft" diff --git a/tools/bpjs/generate-handler.go b/tools/bpjs/generate-handler.go index 77cfe32..b7f16c7 100644 --- a/tools/bpjs/generate-handler.go +++ b/tools/bpjs/generate-handler.go @@ -39,24 +39,25 @@ type FunctionInfo struct { } type EndpointConfig struct { - Methods []string `yaml:"methods"` - GetRoutes string `yaml:"get_routes"` - PostRoutes string `yaml:"post_routes"` - PutRoutes string `yaml:"put_routes"` - DeleteRoutes string `yaml:"delete_routes"` - GetPath string `yaml:"get_path"` - PostPath string `yaml:"post_path"` - PutPath string `yaml:"put_path"` - DeletePath string `yaml:"delete_path"` - Model string `yaml:"model"` - ResponseModel string `yaml:"response_model"` - RequestModel string `yaml:"request_model"` - Description string `yaml:"description"` - Summary string `yaml:"summary"` - Tags []string `yaml:"tags"` - RequireAuth bool `yaml:"require_auth"` - CacheEnabled bool `yaml:"cache_enabled"` - CacheTTL int `yaml:"cache_ttl"` + Methods []string `yaml:"methods"` + GetRoutes string `yaml:"get_routes"` + PostRoutes string `yaml:"post_routes"` + PutRoutes string `yaml:"put_routes"` + DeleteRoutes string `yaml:"delete_routes"` + GetPath string `yaml:"get_path"` + PostPath string `yaml:"post_path"` + PutPath string `yaml:"put_path"` + DeletePath string `yaml:"delete_path"` + Model string `yaml:"model"` + ResponseModel string `yaml:"response_model"` + RequestModel string `yaml:"request_model"` + Description string `yaml:"description"` + Summary string `yaml:"summary"` + Tags []string `yaml:"tags"` + RequireAuth bool `yaml:"require_auth"` + CacheEnabled bool `yaml:"cache_enabled"` + CacheTTL int `yaml:"cache_ttl"` + EnableDatabase bool `yaml:"enable_database"` } type GlobalConfig struct { @@ -103,18 +104,19 @@ type FunctionConfig struct { PutRoutes string `yaml:"put_routes"` DeleteRoutes string `yaml:"delete_routes"` // ✅ Path untuk swagger documentation - GetPath string `yaml:"get_path"` - PostPath string `yaml:"post_path"` - PutPath string `yaml:"put_path"` - DeletePath string `yaml:"delete_path"` - ResponseModel string `yaml:"response_model"` - RequestModel string `yaml:"request_model"` - Description string `yaml:"description"` - Summary string `yaml:"summary"` - Tags []string `yaml:"tags"` - RequireAuth bool `yaml:"require_auth"` - CacheEnabled bool `yaml:"cache_enabled"` - CacheTTL int `yaml:"cache_ttl"` + GetPath string `yaml:"get_path"` + PostPath string `yaml:"post_path"` + PutPath string `yaml:"put_path"` + DeletePath string `yaml:"delete_path"` + ResponseModel string `yaml:"response_model"` + RequestModel string `yaml:"request_model"` + Description string `yaml:"description"` + Summary string `yaml:"summary"` + Tags []string `yaml:"tags"` + RequireAuth bool `yaml:"require_auth"` + CacheEnabled bool `yaml:"cache_enabled"` + CacheTTL int `yaml:"cache_ttl"` + EnableDatabase bool `yaml:"enable_database"` } type TemplateData struct { @@ -136,40 +138,41 @@ type TemplateData struct { GlobalConfig GlobalConfig ShouldGenerateStruct bool ShouldGenerateConstructor bool - FunctionalArea string // e.g. "rujukan", "search" + FunctionalArea string HandlerName string } type EndpointData struct { - Name string - NameLower string - NameUpper string - NameCamel string - Methods []string - GetRoutes string - PostRoutes string - PutRoutes string - DeleteRoutes string - GetPath string - PostPath string - PutPath string - DeletePath string - Model string - ResponseModel string - RequestModel string - DataModel string - Description string - Summary string - Tags []string - HasGet bool - HasPost bool - HasPut bool - HasDelete bool - RequireAuth bool - CacheEnabled bool - CacheTTL int - PathParams []string - ModelPackage string + Name string + NameLower string + NameUpper string + NameCamel string + Methods []string + GetRoutes string + PostRoutes string + PutRoutes string + DeleteRoutes string + GetPath string + PostPath string + PutPath string + DeletePath string + Model string + ResponseModel string + RequestModel string + DataModel string + Description string + Summary string + Tags []string + HasGet bool + HasPost bool + HasPut bool + HasDelete bool + RequireAuth bool + CacheEnabled bool + EnableDatabase bool + CacheTTL int + PathParams []string + ModelPackage string } // Fungsi utama yang diupdate untuk menggunakan separation of concerns @@ -181,39 +184,39 @@ func generateHandlerWithValidation(serviceName string, svc Service, gc GlobalCon // } // Step 2: Generate methods files per endpoint group - baseDir := gc.OutputDir - for groupName, grp := range svc.Endpoints { - folder := filepath.Join(baseDir, grp.HandlerFolder) - if err := os.MkdirAll(folder, 0755); err != nil { - return fmt.Errorf("mkdir %s: %w", folder, err) - } + // baseDir := gc.OutputDir + // for groupName, grp := range svc.Endpoints { + // folder := filepath.Join(baseDir, grp.HandlerFolder) + // if err := os.MkdirAll(folder, 0755); err != nil { + // return fmt.Errorf("mkdir %s: %w", folder, err) + // } - // Generate methods file dengan naming yang jelas - methodsFileName := fmt.Sprintf("%s.go", strings.ToLower(groupName)) - methodsFilePath := filepath.Join(folder, methodsFileName) + // // Generate methods file dengan naming yang jelas + // methodsFileName := fmt.Sprintf("%s.go", strings.ToLower(groupName)) + // methodsFilePath := filepath.Join(folder, methodsFileName) - // Check if methods file exists - fileExists := false - if _, err := os.Stat(methodsFilePath); err == nil { - fileExists = true - } + // // Check if methods file exists + // fileExists := false + // if _, err := os.Stat(methodsFilePath); err == nil { + // fileExists = true + // } - if !fileExists { - // Create new methods file - err := createMethodsFileFromConfig(methodsFilePath, svc, grp, gc, groupName) - if err != nil { - return fmt.Errorf("create methods file %s: %w", methodsFilePath, err) - } - fmt.Printf("✅ Created methods file: %s\n", methodsFilePath) - } else { - // Update existing methods file with new functions only - err := updateExistingMethodsFile(methodsFilePath, svc, grp, gc, groupName) - if err != nil { - return fmt.Errorf("update methods file %s: %w", methodsFilePath, err) - } - fmt.Printf("✅ Updated methods file: %s\n", methodsFilePath) - } - } + // if !fileExists { + // // Create new methods file + // err := createMethodsFileFromConfig(methodsFilePath, svc, grp, gc, groupName) + // if err != nil { + // return fmt.Errorf("create methods file %s: %w", methodsFilePath, err) + // } + // fmt.Printf("✅ Created methods file: %s\n", methodsFilePath) + // } else { + // // Update existing methods file with new functions only + // err := updateExistingMethodsFile(methodsFilePath, svc, grp, gc, groupName) + // if err != nil { + // return fmt.Errorf("update methods file %s: %w", methodsFilePath, err) + // } + // fmt.Printf("✅ Updated methods file: %s\n", methodsFilePath) + // } + // } // ✅ Step 2: Generate routes err := generateRoutes(serviceName, svc, gc) if err != nil { @@ -507,31 +510,32 @@ func processDirectoryStructure(baseDir string, dirInfo *DirectoryInfo, service S } func processFunctionData(svc Service, grp EndpointGroup, fname string, fcfg FunctionConfig, gc GlobalConfig) TemplateData { ed := EndpointData{ - Name: toCamelCase(fname), - NameLower: strings.ToLower(fname), - NameUpper: strings.ToUpper(fname), - NameCamel: toCamelCase(fname), - Methods: fcfg.Methods, - GetRoutes: fcfg.GetRoutes, - PostRoutes: fcfg.PostRoutes, - PutRoutes: fcfg.PutRoutes, - DeleteRoutes: fcfg.DeleteRoutes, - GetPath: fcfg.Path, - PostPath: fcfg.Path, - PutPath: fcfg.Path, - DeletePath: fcfg.Path, - Model: fcfg.Model, - ResponseModel: fcfg.ResponseModel, - RequestModel: fcfg.RequestModel, - DataModel: strings.Replace(fcfg.ResponseModel, "Response", "Data", 1), - Description: fcfg.Description, - Summary: fcfg.Summary, - Tags: fcfg.Tags, - RequireAuth: fcfg.RequireAuth, - CacheEnabled: fcfg.CacheEnabled, - CacheTTL: fcfg.CacheTTL, - PathParams: extractPathParams(fcfg.Path), - ModelPackage: grp.HandlerFolder, + Name: toCamelCase(fname), + NameLower: strings.ToLower(fname), + NameUpper: strings.ToUpper(fname), + NameCamel: toCamelCase(fname), + Methods: fcfg.Methods, + GetRoutes: fcfg.GetRoutes, + PostRoutes: fcfg.PostRoutes, + PutRoutes: fcfg.PutRoutes, + DeleteRoutes: fcfg.DeleteRoutes, + GetPath: fcfg.Path, + PostPath: fcfg.Path, + PutPath: fcfg.Path, + DeletePath: fcfg.Path, + Model: fcfg.Model, + ResponseModel: fcfg.ResponseModel, + RequestModel: fcfg.RequestModel, + DataModel: strings.Replace(fcfg.ResponseModel, "Response", "Data", 1), + Description: fcfg.Description, + Summary: fcfg.Summary, + Tags: fcfg.Tags, + RequireAuth: fcfg.RequireAuth, + CacheEnabled: fcfg.CacheEnabled, + CacheTTL: fcfg.CacheTTL, + EnableDatabase: fcfg.EnableDatabase, + PathParams: extractPathParams(fcfg.Path), + ModelPackage: grp.HandlerFolder, } // set flags for _, m := range fcfg.Methods { @@ -863,6 +867,7 @@ import ( // {{.HandlerName}}Handler handles {{.HandlerName}} BPJS services type {{.HandlerName}}Handler struct { service services.VClaimService + db database.Service validator *validator.Validate logger logger.Logger config config.BpjsConfig @@ -870,7 +875,7 @@ type {{.HandlerName}}Handler struct { // {{.HandlerName}}HandlerConfig contains configuration for {{.HandlerName}}Handler type {{.HandlerName}}HandlerConfig struct { - BpjsConfig config.BpjsConfig + Config *config.Config Logger logger.Logger Validator *validator.Validate } @@ -878,10 +883,11 @@ type {{.HandlerName}}HandlerConfig struct { // New{{.HandlerName}}Handler creates a new {{.HandlerName}}Handler func New{{.HandlerName}}Handler(cfg {{.HandlerName}}HandlerConfig) *{{.HandlerName}}Handler { return &{{.HandlerName}}Handler{ - service: services.NewService(cfg.BpjsConfig), + db: database.New(cfg.Config), + service: services.NewService(cfg.Config.Bpjs), validator: cfg.Validator, logger: cfg.Logger, - config: cfg.BpjsConfig, + config: cfg.Config.Bpjs, } } ` @@ -924,7 +930,8 @@ import ( "{{.ModuleName}}/internal/config" "{{.ModuleName}}/internal/models" - "{{.ModuleName}}/internal/models/vclaim/{{.Package}}" + "{{.ModuleName}}/internal/database" + "{{.ModuleName}}/internal/models/{{.Package}}" "{{.ModuleName}}/internal/services/bpjs" "{{.ModuleName}}/pkg/logger" @@ -935,23 +942,25 @@ import ( // {{.HandlerName}}Handler handles {{.HandlerName}} BPJS services type {{.HandlerName}}Handler struct { service services.VClaimService + db database.Service validator *validator.Validate logger logger.Logger config config.BpjsConfig } // {{.HandlerName}}HandlerConfig contains configuration for {{.HandlerName}}Handler type {{.HandlerName}}HandlerConfig struct { - BpjsConfig config.BpjsConfig + Config *config.Config Logger logger.Logger Validator *validator.Validate } // New{{.HandlerName}}Handler creates a new {{.HandlerName}}Handler func New{{.HandlerName}}Handler(cfg {{.HandlerName}}HandlerConfig) *{{.HandlerName}}Handler { return &{{.HandlerName}}Handler{ - service: services.NewService(cfg.BpjsConfig), + db: database.New(cfg.Config), + service: services.NewService(cfg.Config.Bpjs), validator: cfg.Validator, logger: cfg.Logger, - config: cfg.BpjsConfig, + config: cfg.Config.Bpjs, } } {{range .Endpoints}} @@ -981,7 +990,7 @@ func (h *{{$.HandlerName}}Handler) Get{{.Name}}(c *gin.Context) { requestID = uuid.New().String() c.Header("X-Request-ID", requestID) } - + {{if $.HasLogger}} h.logger.Info("Processing Get{{.Name}} request", map[string]interface{}{ "request_id": requestID, @@ -991,6 +1000,36 @@ func (h *{{$.HandlerName}}Handler) Get{{.Name}}(c *gin.Context) { {{end}} }) {{end}} + {{if $.HasLogger}} + h.logger.Info("Processing Get{{.Name}} request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "{{.GetPath}}", + {{range .PathParams}} + "{{.}}": c.Param("{{.}}"), + {{end}} + }) + {{end}} + + {{if .EnableDatabase}} + // Get database connection + dbConn, err := h.db.GetDB("postgres_satudata") + if err != nil { + {{if $.HasLogger}} + h.logger.Error("Database connection failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Database connection failed", + RequestID: requestID, + }) + return + } + // Note: dbConn is available for future database operations (e.g., caching, logging) + _ = dbConn // Prevent unused variable warning + {{end}} // Extract path parameters {{range .PathParams}} @@ -1068,9 +1107,17 @@ func (h *{{$.HandlerName}}Handler) Get{{.Name}}(c *gin.Context) { } // Ensure response has proper fields - response.Status = "success" - response.RequestID = requestID - c.JSON(http.StatusOK, response) + response.Status = "success" + response.RequestID = requestID + // Ambil status code dari metaData.code + var statusCode int + code := models.ExtractCode(response.MetaData) + if code != nil { + statusCode = models.GetStatusCodeFromMeta(code) + } else { + statusCode = 200 + } + c.JSON(statusCode, response) } {{end}} @@ -1107,6 +1154,36 @@ func (h *{{$.HandlerName}}Handler) Create{{.Name}}(c *gin.Context) { "endpoint": "{{.PostPath}}", }) {{end}} + {{if $.HasLogger}} + h.logger.Info("Processing Get{{.Name}} request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "{{.GetPath}}", + {{range .PathParams}} + "{{.}}": c.Param("{{.}}"), + {{end}} + }) + {{end}} + + {{if .EnableDatabase}} + // Get database connection + dbConn, err := h.db.GetDB("postgres_satudata") + if err != nil { + {{if $.HasLogger}} + h.logger.Error("Database connection failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Database connection failed", + RequestID: requestID, + }) + return + } + // Note: dbConn is available for future database operations (e.g., caching, logging) + _ = dbConn // Prevent unused variable warning + {{end}} // Bind and validate request body var req {{.ModelPackage}}.{{.RequestModel}} @@ -1240,7 +1317,14 @@ func (h *{{$.HandlerName}}Handler) Create{{.Name}}(c *gin.Context) { }) {{end}} - c.JSON(http.StatusCreated, response) + var statusCode int + code := models.ExtractCode(response.MetaData) + if code != nil { + statusCode = models.GetStatusCodeFromMeta(code) + } else { + statusCode = 200 + } + c.JSON(statusCode, response) } {{end}} @@ -1283,6 +1367,36 @@ func (h *{{$.HandlerName}}Handler) Update{{.Name}}(c *gin.Context) { {{end}} }) {{end}} +{{if $.HasLogger}} + h.logger.Info("Processing Get{{.Name}} request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "{{.GetPath}}", + {{range .PathParams}} + "{{.}}": c.Param("{{.}}"), + {{end}} + }) + {{end}} + + {{if .EnableDatabase}} + // Get database connection + dbConn, err := h.db.GetDB("postgres_satudata") + if err != nil { + {{if $.HasLogger}} + h.logger.Error("Database connection failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Database connection failed", + RequestID: requestID, + }) + return + } + // Note: dbConn is available for future database operations (e.g., caching, logging) + _ = dbConn // Prevent unused variable warning + {{end}} // Extract path parameters {{range .PathParams}} @@ -1422,7 +1536,14 @@ func (h *{{$.HandlerName}}Handler) Update{{.Name}}(c *gin.Context) { }) {{end}} - c.JSON(http.StatusOK, response) + var statusCode int + code := models.ExtractCode(response.MetaData) + if code != nil { + statusCode = models.GetStatusCodeFromMeta(code) + } else { + statusCode = 200 + } + c.JSON(statusCode, response) } {{end}} @@ -1463,6 +1584,37 @@ func (h *{{$.HandlerName}}Handler) Delete{{.Name}}(c *gin.Context) { }) {{end}} + {{if $.HasLogger}} + h.logger.Info("Processing Get{{.Name}} request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "{{.GetPath}}", + {{range .PathParams}} + "{{.}}": c.Param("{{.}}"), + {{end}} + }) + {{end}} + + {{if .EnableDatabase}} + // Get database connection + dbConn, err := h.db.GetDB("postgres_satudata") + if err != nil { + {{if $.HasLogger}} + h.logger.Error("Database connection failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Database connection failed", + RequestID: requestID, + }) + return + } + // Note: dbConn is available for future database operations (e.g., caching, logging) + _ = dbConn // Prevent unused variable warning + {{end}} + // Extract path parameters {{range .PathParams}} {{.}} := c.Param("{{.}}") @@ -1562,7 +1714,14 @@ func (h *{{$.HandlerName}}Handler) Delete{{.Name}}(c *gin.Context) { }) {{end}} - c.JSON(http.StatusOK, response) + var statusCode int + code := models.ExtractCode(response.MetaData) + if code != nil { + statusCode = models.GetStatusCodeFromMeta(code) + } else { + statusCode = 200 + } + c.JSON(statusCode, response) } {{end}} {{end}}` @@ -1606,6 +1765,37 @@ func (h *{{$.HandlerName}}Handler) Get{{.Name}}(c *gin.Context) { }) {{end}} + {{if $.HasLogger}} + h.logger.Info("Processing Get{{.Name}} request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "{{.GetPath}}", + {{range .PathParams}} + "{{.}}": c.Param("{{.}}"), + {{end}} + }) + {{end}} + + {{if .EnableDatabase}} + // Get database connection + dbConn, err := h.db.GetDB("postgres_satudata") + if err != nil { + {{if $.HasLogger}} + h.logger.Error("Database connection failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Database connection failed", + RequestID: requestID, + }) + return + } + // Note: dbConn is available for future database operations (e.g., caching, logging) + _ = dbConn // Prevent unused variable warning + {{end}} + // Extract path parameters {{range .PathParams}} {{.}} := c.Param("{{.}}") @@ -1684,7 +1874,14 @@ func (h *{{$.HandlerName}}Handler) Get{{.Name}}(c *gin.Context) { // Ensure response has proper fields response.Status = "success" response.RequestID = requestID - c.JSON(http.StatusOK, response) + var statusCode int + code := models.ExtractCode(response.MetaData) + if code != nil { + statusCode = models.GetStatusCodeFromMeta(code) + } else { + statusCode = 200 + } + c.JSON(statusCode, response) } {{end}} @@ -1722,6 +1919,37 @@ func (h *{{$.HandlerName}}Handler) Create{{.Name}}(c *gin.Context) { }) {{end}} + {{if $.HasLogger}} + h.logger.Info("Processing Get{{.Name}} request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "{{.GetPath}}", + {{range .PathParams}} + "{{.}}": c.Param("{{.}}"), + {{end}} + }) + {{end}} + + {{if .EnableDatabase}} + // Get database connection + dbConn, err := h.db.GetDB("postgres_satudata") + if err != nil { + {{if $.HasLogger}} + h.logger.Error("Database connection failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Database connection failed", + RequestID: requestID, + }) + return + } + // Note: dbConn is available for future database operations (e.g., caching, logging) + _ = dbConn // Prevent unused variable warning + {{end}} + // Bind and validate request body var req {{.ModelPackage}}.{{.RequestModel}} if err := c.ShouldBindJSON(&req); err != nil { @@ -1854,7 +2082,14 @@ func (h *{{$.HandlerName}}Handler) Create{{.Name}}(c *gin.Context) { }) {{end}} - c.JSON(http.StatusCreated, response) + var statusCode int + code := models.ExtractCode(response.MetaData) + if code != nil { + statusCode = models.GetStatusCodeFromMeta(code) + } else { + statusCode = 200 + } + c.JSON(statusCode, response) } {{end}} @@ -1898,6 +2133,37 @@ func (h *{{$.HandlerName}}Handler) Update{{.Name}}(c *gin.Context) { }) {{end}} + {{if $.HasLogger}} + h.logger.Info("Processing Get{{.Name}} request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "{{.GetPath}}", + {{range .PathParams}} + "{{.}}": c.Param("{{.}}"), + {{end}} + }) + {{end}} + + {{if .EnableDatabase}} + // Get database connection + dbConn, err := h.db.GetDB("postgres_satudata") + if err != nil { + {{if $.HasLogger}} + h.logger.Error("Database connection failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Database connection failed", + RequestID: requestID, + }) + return + } + // Note: dbConn is available for future database operations (e.g., caching, logging) + _ = dbConn // Prevent unused variable warning + {{end}} + // Extract path parameters {{range .PathParams}} {{.}} := c.Param("{{.}}") @@ -2036,7 +2302,14 @@ func (h *{{$.HandlerName}}Handler) Update{{.Name}}(c *gin.Context) { }) {{end}} - c.JSON(http.StatusOK, response) + var statusCode int + code := models.ExtractCode(response.MetaData) + if code != nil { + statusCode = models.GetStatusCodeFromMeta(code) + } else { + statusCode = 200 + } + c.JSON(statusCode, response) } {{end}} @@ -2077,6 +2350,37 @@ func (h *{{$.HandlerName}}Handler) Delete{{.Name}}(c *gin.Context) { }) {{end}} + {{if $.HasLogger}} + h.logger.Info("Processing Get{{.Name}} request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "{{.GetPath}}", + {{range .PathParams}} + "{{.}}": c.Param("{{.}}"), + {{end}} + }) + {{end}} + + {{if .EnableDatabase}} + // Get database connection + dbConn, err := h.db.GetDB("postgres_satudata") + if err != nil { + {{if $.HasLogger}} + h.logger.Error("Database connection failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Database connection failed", + RequestID: requestID, + }) + return + } + // Note: dbConn is available for future database operations (e.g., caching, logging) + _ = dbConn // Prevent unused variable warning + {{end}} + // Extract path parameters {{range .PathParams}} {{.}} := c.Param("{{.}}") @@ -2176,7 +2480,14 @@ func (h *{{$.HandlerName}}Handler) Delete{{.Name}}(c *gin.Context) { }) {{end}} - c.JSON(http.StatusOK, response) + var statusCode int + code := models.ExtractCode(response.MetaData) + if code != nil { + statusCode = models.GetStatusCodeFromMeta(code) + } else { + statusCode = 200 + } + c.JSON(statusCode, response) } {{end}} {{end}}` @@ -2276,6 +2587,37 @@ func (h *{{$.HandlerName}}Handler) Get{{.Name}}(c *gin.Context) { }) {{end}} + {{if $.HasLogger}} + h.logger.Info("Processing Get{{.Name}} request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "{{.GetPath}}", + {{range .PathParams}} + "{{.}}": c.Param("{{.}}"), + {{end}} + }) + {{end}} + + {{if .EnableDatabase}} + // Get database connection + dbConn, err := h.db.GetDB("postgres_satudata") + if err != nil { + {{if $.HasLogger}} + h.logger.Error("Database connection failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Database connection failed", + RequestID: requestID, + }) + return + } + // Note: dbConn is available for future database operations (e.g., caching, logging) + _ = dbConn // Prevent unused variable warning + {{end}} + // Extract path parameters {{range .PathParams}} {{.}} := c.Param("{{.}}") @@ -2354,7 +2696,14 @@ func (h *{{$.HandlerName}}Handler) Get{{.Name}}(c *gin.Context) { // Ensure response has proper fields response.Status = "success" response.RequestID = requestID - c.JSON(http.StatusOK, response) + var statusCode int + code := models.ExtractCode(response.MetaData) + if code != nil { + statusCode = models.GetStatusCodeFromMeta(code) + } else { + statusCode = 200 + } + c.JSON(statusCode, response) } {{end}} @@ -2392,6 +2741,37 @@ func (h *{{$.HandlerName}}Handler) Create{{.Name}}(c *gin.Context) { }) {{end}} + {{if $.HasLogger}} + h.logger.Info("Processing Get{{.Name}} request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "{{.GetPath}}", + {{range .PathParams}} + "{{.}}": c.Param("{{.}}"), + {{end}} + }) + {{end}} + + {{if .EnableDatabase}} + // Get database connection + dbConn, err := h.db.GetDB("postgres_satudata") + if err != nil { + {{if $.HasLogger}} + h.logger.Error("Database connection failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Database connection failed", + RequestID: requestID, + }) + return + } + // Note: dbConn is available for future database operations (e.g., caching, logging) + _ = dbConn // Prevent unused variable warning + {{end}} + // Bind and validate request body var req {{.ModelPackage}}.{{.RequestModel}} if err := c.ShouldBindJSON(&req); err != nil { @@ -2524,7 +2904,14 @@ func (h *{{$.HandlerName}}Handler) Create{{.Name}}(c *gin.Context) { }) {{end}} - c.JSON(http.StatusCreated, response) + var statusCode int + code := models.ExtractCode(response.MetaData) + if code != nil { + statusCode = models.GetStatusCodeFromMeta(code) + } else { + statusCode = 200 + } + c.JSON(statusCode, response) } {{end}} @@ -2568,6 +2955,37 @@ func (h *{{$.HandlerName}}Handler) Update{{.Name}}(c *gin.Context) { }) {{end}} + {{if $.HasLogger}} + h.logger.Info("Processing Get{{.Name}} request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "{{.GetPath}}", + {{range .PathParams}} + "{{.}}": c.Param("{{.}}"), + {{end}} + }) + {{end}} + + {{if .EnableDatabase}} + // Get database connection + dbConn, err := h.db.GetDB("postgres_satudata") + if err != nil { + {{if $.HasLogger}} + h.logger.Error("Database connection failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Database connection failed", + RequestID: requestID, + }) + return + } + // Note: dbConn is available for future database operations (e.g., caching, logging) + _ = dbConn // Prevent unused variable warning + {{end}} + // Extract path parameters {{range .PathParams}} {{.}} := c.Param("{{.}}") @@ -2706,7 +3124,14 @@ func (h *{{$.HandlerName}}Handler) Update{{.Name}}(c *gin.Context) { }) {{end}} - c.JSON(http.StatusOK, response) + var statusCode int + code := models.ExtractCode(response.MetaData) + if code != nil { + statusCode = models.GetStatusCodeFromMeta(code) + } else { + statusCode = 200 + } + c.JSON(statusCode, response) } {{end}} @@ -2747,6 +3172,37 @@ func (h *{{$.HandlerName}}Handler) Delete{{.Name}}(c *gin.Context) { }) {{end}} + {{if $.HasLogger}} + h.logger.Info("Processing Get{{.Name}} request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "{{.GetPath}}", + {{range .PathParams}} + "{{.}}": c.Param("{{.}}"), + {{end}} + }) + {{end}} + + {{if .EnableDatabase}} + // Get database connection + dbConn, err := h.db.GetDB("postgres_satudata") + if err != nil { + {{if $.HasLogger}} + h.logger.Error("Database connection failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Database connection failed", + RequestID: requestID, + }) + return + } + // Note: dbConn is available for future database operations (e.g., caching, logging) + _ = dbConn // Prevent unused variable warning + {{end}} + // Extract path parameters {{range .PathParams}} {{.}} := c.Param("{{.}}") @@ -2846,7 +3302,14 @@ func (h *{{$.HandlerName}}Handler) Delete{{.Name}}(c *gin.Context) { }) {{end}} - c.JSON(http.StatusOK, response) + var statusCode int + code := models.ExtractCode(response.MetaData) + if code != nil { + statusCode = models.GetStatusCodeFromMeta(code) + } else { + statusCode = 200 + } + c.JSON(statusCode, response) } {{end}} {{end}}` @@ -3021,8 +3484,6 @@ func generateRoutes(serviceName string, svc Service, gc GlobalConfig) error { processedFolders := make(map[string]bool) for groupName, grp := range svc.Endpoints { // Import berdasarkan handler folder - // imports = append(imports, fmt.Sprintf("\t%sHandlers \"%s/internal/handlers/%s\"", - // grp.HandlerFolder, gc.ModuleName, grp.HandlerFolder)) if !processedFolders[grp.HandlerFolder] { importLine := fmt.Sprintf("\t%sHandlers \"%s/internal/handlers/%s\"", grp.HandlerFolder, gc.ModuleName, grp.HandlerFolder) @@ -3046,7 +3507,7 @@ func generateRoutes(serviceName string, svc Service, gc GlobalConfig) error { grp.HandlerFolder, grp.HandlerName)) - routesCode.WriteString("\t\tBpjsConfig: cfg.Bpjs,\n") + routesCode.WriteString("\t\tConfig: cfg,\n") routesCode.WriteString("\t\tLogger: *logger.Default(),\n") routesCode.WriteString("\t\tValidator: validator.New(),\n") routesCode.WriteString("\t})\n") @@ -3108,19 +3569,28 @@ func generateRoutes(serviceName string, svc Service, gc GlobalConfig) error { } // ✅ Generate route berdasarkan method + var routeLine string switch strings.ToUpper(method) { case "GET": - routesCode.WriteString(fmt.Sprintf("\t%s.GET(\"%s\", %s.Get%s)\n", - groupVar, cleanPath, handlerVar, endpoint.Name)) + routeLine = fmt.Sprintf("%s.GET(\"%s\", %s.Get%s)\n", + groupVar, cleanPath, handlerVar, endpoint.Name) case "POST": - routesCode.WriteString(fmt.Sprintf("\t%s.POST(\"%s\", %s.Create%s)\n", - groupVar, cleanPath, handlerVar, endpoint.Name)) + routeLine = fmt.Sprintf("%s.POST(\"%s\", %s.Create%s)\n", + groupVar, cleanPath, handlerVar, endpoint.Name) case "PUT": - routesCode.WriteString(fmt.Sprintf("\t%s.PUT(\"%s\", %s.Update%s)\n", - groupVar, cleanPath, handlerVar, endpoint.Name)) + routeLine = fmt.Sprintf("%s.PUT(\"%s\", %s.Update%s)\n", + groupVar, cleanPath, handlerVar, endpoint.Name) case "DELETE": - routesCode.WriteString(fmt.Sprintf("\t%s.DELETE(\"%s\", %s.Delete%s)\n", - groupVar, cleanPath, handlerVar, endpoint.Name)) + routeLine = fmt.Sprintf("%s.DELETE(\"%s\", %s.Delete%s)\n", + groupVar, cleanPath, handlerVar, endpoint.Name) + } + + // ✅ PERBAIKAN: Check if route already exists + if !strings.Contains(routesContentStr, strings.TrimSpace(routeLine)) { + routesCode.WriteString(routeLine) + fmt.Printf("✅ Added route: %s %s\n", method, cleanPath) + } else { + fmt.Printf("⚠️ Skipped duplicate route: %s %s\n", method, cleanPath) } } } @@ -3157,20 +3627,51 @@ func generateRoutes(serviceName string, svc Service, gc GlobalConfig) error { } } - // Find and insert routes - publishedRoutesMarker := "// ============= PUBLISHED ROUTES ===============================================" - if !strings.Contains(routesContentStr, publishedRoutesMarker) { - return fmt.Errorf("PUBLISHED ROUTES marker not found in routes.go") + // ✅ PERBAIKAN: Try new format first, then fallback to old format + var publishedRoutesMarker string + var insertionPoint int + + // Try the new multi-line format first + newFormatMarker := "// =============================================================================\n// PUBLISHED ROUTES\n// =============================================================================" + if strings.Contains(routesContentStr, newFormatMarker) { + publishedRoutesMarker = newFormatMarker + fmt.Printf("✅ Found new format PUBLISHED ROUTES marker\n") + } else { + // Fallback to old single-line format + oldFormatMarker := "// PUBLISHED ROUTES" + if strings.Contains(routesContentStr, oldFormatMarker) { + publishedRoutesMarker = oldFormatMarker + fmt.Printf("⚠️ Found old format PUBLISHED ROUTES marker, using fallback\n") + } else { + return fmt.Errorf("PUBLISHED ROUTES marker not found in routes.go (tried both new and old formats)") + } } - insertionPoint := strings.Index(routesContentStr, publishedRoutesMarker) + len(publishedRoutesMarker) - newRoutesContent := routesContentStr[:insertionPoint] + "\n" + strings.Join(allRoutes, "\n") + "\n" + routesContentStr[insertionPoint:] + insertionPoint = strings.Index(routesContentStr, publishedRoutesMarker) + len(publishedRoutesMarker) - err = ioutil.WriteFile(routesFilePath, []byte(newRoutesContent), 0644) - if err != nil { - return fmt.Errorf("failed to write updated routes file: %w", err) + // If we want to upgrade to the new format, we can replace the old marker with the new one + if publishedRoutesMarker == "// PUBLISHED ROUTES" { + // Replace old marker with new format + newRoutesContent := routesContentStr[:insertionPoint] + "\n" + strings.Join(allRoutes, "\n") + "\n" + routesContentStr[insertionPoint:] + + // Now replace the old marker with the new format + newRoutesContent = strings.Replace(newRoutesContent, "// PUBLISHED ROUTES", newFormatMarker, 1) + + err = ioutil.WriteFile(routesFilePath, []byte(newRoutesContent), 0644) + if err != nil { + return fmt.Errorf("failed to write updated routes file: %w", err) + } + fmt.Printf("✅ Updated main routes file with %s routes and upgraded marker format\n", svc.Name) + } else { + // Use existing new format + newRoutesContent := routesContentStr[:insertionPoint] + "\n" + strings.Join(allRoutes, "\n") + "\n" + routesContentStr[insertionPoint:] + + err = ioutil.WriteFile(routesFilePath, []byte(newRoutesContent), 0644) + if err != nil { + return fmt.Errorf("failed to write updated routes file: %w", err) + } + fmt.Printf("✅ Updated main routes file with %s routes\n", svc.Name) } - fmt.Printf("✅ Updated main routes file with %s routes\n", svc.Name) return nil } diff --git a/tools/bpjs/services-config-bpjs.yaml b/tools/bpjs/services-config-bpjs.yaml index 4dd95e9..6c80820 100644 --- a/tools/bpjs/services-config-bpjs.yaml +++ b/tools/bpjs/services-config-bpjs.yaml @@ -40,6 +40,7 @@ services: tags: ["Peserta"] require_auth: true cache_enabled: true + enable_database: true # jika menggunakan database cache_ttl: 300 bynik: @@ -61,6 +62,7 @@ services: tags: ["Peserta"] require_auth: true cache_enabled: true + enable_database: true # jika menggunakan database cache_ttl: 300 rujukan: @@ -88,6 +90,7 @@ services: tags: ["Rujukan"] require_auth: true cache_enabled: true + enable_database: true # jika menggunakan database cache_ttl: 180 rujukanbalik: methods: ["POST", "PUT", "DELETE"] @@ -108,6 +111,7 @@ services: tags: ["Rujukan"] require_auth: true cache_enabled: true + enable_database: true # jika menggunakan database cache_ttl: 180 search: @@ -135,6 +139,7 @@ services: tags: ["Rujukan"] require_auth: true cache_enabled: true + enable_database: true # jika menggunakan database cache_ttl: 300 bynokartu: @@ -156,5 +161,6 @@ services: tags: ["Rujukan"] require_auth: true cache_enabled: true + enable_database: true # jika menggunakan database cache_ttl: 300