diff --git a/README.md b/README.md index f0b6003..16e878c 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,12 @@ go run tools/general/generate-handler.go orders/product get post go run tools/general/generate-handler.go orders/order get post put delete dynamic search stats go run tools/bpjs/generate-bpjs-handler.go reference/peserta get + +# 1. Test generate satu service +go run tools/bpjs/generate-handler.go services-config-bpjs.yaml vclaim + +# 2. Test generate semua service +go run tools/bpjs/generate-handler.go services-config-bpjs.yaml ``` ### Method Tersedia diff --git a/handler.tmpl b/handler.tmpl new file mode 100644 index 0000000..94a8a91 --- /dev/null +++ b/handler.tmpl @@ -0,0 +1,163 @@ +// Code generated by genhandler; DO NOT EDIT. + +package handlers + +import ( + "context" + "net/http" + "time" + "{{.ModuleName}}/internal/models/reference" + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +/* ===== HANDLER STRUCT & CONSTRUCTOR ===== */ +type {{.ServiceName}}Handler struct { + svc reference.{{.ServiceName}}Service + timeout time.Duration +} + +func New{{.ServiceName}}Handler(svc reference.{{.ServiceName}}Service) *{{.ServiceName}}Handler { + return &{{.ServiceName}}Handler{ + svc: svc, + timeout: time.Duration({{.Timeout}}) * time.Second, + } +} + +/* ===== ENDPOINTS ===== */ +{{range .Endpoints}} +{{/* ---------- GET ---------- */}} +{{if .HasGet}} +{{if $.HasSwagger}} +// @Summary {{.Summary}} +// @Description {{.Description}} +// @Tags {{ join .Tags "," }} +// @Accept json +// @Produce json +{{range .PathParams}} +// @Param {{.}} path string true "{{.}}" +{{end}} +{{if .RequireAuth}} +// @Security ApiKeyAuth +{{end}} +// @Success 200 {object} reference.{{.ResponseModel}} +// @Failure 400 {object} reference.ErrorResponse +// @Failure 500 {object} reference.ErrorResponse +// @Router {{.GetPath}} [get] +{{end}} +func (h *{{$.ServiceName}}Handler) Get{{.NameCamel}}(c *gin.Context) { + // ... (isi fungsi handler) +} +{{end}} +{{/* ---------- POST ---------- */}} +{{if .HasPost}} +{{if $.HasSwagger}} +// @Summary {{.Summary}} +// @Description {{.Description}} +// @Tags {{ join .Tags "," }} +// @Accept json +// @Produce json +{{if .RequireAuth}} +// @Security ApiKeyAuth +{{end}} +// @Param request body reference.{{.Model}} true "{{.Name}} data" +// @Success 201 {object} reference.{{.ResponseModel}} +// @Failure 400 {object} reference.ErrorResponse +// @Failure 500 {object} reference.ErrorResponse +// @Router {{.PostPath}} [post] +{{end}} +func (h *{{$.ServiceName}}Handler) Create{{.NameCamel}}(c *gin.Context) { + // ... (isi fungsi handler) +} +{{end}} +{{/* ---------- PUT ---------- */}} +{{if .HasPut}} +{{if $.HasSwagger}} +// @Summary Update {{.Name}} +// @Description {{.Description}} +// @Tags {{ join .Tags "," }} +// @Accept json +// @Produce json +{{range .PathParams}} +// @Param {{.}} path string true "{{.}}" +{{end}} +{{if .RequireAuth}} +// @Security ApiKeyAuth +{{end}} +// @Param request body reference.{{.Model}} true "{{.Name}} data" +// @Success 200 {object} reference.{{.ResponseModel}} +// @Failure 400 {object} reference.ErrorResponse +// @Failure 500 {object} reference.ErrorResponse +// @Router {{.PutPath}} [put] +{{end}} +func (h *{{$.ServiceName}}Handler) Update{{.NameCamel}}(c *gin.Context) { + // ... (isi fungsi handler) +} +{{end}} +{{/* ---------- PATCH ---------- */}} +{{if .HasPatch}} +{{if $.HasSwagger}} +// @Summary Patch {{.Name}} +// @Description {{.Description}} +// @Tags {{ join .Tags "," }} +// @Accept json +// @Produce json +{{range .PathParams}} +// @Param {{.}} path string true "{{.}}" +{{end}} +{{if .RequireAuth}} +// @Security ApiKeyAuth +{{end}} +// @Param request body reference.{{.Model}} true "Partial {{.Name}} data" +// @Success 200 {object} reference.{{.ResponseModel}} +// @Failure 400 {object} reference.ErrorResponse +// @Failure 500 {object} reference.ErrorResponse +// @Router {{.PatchPath}} [patch] +{{end}} +func (h *{{$.ServiceName}}Handler) Patch{{.NameCamel}}(c *gin.Context) { + // ... (isi fungsi handler) +} +{{end}} +{{/* ---------- DELETE ---------- */}} +{{if .HasDelete}} +{{if $.HasSwagger}} +// @Summary Delete {{.Name}} +// @Description {{.Description}} +// @Tags {{ join .Tags "," }} +// @Accept json +// @Produce json +{{range .PathParams}} +// @Param {{.}} path string true "{{.}}" +{{end}} +{{if .RequireAuth}} +// @Security ApiKeyAuth +{{end}} +// @Success 204 {object} nil +// @Failure 400 {object} reference.ErrorResponse +// @Failure 500 {object} reference.ErrorResponse +// @Router {{.DeletePath}} [delete] +{{end}} +func (h *{{$.ServiceName}}Handler) Delete{{.NameCamel}}(c *gin.Context) { + // ... (isi fungsi handler) +} +{{end}} +{{end}} + + +/* ===== HELPER FUNCTIONS ===== */ +func getOrSetRequestID(c *gin.Context) string { + id := c.GetHeader("X-Request-ID") + if id == "" { + id = uuid.New().String() + c.Header("X-Request-ID", id) + } + return id +} + +func errorJSON(c *gin.Context, code int, reqID string, err error) { + c.JSON(code, gin.H{ + "status": "error", + "message": err.Error(), + "request_id": reqID, + }) +} diff --git a/internal/handlers/vclaim_handler.go b/internal/handlers/vclaim_handler.go index 0110a12..b9ef4ad 100644 --- a/internal/handlers/vclaim_handler.go +++ b/internal/handlers/vclaim_handler.go @@ -1,401 +1,256 @@ -// Code generated by generate-dynamic-handler.go; DO NOT EDIT. -// Generated at: 2025-08-28 17:47:19 +// Code generated by generate-handler.go; DO NOT EDIT. +// Generated at: 2025-08-29 04:42:52 // Service: VClaim (vclaim) -// Description: BPJS VClaim service for eligibility and SEP management - package handlers import ( - "context" - "fmt" - "net/http" - "strconv" - "strings" - "time" - - "api-service/internal/config" - "api-service/internal/models/reference" - "api-service/internal/services/bpjs" - "api-service/pkg/logger" - - "github.com/gin-gonic/gin" - "github.com/go-playground/validator/v10" - "github.com/google/uuid" + "context" + "net/http" + "time" + "api-service/internal/models/reference" + "github.com/gin-gonic/gin" + "github.com/google/uuid" ) -// VClaimHandler handles VClaim BPJS services -type VClaimHandler struct { - service services.VClaimService - validator *validator.Validate - logger logger.Logger - config config.BpjsConfig -} +type VClaimHandler struct {} -// VClaimHandlerConfig contains configuration for VClaimHandler -type VClaimHandlerConfig struct { - BpjsConfig config.BpjsConfig - Logger logger.Logger - Validator *validator.Validate -} - -// NewVClaimHandler creates a new VClaimHandler -func NewVClaimHandler(cfg VClaimHandlerConfig) *VClaimHandler { - return &VClaimHandler{ - service: services.NewService(cfg.BpjsConfig), - validator: cfg.Validator, - logger: cfg.Logger, - config: cfg.BpjsConfig, - } +func NewVClaimHandler() *VClaimHandler { + return &VClaimHandler{} } -// GetPESERTA retrieves Peserta data - -// @Summary Get Peserta data +// +// @Summary Get Get Participant Info data // @Description Get participant eligibility information // @Tags vclaim,peserta // @Accept json // @Produce json +// @Param nokartu path string true "nokartu" // @Security ApiKeyAuth - -// @Param nokartu path string true "nokartu" - // @Success 200 {object} reference.PesertaResponse // @Failure 400 {object} reference.ErrorResponse // @Failure 500 {object} reference.ErrorResponse // @Router /Peserta/:nokartu [get] - -func (h *VClaimHandler) GetPESERTA(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) - defer cancel() - - // Generate request ID if not present - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } - - - h.logger.Info("Processing GetPeserta request", map[string]interface{}{ - "request_id": requestID, - "endpoint": "/Peserta/:nokartu", - - "nokartu": c.Param("nokartu"), - - }) - - - // Extract path parameters - - nokartu := c.Param("nokartu") - if nokartu == "" { - - h.logger.Error("Missing required parameter nokartu", map[string]interface{}{ - "request_id": requestID, - }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Missing required parameter nokartu", - RequestID: requestID, - }) - return - } - - - // Call service method - var response reference.PesertaResponse - - result, err := h.service.GetPESERTA(ctx, nokartu) - - if err != nil { - - h.logger.Error("Failed to get Peserta", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } - - // Ensure response has proper fields - response.Status = "success" - response.RequestID = requestID - c.JSON(http.StatusOK, response) +func (h *VClaimHandler) GetGet Participant Info(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), time.Duration(30)*time.Second) + defer cancel() + reqID := c.GetHeader("X-Request-ID") + if reqID == "" { + reqID = uuid.New().String() + c.Header("X-Request-ID", reqID) + } + + nokartu := c.Param("nokartu") + + // TODO: Panggil service layer, mapping response, error handling dsb. + c.JSON(http.StatusOK, gin.H{"status": "success", "request_id": reqID}) } -// GetSEP retrieves Sep data -// @Summary Get Sep data + +// +// @Summary Get SEP Management data // @Description Manage SEP (Surat Eligibilitas Peserta) // @Tags vclaim,sep // @Accept json // @Produce json +// @Param nosep path string true "nosep" // @Security ApiKeyAuth - -// @Param nosep path string true "nosep" - // @Success 200 {object} reference.SEPResponse // @Failure 400 {object} reference.ErrorResponse // @Failure 500 {object} reference.ErrorResponse // @Router /SEP/:nosep [get] - -func (h *VClaimHandler) GetSEP(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) - defer cancel() - - // Generate request ID if not present - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } - - - h.logger.Info("Processing GetSep request", map[string]interface{}{ - "request_id": requestID, - "endpoint": "/SEP/:nosep", - - "nosep": c.Param("nosep"), - - }) - - - // Extract path parameters - - nosep := c.Param("nosep") - if nosep == "" { - - h.logger.Error("Missing required parameter nosep", map[string]interface{}{ - "request_id": requestID, - }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Missing required parameter nosep", - RequestID: requestID, - }) - return - } - - - // Call service method - var response reference.SEPResponse - - result, err := h.service.GetSEP(ctx, nosep) - - if err != nil { - - h.logger.Error("Failed to get Sep", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } - - // Ensure response has proper fields - response.Status = "success" - response.RequestID = requestID - c.JSON(http.StatusOK, response) +func (h *VClaimHandler) GetSEP Management(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), time.Duration(30)*time.Second) + defer cancel() + reqID := c.GetHeader("X-Request-ID") + if reqID == "" { + reqID = uuid.New().String() + c.Header("X-Request-ID", reqID) + } + + nosep := c.Param("nosep") + + // TODO: Panggil service layer, mapping response, error handling dsb. + c.JSON(http.StatusOK, gin.H{"status": "success", "request_id": reqID}) } - -// CreateSEP creates new Sep - -// @Summary Create Sep +// +// @Summary Create SEP Management data // @Description Manage SEP (Surat Eligibilitas Peserta) // @Tags vclaim,sep // @Accept json // @Produce json - // @Security ApiKeyAuth -// @Param request body reference.SEPRequest true "Sep data" +// @Param request body reference.SEPRequest true "SEP Management data" // @Success 201 {object} reference.SEPResponse // @Failure 400 {object} reference.ErrorResponse // @Failure 500 {object} reference.ErrorResponse // @Router /sep [post] +func (h *VClaimHandler) CreateSEP Management(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), time.Duration(30)*time.Second) + defer cancel() + reqID := c.GetHeader("X-Request-ID") + if reqID == "" { + reqID = uuid.New().String() + c.Header("X-Request-ID", reqID) + } + var req reference.SEPRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"status": "error", "message": err.Error(), "request_id": reqID}) + return + } + // TODO: Panggil service layer, validasi, error handling dsb. + c.JSON(http.StatusCreated, gin.H{"status": "success", "request_id": reqID}) +} -func (h *VClaimHandler) CreateSEP(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) - defer cancel() - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } +// +// @Summary Update SEP Management data +// @Description Update Manage SEP (Surat Eligibilitas Peserta) +// @Tags vclaim,sep +// @Accept json +// @Produce json +// @Param nosep path string true "nosep" - - h.logger.Info("Processing CreateSep request", map[string]interface{}{ - "request_id": requestID, - }) - +// @Security ApiKeyAuth - var req reference.SEPRequest - if err := c.ShouldBindJSON(&req); err != nil { - - h.logger.Error("Invalid request body", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Invalid request body: " + err.Error(), - RequestID: requestID, - }) - return - } +// @Param request body reference.SEPRequest true "SEP Management data" +// @Success 200 {object} reference.SEPResponse +// @Failure 400 {object} reference.ErrorResponse +// @Failure 500 {object} reference.ErrorResponse +// @Router /sep/:nosep [put] +func (h *VClaimHandler) UpdateSEP Management(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } - // Validate request - if err := h.validator.Struct(&req); err != nil { - - h.logger.Error("Validation failed", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Validation failed: " + err.Error(), - RequestID: requestID, - }) - return - } + + nosep := c.Param("nosep") + if nosep == "" { + c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + Status: "error", + Message: "Missing required parameter nosep", + RequestID: requestID, + }) + return + } + - // Call service method - var response reference.SEPResponse - result, err := h.service.CreateSEP(ctx, &req) - if err != nil { - - h.logger.Error("Failed to create Sep", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } + var req reference.SEPRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + Status: "error", + Message: "Invalid request body: " + err.Error(), + RequestID: requestID, + }) + return + } - // Ensure response has proper fields - response.Status = "success" - response.RequestID = requestID - c.JSON(http.StatusCreated, response) + // TODO: Validasi, panggil service update + // result, err := h.service.UpdateSEP Management(ctx, nosep, &req) + // if err != nil { + // c.JSON(http.StatusInternalServerError, ...) + // return + // } + c.JSON(http.StatusOK, gin.H{"status": "success", "request_id": requestID}) } -// GetRUJUKAN retrieves Rujukan data +// +// @Summary Delete SEP Management data +// @Description Delete Manage SEP (Surat Eligibilitas Peserta) +// @Tags vclaim,sep +// @Accept json +// @Produce json +// @Param nosep path string true "nosep" -// @Summary Get Rujukan data +// @Security ApiKeyAuth + +// @Success 204 {object} nil +// @Failure 400 {object} reference.ErrorResponse +// @Failure 500 {object} reference.ErrorResponse +// @Router /sep/:nosep [delete] +func (h *VClaimHandler) DeleteSEP Management(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + + nosep := c.Param("nosep") + if nosep == "" { + c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + Status: "error", + Message: "Missing required parameter nosep", + RequestID: requestID, + }) + return + } + + + // TODO: Panggil service delete + // err := h.service.DeleteSEP Management(ctx, nosep, ) + // if err != nil { + // c.JSON(http.StatusInternalServerError, ...) + // return + // } + c.Status(http.StatusNoContent) +} + + + +// +// @Summary Get Get Referral Info data // @Description Get referral information // @Tags vclaim,rujukan // @Accept json // @Produce json +// @Param norujukan path string true "norujukan" // @Security ApiKeyAuth - -// @Param norujukan path string true "norujukan" - // @Success 200 {object} reference.RujukanResponse // @Failure 400 {object} reference.ErrorResponse // @Failure 500 {object} reference.ErrorResponse // @Router /rujukan/:norujukan [get] - -func (h *VClaimHandler) GetRUJUKAN(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) - defer cancel() - - // Generate request ID if not present - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } - - - h.logger.Info("Processing GetRujukan request", map[string]interface{}{ - "request_id": requestID, - "endpoint": "/rujukan/:norujukan", - - "norujukan": c.Param("norujukan"), - - }) - - - // Extract path parameters - - norujukan := c.Param("norujukan") - if norujukan == "" { - - h.logger.Error("Missing required parameter norujukan", map[string]interface{}{ - "request_id": requestID, - }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Missing required parameter norujukan", - RequestID: requestID, - }) - return - } - - - // Call service method - var response reference.RujukanResponse - - result, err := h.service.GetRUJUKAN(ctx, norujukan) - - if err != nil { - - h.logger.Error("Failed to get Rujukan", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } - - // Ensure response has proper fields - response.Status = "success" - response.RequestID = requestID - c.JSON(http.StatusOK, response) +func (h *VClaimHandler) GetGet Referral Info(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), time.Duration(30)*time.Second) + defer cancel() + reqID := c.GetHeader("X-Request-ID") + if reqID == "" { + reqID = uuid.New().String() + c.Header("X-Request-ID", reqID) + } + + norujukan := c.Param("norujukan") + + // TODO: Panggil service layer, mapping response, error handling dsb. + c.JSON(http.StatusOK, gin.H{"status": "success", "request_id": reqID}) } + + diff --git a/services-config-bpjs.yaml b/services-config-bpjs.yaml index ad0ee5e..6f8aba7 100644 --- a/services-config-bpjs.yaml +++ b/services-config-bpjs.yaml @@ -67,83 +67,4 @@ services: cache_enabled: true cache_ttl: 600 - eclaim: - name: "EClaim" - category: "eclaim" - package: "eclaim" - description: "BPJS EClaim service for claim processing and grouper" - base_url: "https://apijkn.bpjs-kesehatan.go.id/new-eclaim-rest" - timeout: 60 - retry_count: 2 - middleware: - - "RequestLogger" - - "ResponseLogger" - - "ClaimValidator" - dependencies: - - "database" - - "grouper_service" - endpoints: - klaim: - methods: ["GET", "POST", "PUT"] - get_path: "/klaim/:noklaim" - post_path: "/klaim" - put_path: "/klaim/:noklaim" - model: "KlaimRequest" - response_model: "KlaimResponse" - description: "Manage insurance claims" - summary: "Claim Management" - tags: ["eclaim", "klaim"] - require_auth: true - rate_limit: 30 - cache_enabled: false - grouper: - methods: ["POST"] - post_path: "/grouper" - model: "GrouperRequest" - response_model: "GrouperResponse" - description: "Process claim grouping and pricing" - summary: "Claim Grouper" - tags: ["eclaim", "grouper"] - require_auth: true - rate_limit: 20 - cache_enabled: true - cache_ttl: 120 - - aplicare: - name: "Aplicare" - category: "aplicare" - package: "aplicare" - description: "BPJS Aplicare service for reference data and monitoring" - base_url: "https://apijkn.bpjs-kesehatan.go.id/aplicaresws" - timeout: 45 - retry_count: 3 - middleware: - - "RequestLogger" - - "ResponseLogger" - dependencies: - - "database" - endpoints: - referensi: - methods: ["GET"] - get_path: "/referensi/:jenis" - model: "ReferensiRequest" - response_model: "ReferensiResponse" - description: "Get reference data (diagnoses, procedures, etc.)" - summary: "Get Reference Data" - tags: ["aplicare", "referensi"] - require_auth: true - rate_limit: 200 - cache_enabled: true - cache_ttl: 3600 - monitoring: - methods: ["GET", "POST"] - get_path: "/monitoring/:tanggal" - post_path: "/monitoring" - model: "MonitoringRequest" - response_model: "MonitoringResponse" - description: "Healthcare monitoring and reporting" - summary: "Monitoring Data" - tags: ["aplicare", "monitoring"] - require_auth: true - rate_limit: 50 - cache_enabled: false + diff --git a/test b/test new file mode 100644 index 0000000..ad0ee5e --- /dev/null +++ b/test @@ -0,0 +1,149 @@ +# services-config-complete.yaml +global: + module_name: "api-service" + output_dir: "internal/handlers" + package_prefix: "api-service" + enable_swagger: true + enable_logging: true + enable_metrics: true + +services: + vclaim: + name: "VClaim" + category: "vclaim" + package: "vclaim" + description: "BPJS VClaim service for eligibility and SEP management" + base_url: "https://apijkn.bpjs-kesehatan.go.id/vclaim-rest" + timeout: 30 + retry_count: 3 + middleware: + - "RequestLogger" + - "ResponseLogger" + - "RateLimiter" + dependencies: + - "database" + - "redis" + endpoints: + peserta: + methods: ["GET"] + get_path: "/Peserta/:nokartu" + model: "PesertaRequest" + response_model: "PesertaResponse" + description: "Get participant eligibility information" + summary: "Get Participant Info" + tags: ["vclaim", "peserta"] + require_auth: true + rate_limit: 100 + cache_enabled: true + cache_ttl: 300 + sep: + methods: ["GET", "POST", "PUT", "DELETE"] + get_path: "/SEP/:nosep" + post_path: "/sep" + put_path: "/sep/:nosep" + delete_path: "/sep/:nosep" + model: "SEPRequest" + response_model: "SEPResponse" + description: "Manage SEP (Surat Eligibilitas Peserta)" + summary: "SEP Management" + tags: ["vclaim", "sep"] + require_auth: true + rate_limit: 50 + cache_enabled: true + cache_ttl: 180 + custom_headers: + X-Service: "VClaim" + X-Version: "2.0" + rujukan: + methods: ["GET"] + get_path: "/rujukan/:norujukan" + model: "RujukanRequest" + response_model: "RujukanResponse" + description: "Get referral information" + summary: "Get Referral Info" + tags: ["vclaim", "rujukan"] + require_auth: true + rate_limit: 100 + cache_enabled: true + cache_ttl: 600 + + eclaim: + name: "EClaim" + category: "eclaim" + package: "eclaim" + description: "BPJS EClaim service for claim processing and grouper" + base_url: "https://apijkn.bpjs-kesehatan.go.id/new-eclaim-rest" + timeout: 60 + retry_count: 2 + middleware: + - "RequestLogger" + - "ResponseLogger" + - "ClaimValidator" + dependencies: + - "database" + - "grouper_service" + endpoints: + klaim: + methods: ["GET", "POST", "PUT"] + get_path: "/klaim/:noklaim" + post_path: "/klaim" + put_path: "/klaim/:noklaim" + model: "KlaimRequest" + response_model: "KlaimResponse" + description: "Manage insurance claims" + summary: "Claim Management" + tags: ["eclaim", "klaim"] + require_auth: true + rate_limit: 30 + cache_enabled: false + grouper: + methods: ["POST"] + post_path: "/grouper" + model: "GrouperRequest" + response_model: "GrouperResponse" + description: "Process claim grouping and pricing" + summary: "Claim Grouper" + tags: ["eclaim", "grouper"] + require_auth: true + rate_limit: 20 + cache_enabled: true + cache_ttl: 120 + + aplicare: + name: "Aplicare" + category: "aplicare" + package: "aplicare" + description: "BPJS Aplicare service for reference data and monitoring" + base_url: "https://apijkn.bpjs-kesehatan.go.id/aplicaresws" + timeout: 45 + retry_count: 3 + middleware: + - "RequestLogger" + - "ResponseLogger" + dependencies: + - "database" + endpoints: + referensi: + methods: ["GET"] + get_path: "/referensi/:jenis" + model: "ReferensiRequest" + response_model: "ReferensiResponse" + description: "Get reference data (diagnoses, procedures, etc.)" + summary: "Get Reference Data" + tags: ["aplicare", "referensi"] + require_auth: true + rate_limit: 200 + cache_enabled: true + cache_ttl: 3600 + monitoring: + methods: ["GET", "POST"] + get_path: "/monitoring/:tanggal" + post_path: "/monitoring" + model: "MonitoringRequest" + response_model: "MonitoringResponse" + description: "Healthcare monitoring and reporting" + summary: "Monitoring Data" + tags: ["aplicare", "monitoring"] + require_auth: true + rate_limit: 50 + cache_enabled: false diff --git a/tools/bpjs/generate-handler.go b/tools/bpjs/generate-handler.go index ada9d50..213f600 100644 --- a/tools/bpjs/generate-handler.go +++ b/tools/bpjs/generate-handler.go @@ -1,24 +1,25 @@ package main import ( + "flag" "fmt" "io/ioutil" "os" "path/filepath" "strings" + "sync" "text/template" "time" "gopkg.in/yaml.v2" ) -// ServiceConfig represents the main configuration structure +/* ---------- Model Konfig ---------- */ type ServiceConfig struct { Services map[string]Service `yaml:"services"` Global GlobalConfig `yaml:"global,omitempty"` } -// GlobalConfig contains global configuration type GlobalConfig struct { ModuleName string `yaml:"module_name"` OutputDir string `yaml:"output_dir"` @@ -28,7 +29,6 @@ type GlobalConfig struct { EnableMetrics bool `yaml:"enable_metrics"` } -// Service represents individual service configuration type Service struct { Name string `yaml:"name"` Category string `yaml:"category"` @@ -38,11 +38,10 @@ type Service struct { Timeout int `yaml:"timeout"` RetryCount int `yaml:"retry_count"` Endpoints map[string]Endpoint `yaml:"endpoints"` - Middleware []string `yaml:"middleware,omitempty"` // FIXED: Changed to []string - Dependencies []string `yaml:"dependencies,omitempty"` // FIXED: Changed to []string + Middleware []string `yaml:"middleware,omitempty"` + Dependencies []string `yaml:"dependencies,omitempty"` } -// Endpoint represents endpoint configuration type Endpoint struct { Methods []string `yaml:"methods"` GetPath string `yaml:"get_path,omitempty"` @@ -59,10 +58,11 @@ type Endpoint struct { RateLimit int `yaml:"rate_limit,omitempty"` CacheEnabled bool `yaml:"cache_enabled"` CacheTTL int `yaml:"cache_ttl,omitempty"` - CustomHeaders map[string]string `yaml:"custom_headers,omitempty"` // ADDED: Missing field + CustomHeaders map[string]string `yaml:"custom_headers,omitempty"` } -// TemplateData holds data for generating handlers +/* ---------- Data Template ---------- */ + type TemplateData struct { ServiceName string ServiceLower string @@ -82,342 +82,338 @@ type TemplateData struct { HasSwagger bool HasAuth bool HasCache bool - Dependencies []string // FIXED: Changed to []string - Middleware []string // FIXED: Changed to []string + Dependencies []string + Middleware []string GlobalConfig GlobalConfig } -// EndpointData represents processed endpoint data type EndpointData struct { - Name string - NameLower string - NameUpper string - NameCamel string - Methods []string - GetPath string - PostPath string - PutPath string - DeletePath string - PatchPath string - Model string - ResponseModel string - Description string - Summary string - Tags []string - HasGet bool - HasPost bool - HasPut bool - HasDelete bool - HasPatch bool - RequireAuth bool - RateLimit int - CacheEnabled bool - CacheTTL int - PathParams []string - QueryParams []string - RequiredFields []string - OptionalFields []string - CustomHeaders map[string]string + Name string + NameLower string + NameUpper string + NameCamel string + Methods []string + GetPath string + PostPath string + PutPath string + DeletePath string + PatchPath string + Model string + ResponseModel string + Description string + Summary string + Tags []string + HasGet bool + HasPost bool + HasPut bool + HasDelete bool + HasPatch bool + RequireAuth bool + RateLimit int + CacheEnabled bool + CacheTTL int + PathParams []string + CustomHeaders map[string]string } -// Template remains the same as before... +/* ---------- Template Handler ---------- */ + const handlerTemplate = ` -// Code generated by generate-dynamic-handler.go; DO NOT EDIT. +// Code generated by generate-handler.go; DO NOT EDIT. // Generated at: {{.Timestamp}} // Service: {{.ServiceName}} ({{.Category}}) -// Description: {{.Description}} - package handlers import ( - "context" - "fmt" - "net/http" - "strconv" - "strings" - "time" - - "{{.ModuleName}}/internal/config" - "{{.ModuleName}}/internal/models/reference" - "{{.ModuleName}}/internal/services/bpjs" - "{{.ModuleName}}/pkg/logger" - - "github.com/gin-gonic/gin" - "github.com/go-playground/validator/v10" - "github.com/google/uuid" + "context" + "net/http" + "time" + "{{.ModuleName}}/internal/models/reference" + "github.com/gin-gonic/gin" + "github.com/google/uuid" ) -// {{.ServiceName}}Handler handles {{.ServiceName}} BPJS services -type {{.ServiceName}}Handler struct { - service services.{{.ServiceName}}Service - validator *validator.Validate - logger logger.Logger - config config.BpjsConfig -} +type {{.ServiceName}}Handler struct {} -// {{.ServiceName}}HandlerConfig contains configuration for {{.ServiceName}}Handler -type {{.ServiceName}}HandlerConfig struct { - BpjsConfig config.BpjsConfig - Logger logger.Logger - Validator *validator.Validate -} - -// New{{.ServiceName}}Handler creates a new {{.ServiceName}}Handler -func New{{.ServiceName}}Handler(cfg {{.ServiceName}}HandlerConfig) *{{.ServiceName}}Handler { - return &{{.ServiceName}}Handler{ - service: services.NewService(cfg.BpjsConfig), - validator: cfg.Validator, - logger: cfg.Logger, - config: cfg.BpjsConfig, - } +func New{{.ServiceName}}Handler() *{{.ServiceName}}Handler { + return &{{.ServiceName}}Handler{} } {{range .Endpoints}} {{if .HasGet}} -// Get{{.NameUpper}} retrieves {{.Name}} data -{{if $.HasSwagger}} +// // @Summary Get {{.Name}} data // @Description {{.Description}} // @Tags {{join .Tags ","}} // @Accept json // @Produce json -{{if .RequireAuth}} -// @Security ApiKeyAuth +{{range .PathParams}}// @Param {{.}} path string true "{{.}}" {{end}} -{{range .PathParams}} -// @Param {{.}} path string true "{{.}}" +{{if .RequireAuth}}// @Security ApiKeyAuth {{end}} // @Success 200 {object} reference.{{.ResponseModel}} // @Failure 400 {object} reference.ErrorResponse // @Failure 500 {object} reference.ErrorResponse // @Router {{.GetPath}} [get] -{{end}} -func (h *{{$.ServiceName}}Handler) Get{{.NameUpper}}(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second) - defer cancel() - - // Generate request ID if not present - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - 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, - "endpoint": "{{.GetPath}}", - {{range .PathParams}} - "{{.}}": c.Param("{{.}}"), - {{end}} - }) - {{end}} - - // Extract path parameters - {{range .PathParams}} - {{.}} := c.Param("{{.}}") - if {{.}} == "" { - {{if $.HasLogger}} - h.logger.Error("Missing required parameter {{.}}", map[string]interface{}{ - "request_id": requestID, - }) - {{end}} - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Missing required parameter {{.}}", - RequestID: requestID, - }) - return - } - {{end}} - - // Call service method - var response reference.{{.ResponseModel}} - {{if .PathParams}} - result, err := h.service.Get{{.NameUpper}}(ctx{{range .PathParams}}, {{.}}{{end}}) - {{else}} - result, err := h.service.Get{{.NameUpper}}(ctx) - {{end}} - if err != nil { - {{if $.HasLogger}} - h.logger.Error("Failed to get {{.Name}}", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - {{end}} - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } - - // Ensure response has proper fields - response.Status = "success" - response.RequestID = requestID - c.JSON(http.StatusOK, response) +func (h *{{$.ServiceName}}Handler) Get{{.Name}}(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), time.Duration({{$.Timeout}})*time.Second) + defer cancel() + reqID := c.GetHeader("X-Request-ID") + if reqID == "" { + reqID = uuid.New().String() + c.Header("X-Request-ID", reqID) + } + {{range .PathParams}} + {{.}} := c.Param("{{.}}") + {{end}} + // TODO: Panggil service layer, mapping response, error handling dsb. + c.JSON(http.StatusOK, gin.H{"status": "success", "request_id": reqID}) } {{end}} - {{if .HasPost}} -// Create{{.NameUpper}} creates new {{.Name}} -{{if $.HasSwagger}} -// @Summary Create {{.Name}} +// +// @Summary Create {{.Name}} data // @Description {{.Description}} // @Tags {{join .Tags ","}} // @Accept json // @Produce json -{{if .RequireAuth}} -// @Security ApiKeyAuth +{{if .RequireAuth}}// @Security ApiKeyAuth {{end}} // @Param request body reference.{{.Model}} true "{{.Name}} data" // @Success 201 {object} reference.{{.ResponseModel}} // @Failure 400 {object} reference.ErrorResponse // @Failure 500 {object} reference.ErrorResponse // @Router {{.PostPath}} [post] +func (h *{{$.ServiceName}}Handler) Create{{.Name}}(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), time.Duration({{$.Timeout}})*time.Second) + defer cancel() + reqID := c.GetHeader("X-Request-ID") + if reqID == "" { + reqID = uuid.New().String() + c.Header("X-Request-ID", reqID) + } + var req reference.{{.Model}} + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"status": "error", "message": err.Error(), "request_id": reqID}) + return + } + // TODO: Panggil service layer, validasi, error handling dsb. + c.JSON(http.StatusCreated, gin.H{"status": "success", "request_id": reqID}) +} {{end}} -func (h *{{$.ServiceName}}Handler) Create{{.NameUpper}}(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second) - defer cancel() +{{if .HasPut}} +// +// @Summary Update {{.Name}} data +// @Description Update {{.Description}} +// @Tags {{join .Tags ","}} +// @Accept json +// @Produce json +{{range .PathParams}}// @Param {{.}} path string true "{{.}}" +{{end}} +{{if .RequireAuth}}// @Security ApiKeyAuth +{{end}} +// @Param request body reference.{{.Model}} true "{{.Name}} data" +// @Success 200 {object} reference.{{.ResponseModel}} +// @Failure 400 {object} reference.ErrorResponse +// @Failure 500 {object} reference.ErrorResponse +// @Router {{.PutPath}} [put] +func (h *{{$.ServiceName}}Handler) Update{{.Name}}(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second) + defer cancel() + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } + {{range .PathParams}} + {{.}} := c.Param("{{.}}") + if {{.}} == "" { + c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + Status: "error", + Message: "Missing required parameter {{.}}", + RequestID: requestID, + }) + return + } + {{end}} - {{if $.HasLogger}} - h.logger.Info("Processing Create{{.Name}} request", map[string]interface{}{ - "request_id": requestID, - }) - {{end}} + var req reference.{{.Model}} + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + Status: "error", + Message: "Invalid request body: " + err.Error(), + RequestID: requestID, + }) + return + } - var req reference.{{.Model}} - if err := c.ShouldBindJSON(&req); err != nil { - {{if $.HasLogger}} - h.logger.Error("Invalid request body", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - {{end}} - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Invalid request body: " + err.Error(), - RequestID: requestID, - }) - return - } + // TODO: Validasi, panggil service update + // result, err := h.service.Update{{.Name}}(ctx, {{range .PathParams}}{{.}}, {{end}}&req) + // if err != nil { + // c.JSON(http.StatusInternalServerError, ...) + // return + // } + c.JSON(http.StatusOK, gin.H{"status": "success", "request_id": requestID}) +} +{{end}} +{{if .HasPatch}} +// +// @Summary Patch {{.Name}} data +// @Description Patch {{.Description}} +// @Tags {{join .Tags ","}} +// @Accept json +// @Produce json +{{range .PathParams}}// @Param {{.}} path string true "{{.}}" +{{end}} +{{if .RequireAuth}}// @Security ApiKeyAuth +{{end}} +// @Param request body reference.{{.Model}} true "Partial {{.Name}} data" +// @Success 200 {object} reference.{{.ResponseModel}} +// @Failure 400 {object} reference.ErrorResponse +// @Failure 500 {object} reference.ErrorResponse +// @Router {{.PatchPath}} [patch] +func (h *{{$.ServiceName}}Handler) Patch{{.Name}}(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second) + defer cancel() + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } - // Validate request - if err := h.validator.Struct(&req); err != nil { - {{if $.HasLogger}} - h.logger.Error("Validation failed", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - {{end}} - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Validation failed: " + err.Error(), - RequestID: requestID, - }) - return - } + {{range .PathParams}} + {{.}} := c.Param("{{.}}") + if {{.}} == "" { + c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + Status: "error", + Message: "Missing required parameter {{.}}", + RequestID: requestID, + }) + return + } + {{end}} - // Call service method - var response reference.{{.ResponseModel}} - result, err := h.service.Create{{.NameUpper}}(ctx, &req) - if err != nil { - {{if $.HasLogger}} - h.logger.Error("Failed to create {{.Name}}", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - {{end}} - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } + var req reference.{{.Model}} + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + Status: "error", + Message: "Invalid request body: " + err.Error(), + RequestID: requestID, + }) + return + } - // Ensure response has proper fields - response.Status = "success" - response.RequestID = requestID - c.JSON(http.StatusCreated, response) + // TODO: Validasi, panggil service patch + // result, err := h.service.Patch{{.Name}}(ctx, {{range .PathParams}}{{.}}, {{end}}&req) + // if err != nil { + // c.JSON(http.StatusInternalServerError, ...) + // return + // } + c.JSON(http.StatusOK, gin.H{"status": "success", "request_id": requestID}) +} +{{end}} +{{if .HasDelete}} +// +// @Summary Delete {{.Name}} data +// @Description Delete {{.Description}} +// @Tags {{join .Tags ","}} +// @Accept json +// @Produce json +{{range .PathParams}}// @Param {{.}} path string true "{{.}}" +{{end}} +{{if .RequireAuth}}// @Security ApiKeyAuth +{{end}} +// @Success 204 {object} nil +// @Failure 400 {object} reference.ErrorResponse +// @Failure 500 {object} reference.ErrorResponse +// @Router {{.DeletePath}} [delete] +func (h *{{$.ServiceName}}Handler) Delete{{.Name}}(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second) + defer cancel() + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + {{range .PathParams}} + {{.}} := c.Param("{{.}}") + if {{.}} == "" { + c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + Status: "error", + Message: "Missing required parameter {{.}}", + RequestID: requestID, + }) + return + } + {{end}} + + // TODO: Panggil service delete + // err := h.service.Delete{{.Name}}(ctx, {{range .PathParams}}{{.}}, {{end}}) + // if err != nil { + // c.JSON(http.StatusInternalServerError, ...) + // return + // } + c.Status(http.StatusNoContent) } {{end}} {{end}} ` +/* ---------- Main ---------- */ + func main() { - if len(os.Args) < 2 { - printUsage() + flag.Usage = usage + outFlag := flag.String("o", "", "override output directory") + serviceFlag := flag.String("s", "", "generate only selected service name") + tmplFlag := flag.String("tmpl", "", "custom handler template path") + flag.Parse() + + if flag.NArg() < 1 { + usage() os.Exit(1) } - configFile := os.Args[1] - var targetService string - if len(os.Args) > 2 { - targetService = os.Args[2] - } - - // Load configuration + configFile := flag.Arg(0) config, err := loadConfig(configFile) if err != nil { fmt.Printf("āŒ Error loading config: %v\n", err) os.Exit(1) } - fmt.Println("šŸš€ Starting BPJS Dynamic Handler Generation...") - fmt.Printf("šŸ“ Config file: %s\n", configFile) - if targetService != "" { - fmt.Printf("šŸŽÆ Target service: %s\n", targetService) + outDir := firstNonEmpty(*outFlag, config.Global.OutputDir, "internal/handlers") + templatePath := firstNonEmpty(*tmplFlag, "") + + // Template setup + var tmpl *template.Template + if templatePath != "" { + tmpl, err = template.New("handler").Funcs(templateFuncMap()).ParseFiles(templatePath) + } else { + tmpl, err = template.New("handler").Funcs(templateFuncMap()).Parse(handlerTemplate) + } + if err != nil { + fmt.Printf("āŒ Error parsing template: %v\n", err) + os.Exit(1) } - generated := 0 - errors := 0 - - // Generate handlers - for serviceName, service := range config.Services { - if targetService != "" && serviceName != targetService { + var wg sync.WaitGroup + for svcName, svc := range config.Services { + if *serviceFlag != "" && svcName != *serviceFlag { continue } - - fmt.Printf("šŸ”§ Generating handler for service: %s (%s)\n", service.Name, service.Category) - err := generateHandler(serviceName, service, config.Global) - if err != nil { - fmt.Printf("āŒ Error generating handler for %s: %v\n", serviceName, err) - errors++ - continue - } - fmt.Printf("āœ… Generated handler: %s_handler.go\n", strings.ToLower(serviceName)) - generated++ + wg.Add(1) + go func(serviceName string, service Service) { + defer wg.Done() + err := generateHandler(serviceName, service, config.Global, outDir, tmpl) + if err != nil { + fmt.Printf("āŒ Error generating handler for %s: %v\n", serviceName, err) + } else { + fmt.Printf("āœ… Generated handler: %s_handler.go\n", strings.ToLower(serviceName)) + } + }(svcName, svc) } - - // Summary - fmt.Println("\nšŸ“Š Generation Summary:") - fmt.Printf(" āœ… Successfully generated: %d handlers\n", generated) - if errors > 0 { - fmt.Printf(" āŒ Failed: %d handlers\n", errors) - } - - if generated > 0 { - fmt.Println("šŸŽ‰ Generation completed successfully!") - } -} - -func printUsage() { - fmt.Println("BPJS Dynamic Handler Generator") - fmt.Println() - fmt.Println("Usage:") - fmt.Println(" go run generate-dynamic-handler.go [service-name]") - fmt.Println() - fmt.Println("Examples:") - fmt.Println(" go run generate-dynamic-handler.go services-config.yaml") - fmt.Println(" go run generate-dynamic-handler.go services-config.yaml vclaim") + wg.Wait() } func loadConfig(filename string) (*ServiceConfig, error) { @@ -431,140 +427,104 @@ func loadConfig(filename string) (*ServiceConfig, error) { if err != nil { return nil, fmt.Errorf("failed to parse YAML config: %w", err) } - - // Set default values if config.Global.ModuleName == "" { config.Global.ModuleName = "api-service" } if config.Global.OutputDir == "" { config.Global.OutputDir = "internal/handlers" } - return &config, nil } -func generateHandler(serviceName string, service Service, globalConfig GlobalConfig) error { - // Prepare template data - templateData := TemplateData{ - ServiceName: service.Name, - ServiceLower: strings.ToLower(service.Name), - ServiceUpper: strings.ToUpper(service.Name), - Category: service.Category, - Package: service.Package, - Description: service.Description, - BaseURL: service.BaseURL, - Timeout: getOrDefault(service.Timeout, 30), - RetryCount: getOrDefault(service.RetryCount, 3), +func generateHandler(serviceName string, service Service, globalConfig GlobalConfig, outDir string, tmpl *template.Template) error { + td := prepareTemplateData(serviceName, service, globalConfig) + if err := os.MkdirAll(outDir, 0o755); err != nil { + return fmt.Errorf("failed to create output directory: %w", err) + } + filename := filepath.Join(outDir, fmt.Sprintf("%s_handler.go", td.ServiceLower)) + file, err := os.Create(filename) + if err != nil { + return fmt.Errorf("failed to create handler file: %w", err) + } + defer file.Close() + return tmpl.Execute(file, td) +} + +func prepareTemplateData(serviceName string, svc Service, globalConfig GlobalConfig) TemplateData { + td := TemplateData{ + ServiceName: svc.Name, + ServiceLower: strings.ToLower(svc.Name), + ServiceUpper: strings.ToUpper(svc.Name), + Category: svc.Category, + Package: svc.Package, + Description: svc.Description, + BaseURL: svc.BaseURL, + Timeout: getOrDefault(svc.Timeout, 30), + RetryCount: getOrDefault(svc.RetryCount, 3), Timestamp: time.Now().Format("2006-01-02 15:04:05"), ModuleName: globalConfig.ModuleName, HasValidator: true, HasLogger: globalConfig.EnableLogging, HasMetrics: globalConfig.EnableMetrics, HasSwagger: globalConfig.EnableSwagger, - Dependencies: service.Dependencies, // Now []string - Middleware: service.Middleware, // Now []string + Dependencies: svc.Dependencies, + Middleware: svc.Middleware, GlobalConfig: globalConfig, } - - // Check for advanced features - for _, endpoint := range service.Endpoints { - if endpoint.RequireAuth { - templateData.HasAuth = true + for _, ep := range svc.Endpoints { + ed := processEndpoint(ep) + if ed.RequireAuth { + td.HasAuth = true } - if endpoint.CacheEnabled { - templateData.HasCache = true + if ed.CacheEnabled { + td.HasCache = true } + td.Endpoints = append(td.Endpoints, ed) } - - // Process endpoints - for endpointName, endpoint := range service.Endpoints { - endpointData := processEndpoint(endpointName, endpoint) - templateData.Endpoints = append(templateData.Endpoints, endpointData) - } - - // Create output directory - outputDir := globalConfig.OutputDir - if outputDir == "" { - outputDir = "internal/handlers" - } - if err := os.MkdirAll(outputDir, 0755); err != nil { - return fmt.Errorf("failed to create output directory: %w", err) - } - - // Generate handler file - filename := filepath.Join(outputDir, fmt.Sprintf("%s_handler.go", strings.ToLower(serviceName))) - - // Create template with custom functions - tmpl := template.New("handler").Funcs(template.FuncMap{ - "contains": strings.Contains, - "join": strings.Join, - "title": strings.Title, - "trimPrefix": strings.TrimPrefix, - }) - - tmpl, err := tmpl.Parse(handlerTemplate) - if err != nil { - return fmt.Errorf("failed to parse template: %w", err) - } - - file, err := os.Create(filename) - if err != nil { - return fmt.Errorf("failed to create file: %w", err) - } - defer file.Close() - - err = tmpl.Execute(file, templateData) - if err != nil { - return fmt.Errorf("failed to execute template: %w", err) - } - - return nil + return td } -func processEndpoint(name string, endpoint Endpoint) EndpointData { +func processEndpoint(ep Endpoint) EndpointData { data := EndpointData{ - Name: strings.Title(name), - NameLower: strings.ToLower(name), - NameUpper: strings.ToUpper(name), - NameCamel: toCamelCase(name), - Methods: endpoint.Methods, - GetPath: endpoint.GetPath, - PostPath: endpoint.PostPath, - PutPath: endpoint.PutPath, - DeletePath: endpoint.DeletePath, - PatchPath: endpoint.PatchPath, - Model: endpoint.Model, - ResponseModel: endpoint.ResponseModel, - Description: endpoint.Description, - Summary: endpoint.Summary, - Tags: endpoint.Tags, - RequireAuth: endpoint.RequireAuth, - RateLimit: endpoint.RateLimit, - CacheEnabled: endpoint.CacheEnabled, - CacheTTL: getOrDefault(endpoint.CacheTTL, 300), - CustomHeaders: endpoint.CustomHeaders, + Name: strings.Title(ep.Summary), + NameLower: strings.ToLower(ep.Summary), + NameUpper: strings.ToUpper(ep.Summary), + NameCamel: toCamelCase(ep.Summary), + Methods: ep.Methods, + GetPath: ep.GetPath, + PostPath: ep.PostPath, + PutPath: ep.PutPath, + DeletePath: ep.DeletePath, + PatchPath: ep.PatchPath, + Model: ep.Model, + ResponseModel: ep.ResponseModel, + Description: ep.Description, + Summary: ep.Summary, + Tags: ep.Tags, + RequireAuth: ep.RequireAuth, + RateLimit: ep.RateLimit, + CacheEnabled: ep.CacheEnabled, + CacheTTL: getOrDefault(ep.CacheTTL, 300), + CustomHeaders: ep.CustomHeaders, } - - // Set method flags and extract path parameters - for _, method := range endpoint.Methods { + for _, method := range ep.Methods { switch strings.ToUpper(method) { case "GET": data.HasGet = true - data.PathParams = extractPathParams(endpoint.GetPath) + data.PathParams = extractPathParams(ep.GetPath) case "POST": data.HasPost = true case "PUT": data.HasPut = true - data.PathParams = extractPathParams(endpoint.PutPath) + data.PathParams = extractPathParams(ep.PutPath) case "DELETE": data.HasDelete = true - data.PathParams = extractPathParams(endpoint.DeletePath) + data.PathParams = extractPathParams(ep.DeletePath) case "PATCH": data.HasPatch = true - data.PathParams = extractPathParams(endpoint.PatchPath) + data.PathParams = extractPathParams(ep.PatchPath) } } - return data } @@ -572,7 +532,6 @@ func extractPathParams(path string) []string { if path == "" { return nil } - var params []string parts := strings.Split(path, "/") for _, part := range parts { @@ -580,7 +539,6 @@ func extractPathParams(path string) []string { params = append(params, strings.TrimPrefix(part, ":")) } } - return params } @@ -588,16 +546,13 @@ func toCamelCase(str string) string { words := strings.FieldsFunc(str, func(c rune) bool { return c == '_' || c == '-' || c == ' ' }) - if len(words) == 0 { return str } - - result := strings.ToLower(words[0]) + result := strings.Title(strings.ToLower(words[0])) for _, word := range words[1:] { result += strings.Title(strings.ToLower(word)) } - return result } @@ -607,3 +562,32 @@ func getOrDefault(value, defaultValue int) int { } return value } + +func firstNonEmpty(vals ...string) string { + for _, v := range vals { + if v != "" { + return v + } + } + return "" +} + +func templateFuncMap() template.FuncMap { + return template.FuncMap{ + "contains": strings.Contains, + "join": strings.Join, + "title": strings.Title, + "trimPrefix": strings.TrimPrefix, + } +} + +func usage() { + fmt.Println("BPJS Dynamic Handler Generator") + fmt.Println(`Usage: + go run generate-handler.go [flags] +Flags: + -o Output directory + -s Single service name + -tmpl Custom handler template path +`) +} diff --git a/vclaim_handler.go b/vclaim_handler.go deleted file mode 100644 index 42c7b96..0000000 --- a/vclaim_handler.go +++ /dev/null @@ -1,573 +0,0 @@ -// Code generated by generate-dynamic-handler.go; DO NOT EDIT. -// Generated at: 2025-08-28 14:59:09 -// Service: VClaim (vclaim) -// Description: BPJS VClaim service for eligibility and SEP management - -package handlers - -import ( - "context" - "fmt" - "net/http" - "strings" - "time" - - "api-service/internal/config" - "api-service/internal/models/reference" - services "api-service/internal/services/bpjs" - "api-service/pkg/logger" - - "github.com/gin-gonic/gin" - "github.com/go-playground/validator/v10" - "github.com/google/uuid" -) - -// VClaimHandler handles VClaim BPJS services -type VClaimHandler struct { - service services.VClaimService - validator *validator.Validate - logger logger.Logger - config *config.BpjsConfig -} - -// VClaimHandlerConfig contains configuration for VClaimHandler -type VClaimHandlerConfig struct { - BpjsConfig *config.BpjsConfig - Logger logger.Logger - Validator *validator.Validate -} - -// NewVClaimHandler creates a new VClaimHandler -func NewVClaimHandler(cfg VClaimHandlerConfig) *VClaimHandler { - return &VClaimHandler{ - service: services.NewService(*cfg.BpjsConfig), - validator: cfg.Validator, - logger: cfg.Logger, - config: cfg.BpjsConfig, - } -} - -// GetPESERTA retrieves Peserta data -// @Summary Get Peserta data -// @Description Get participant eligibility information -// @Tags vclaim,peserta -// @Accept json -// @Produce json -// @Security ApiKeyAuth -// @Param nokartu path string true "Nokartu" -// @Success 200 {object} reference.PesertaResponse -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /Peserta/:nokartu [get] -func (h *VClaimHandler) GetPESERTA(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) - defer cancel() - - // Generate request ID if not present - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } - // Authentication check - if err := h.authenticateRequest(c); err != nil { - h.logger.Error("Authentication failed", "error", err.Error(), "request_id", requestID) - c.JSON(http.StatusUnauthorized, reference.ErrorResponse{ - Status: "error", - Message: "Authentication failed", - RequestID: requestID, - }) - return - } - - // Extract path parameters - nokartu := c.Param("nokartu") - if nokartu == "" { - h.logger.Error("Missing required parameter: nokartu", "request_id", requestID) - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Missing required parameter: nokartu", - RequestID: requestID, - }) - return - } - // Check cache first - cacheKey := fmt.Sprintf("vclaim:peserta:%s", nokartu) - if cached, found := h.getCachedResponse(cacheKey); found { - h.logger.Info("Cache hit for GetPESERTA", "request_id", requestID, "cache_key", cacheKey) - c.Header("X-Cache", "HIT") - c.JSON(http.StatusOK, cached) - return - } - - h.logger.Info("Processing GetPESERTA request", - "request_id", requestID, - "endpoint", "/Peserta/:nokartu", - "nokartu", nokartu) - - // Call service method - result, err := h.service.GetPESERTA(ctx, nokartu) - - if err != nil { - h.logger.Error("Failed to get Peserta", - "error", err.Error(), - "request_id", requestID) - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } - - // Prepare response - response := reference.PesertaResponse{ - Status: "success", - Data: result, - RequestID: requestID, - } - // Cache successful response - h.setCachedResponse(cacheKey, response, 300) - - c.Header("X-Cache", "MISS") - c.JSON(http.StatusOK, response) -} - -// GetSEP retrieves Sep data -// @Summary Get Sep data -// @Description Manage SEP (Surat Eligibilitas Peserta) -// @Tags vclaim,sep -// @Accept json -// @Produce json -// @Security ApiKeyAuth -// @Param nosep path string true "Nosep" -// @Success 200 {object} reference.SEPResponse -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /SEP/:nosep [get] -func (h *VClaimHandler) GetSEP(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) - defer cancel() - - // Generate request ID if not present - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } - // Authentication check - if err := h.authenticateRequest(c); err != nil { - h.logger.Error("Authentication failed", "error", err.Error(), "request_id", requestID) - c.JSON(http.StatusUnauthorized, reference.ErrorResponse{ - Status: "error", - Message: "Authentication failed", - RequestID: requestID, - }) - return - } - - // Extract path parameters - nosep := c.Param("nosep") - if nosep == "" { - h.logger.Error("Missing required parameter: nosep", "request_id", requestID) - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Missing required parameter: nosep", - RequestID: requestID, - }) - return - } - // Check cache first - cacheKey := fmt.Sprintf("vclaim:sep:%s", nosep) - if cached, found := h.getCachedResponse(cacheKey); found { - h.logger.Info("Cache hit for GetSEP", "request_id", requestID, "cache_key", cacheKey) - c.Header("X-Cache", "HIT") - c.JSON(http.StatusOK, cached) - return - } - - h.logger.Info("Processing GetSEP request", - "request_id", requestID, - "endpoint", "/SEP/:nosep", - "nosep", nosep) - - // Call service method - result, err := h.service.GetSEP(ctx, nosep) - - if err != nil { - h.logger.Error("Failed to get Sep", - "error", err.Error(), - "request_id", requestID) - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } - - // Prepare response - response := reference.SEPResponse{ - Status: "success", - Data: result, - RequestID: requestID, - } - // Cache successful response - h.setCachedResponse(cacheKey, response, 180) - - c.Header("X-Cache", "MISS") - c.JSON(http.StatusOK, response) -} - -// CreateSEP creates new Sep -// @Summary Create Sep -// @Description Manage SEP (Surat Eligibilitas Peserta) -// @Tags vclaim,sep -// @Accept json -// @Produce json -// @Security ApiKeyAuth -// @Param request body reference.SEPRequest true "Sep data" -// @Success 201 {object} reference.SEPResponse -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /sep [post] -func (h *VClaimHandler) CreateSEP(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) - defer cancel() - - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } - // Authentication check - if err := h.authenticateRequest(c); err != nil { - h.logger.Error("Authentication failed", "error", err.Error(), "request_id", requestID) - c.JSON(http.StatusUnauthorized, reference.ErrorResponse{ - Status: "error", - Message: "Authentication failed", - RequestID: requestID, - }) - return - } - - var req reference.SEPRequest - if err := c.ShouldBindJSON(&req); err != nil { - h.logger.Error("Invalid request body", - "error", err.Error(), - "request_id", requestID) - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Invalid request body: " + err.Error(), - RequestID: requestID, - }) - return - } - - // Validate request - if err := h.validator.Struct(&req); err != nil { - h.logger.Error("Validation failed", - "error", err.Error(), - "request_id", requestID) - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Validation failed: " + err.Error(), - RequestID: requestID, - }) - return - } - - h.logger.Info("Processing CreateSEP request", "request_id", requestID) - - // Call service method - result, err := h.service.CreateSEP(ctx, &req) - if err != nil { - h.logger.Error("Failed to create Sep", - "error", err.Error(), - "request_id", requestID) - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } - - response := reference.SEPResponse{ - Status: "success", - Data: result, - RequestID: requestID, - } - - c.JSON(http.StatusCreated, response) -} - -// UpdateSEP updates existing Sep -// @Summary Update Sep -// @Description Manage SEP (Surat Eligibilitas Peserta) -// @Tags vclaim,sep -// @Accept json -// @Produce json -// @Security ApiKeyAuth -// @Param nosep path string true "Nosep" -// @Param request body reference.SEPRequest true "Sep data" -// @Success 200 {object} reference.SEPResponse -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /sep/:nosep [put] -func (h *VClaimHandler) UpdateSEP(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) - defer cancel() - - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } - - // Extract path parameters - nosep := c.Param("nosep") - if nosep == "" { - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Missing required parameter: nosep", - RequestID: requestID, - }) - return - } - - var req reference.SEPRequest - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Invalid request body: " + err.Error(), - RequestID: requestID, - }) - return - } - - if err := h.validator.Struct(&req); err != nil { - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Validation failed: " + err.Error(), - RequestID: requestID, - }) - return - } - // Invalidate cache - cacheKey := fmt.Sprintf("vclaim:sep:%s", nosep) - h.invalidateCache(cacheKey) - - // Call service method - result, err := h.service.UpdateSEP(ctx, nosep, &req) - - if err != nil { - h.logger.Error("Failed to update Sep", "error", err.Error(), "request_id", requestID) - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } - - response := reference.SEPResponse{ - Status: "success", - Data: result, - RequestID: requestID, - } - - c.JSON(http.StatusOK, response) -} - -// DeleteSEP deletes existing Sep -// @Summary Delete Sep -// @Description Manage SEP (Surat Eligibilitas Peserta) -// @Tags vclaim,sep -// @Accept json -// @Produce json -// @Security ApiKeyAuth -// @Param nosep path string true "Nosep" -// @Success 200 {object} reference.BaseResponse -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /sep/:nosep [delete] -func (h *VClaimHandler) DeleteSEP(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) - defer cancel() - - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } - - // Extract path parameters - nosep := c.Param("nosep") - if nosep == "" { - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Missing required parameter: nosep", - RequestID: requestID, - }) - return - } - // Invalidate cache - cacheKey := fmt.Sprintf("vclaim:sep:%s", nosep) - h.invalidateCache(cacheKey) - - // Call service method - err := h.service.DeleteSEP(ctx, nosep) - - if err != nil { - h.logger.Error("Failed to delete Sep", "error", err.Error(), "request_id", requestID) - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } - - response := reference.BaseResponse{ - Status: "success", - Message: "Sep deleted successfully", - RequestID: requestID, - } - - c.JSON(http.StatusOK, response) -} - -// GetRUJUKAN retrieves Rujukan data -// @Summary Get Rujukan data -// @Description Get referral information -// @Tags vclaim,rujukan -// @Accept json -// @Produce json -// @Security ApiKeyAuth -// @Param norujukan path string true "Norujukan" -// @Success 200 {object} reference.RujukanResponse -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /rujukan/:norujukan [get] -func (h *VClaimHandler) GetRUJUKAN(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) - defer cancel() - - // Generate request ID if not present - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } - // Authentication check - if err := h.authenticateRequest(c); err != nil { - h.logger.Error("Authentication failed", "error", err.Error(), "request_id", requestID) - c.JSON(http.StatusUnauthorized, reference.ErrorResponse{ - Status: "error", - Message: "Authentication failed", - RequestID: requestID, - }) - return - } - - // Extract path parameters - norujukan := c.Param("norujukan") - if norujukan == "" { - h.logger.Error("Missing required parameter: norujukan", "request_id", requestID) - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Missing required parameter: norujukan", - RequestID: requestID, - }) - return - } - // Check cache first - cacheKey := fmt.Sprintf("vclaim:rujukan:%s", norujukan) - if cached, found := h.getCachedResponse(cacheKey); found { - h.logger.Info("Cache hit for GetRUJUKAN", "request_id", requestID, "cache_key", cacheKey) - c.Header("X-Cache", "HIT") - c.JSON(http.StatusOK, cached) - return - } - - h.logger.Info("Processing GetRUJUKAN request", - "request_id", requestID, - "endpoint", "/rujukan/:norujukan", - "norujukan", norujukan) - - // Call service method - result, err := h.service.GetRUJUKAN(ctx, norujukan) - - if err != nil { - h.logger.Error("Failed to get Rujukan", - "error", err.Error(), - "request_id", requestID) - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } - - // Prepare response - response := reference.RujukanResponse{ - Status: "success", - Data: result, - RequestID: requestID, - } - // Cache successful response - h.setCachedResponse(cacheKey, response, 600) - - c.Header("X-Cache", "MISS") - c.JSON(http.StatusOK, response) -} - -// RegisterRoutes registers all VClaim routes -func (h *VClaimHandler) RegisterRoutes(router *gin.RouterGroup) { - - router.GET("/Peserta/:nokartu", h.GetPESERTA) - - router.GET("/SEP/:nosep", h.GetSEP) - router.POST("/sep", h.CreateSEP) - router.PUT("/sep/:nosep", h.UpdateSEP) - router.DELETE("/sep/:nosep", h.DeleteSEP) - - router.GET("/rujukan/:norujukan", h.GetRUJUKAN) - -} - -// Helper methods -func (h *VClaimHandler) authenticateRequest(c *gin.Context) error { - token := c.GetHeader("Authorization") - if token == "" { - return fmt.Errorf("missing authorization header") - } - - if strings.HasPrefix(token, "Bearer ") { - token = strings.TrimPrefix(token, "Bearer ") - } - - return h.auth.ValidateToken(token) -} -func (h *VClaimHandler) getCachedResponse(key string) (interface{}, bool) { - if h.cache == nil { - return nil, false - } - return h.cache.Get(key) -} - -func (h *VClaimHandler) setCachedResponse(key string, data interface{}, ttl int) { - if h.cache == nil { - return - } - h.cache.Set(key, data, time.Duration(ttl)*time.Second) -} - -func (h *VClaimHandler) invalidateCache(key string) { - if h.cache == nil { - return - } - h.cache.Delete(key) -} diff --git a/vclaim_handler2.go b/vclaim_handler2.go deleted file mode 100644 index 7807984..0000000 --- a/vclaim_handler2.go +++ /dev/null @@ -1,365 +0,0 @@ -// Code generated by generate-dynamic-handler.go; DO NOT EDIT. -// Generated at: 2025-08-28 14:47:36 -// Service: VClaim (vclaim) -// Description: BPJS VClaim service for eligibility and SEP management - -package handlers - -import ( - "api-service/internal/config" - "api-service/internal/models/reference" - services "api-service/internal/services/bpjs" - "api-service/pkg/logger" - "context" - "net/http" - "time" - - "github.com/gin-gonic/gin" - "github.com/go-playground/validator/v10" - "github.com/google/uuid" -) - -// VClaimHandler handles VClaim BPJS services -type VClaimHandler struct { - service services.VClaimService - validator *validator.Validate - logger logger.Logger - config config.BpjsConfig -} - -// VClaimHandlerConfig contains configuration for VClaimHandler -type VClaimHandlerConfig struct { - BpjsConfig config.BpjsConfig - Logger logger.Logger - Validator *validator.Validate -} - -// NewVClaimHandler creates a new VClaimHandler -func NewVClaimHandler(cfg VClaimHandlerConfig) *VClaimHandler { - return &VClaimHandler{ - service: services.NewService(cfg.BpjsConfig), - validator: cfg.Validator, - logger: cfg.Logger, - config: cfg.BpjsConfig, - } -} - -// GetPESERTA retrieves Peserta data - -// @Summary Get Peserta data -// @Description Get participant eligibility information -// @Tags vclaim,peserta -// @Accept json -// @Produce json - -// @Security ApiKeyAuth - -// @Param nokartu path string true "nokartu" - -// @Success 200 {object} reference.PesertaResponse -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /Peserta/:nokartu [get] - -func (h *VClaimHandler) GetPeserta(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) - defer cancel() - - // Generate request ID if not present - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } - - h.logger.Info("Processing GetPeserta request", map[string]interface{}{ - "request_id": requestID, - "endpoint": "/Peserta/:nokartu", - - "nokartu": c.Param("nokartu"), - }) - - // Extract path parameters - - nokartu := c.Param("nokartu") - if nokartu == "" { - - h.logger.Error("Missing required parameter nokartu", map[string]interface{}{ - "request_id": requestID, - }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Missing required parameter nokartu", - RequestID: requestID, - }) - return - } - - // Call service method - var response reference.PesertaResponse - - response, err := h.service.GetPeserta(ctx, nokartu) - - if err != nil { - - h.logger.Error("Failed to get Peserta", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } - - // Ensure response has proper fields - response.Status = "success" - response.RequestID = requestID - c.JSON(http.StatusOK, response) -} - -// GetSEP retrieves Sep data - -// @Summary Get Sep data -// @Description Manage SEP (Surat Eligibilitas Peserta) -// @Tags vclaim,sep -// @Accept json -// @Produce json - -// @Security ApiKeyAuth - -// @Param nosep path string true "nosep" - -// @Success 200 {object} reference.SEPResponse -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /SEP/:nosep [get] - -func (h *VClaimHandler) GetSEP(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) - defer cancel() - - // Generate request ID if not present - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } - - h.logger.Info("Processing GetSep request", map[string]interface{}{ - "request_id": requestID, - "endpoint": "/SEP/:nosep", - - "nosep": c.Param("nosep"), - }) - - // Extract path parameters - - nosep := c.Param("nosep") - if nosep == "" { - - h.logger.Error("Missing required parameter nosep", map[string]interface{}{ - "request_id": requestID, - }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Missing required parameter nosep", - RequestID: requestID, - }) - return - } - - // Call service method - var response reference.SEPResponse - - result, err := h.service.GetSEP(ctx, nosep) - - if err != nil { - - h.logger.Error("Failed to get Sep", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } - - // Ensure response has proper fields - response.Status = "success" - response.RequestID = requestID - c.JSON(http.StatusOK, response) -} - -// CreateSEP creates new Sep - -// @Summary Create Sep -// @Description Manage SEP (Surat Eligibilitas Peserta) -// @Tags vclaim,sep -// @Accept json -// @Produce json - -// @Security ApiKeyAuth - -// @Param request body reference.SEPRequest true "Sep data" -// @Success 201 {object} reference.SEPResponse -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /sep [post] - -func (h *VClaimHandler) CreateSEP(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) - defer cancel() - - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } - - h.logger.Info("Processing CreateSep request", map[string]interface{}{ - "request_id": requestID, - }) - - var req reference.SEPRequest - if err := c.ShouldBindJSON(&req); err != nil { - - h.logger.Error("Invalid request body", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Invalid request body: " + err.Error(), - RequestID: requestID, - }) - return - } - - // Validate request - if err := h.validator.Struct(&req); err != nil { - - h.logger.Error("Validation failed", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Validation failed: " + err.Error(), - RequestID: requestID, - }) - return - } - - // Call service method - var response reference.SEPResponse - response, err := h.service.CreateSEP(ctx, &req) - if err != nil { - - h.logger.Error("Failed to create Sep", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } - - // Ensure response has proper fields - response.Status = "success" - response.RequestID = requestID - c.JSON(http.StatusCreated, response) -} - -// GetRUJUKAN retrieves Rujukan data - -// @Summary Get Rujukan data -// @Description Get referral information -// @Tags vclaim,rujukan -// @Accept json -// @Produce json - -// @Security ApiKeyAuth - -// @Param norujukan path string true "norujukan" - -// @Success 200 {object} reference.RujukanResponse -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /rujukan/:norujukan [get] - -func (h *VClaimHandler) GetRUJUKAN(c *gin.Context) { - ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) - defer cancel() - - // Generate request ID if not present - requestID := c.GetHeader("X-Request-ID") - if requestID == "" { - requestID = uuid.New().String() - c.Header("X-Request-ID", requestID) - } - - h.logger.Info("Processing GetRujukan request", map[string]interface{}{ - "request_id": requestID, - "endpoint": "/rujukan/:norujukan", - - "norujukan": c.Param("norujukan"), - }) - - // Extract path parameters - - norujukan := c.Param("norujukan") - if norujukan == "" { - - h.logger.Error("Missing required parameter norujukan", map[string]interface{}{ - "request_id": requestID, - }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ - Status: "error", - Message: "Missing required parameter norujukan", - RequestID: requestID, - }) - return - } - - // Call service method - var response reference.RujukanResponse - - response, err := h.service.GetRUJUKAN(ctx, norujukan) - - if err != nil { - - h.logger.Error("Failed to get Rujukan", map[string]interface{}{ - "error": err.Error(), - "request_id": requestID, - }) - - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ - Status: "error", - Message: "Internal server error", - RequestID: requestID, - }) - return - } - - // Ensure response has proper fields - response.Status = "success" - response.RequestID = requestID - c.JSON(http.StatusOK, response) -}