package main import ( "fmt" "os" "path/filepath" "strings" "time" ) // SatuSehatHandlerData contains template data for Satu Sehat handler generation type SatuSehatHandlerData struct { Name string NameLower string NameUpper string Category string CategoryPath string CategoryParts []string ModuleName string HasGet bool HasPost bool HasPut bool HasPatch bool HasDelete bool GetEndpoint string PostEndpoint string PutEndpoint string PatchEndpoint string DeleteEndpoint string Timestamp string DirectoryDepth int FhirResource string } func main() { if len(os.Args) < 2 { fmt.Println("Usage: go run generate-satusehat-handler.go [level1[/level2[/level3[/level4]]]]/entity [methods]") fmt.Println("Examples:") fmt.Println(" go run generate-satusehat-handler.go fhir/patient get post put patch") fmt.Println(" go run generate-satusehat-handler.go master/organization get post") fmt.Println(" go run generate-satusehat-handler.go integration/encounter/observation get post") fmt.Println(" go run generate-satusehat-handler.go api/v1/fhir/r4/patient get post put patch delete") fmt.Println(" go run generate-satusehat-handler.go location get") os.Exit(1) } // Parse entity path (up to 4 levels + entity) entityPath := os.Args[1] methods := []string{} if len(os.Args) > 2 { methods = os.Args[2:] } else { // Default methods for FHIR resources methods = []string{"get", "post", "put", "patch"} } // Parse multi-level category and entity var categoryParts []string var entityName string var category string parts := strings.Split(entityPath, "/") if len(parts) > 1 && len(parts) <= 5 { // Up to 4 levels + entity categoryParts = parts[:len(parts)-1] entityName = parts[len(parts)-1] category = strings.Join(categoryParts, "/") } else if len(parts) == 1 { category = "" entityName = parts[0] categoryParts = []string{} } else { fmt.Println("❌ Error: Invalid path format. Use up to 4 levels like 'level1/level2/level3/level4/entity' or just 'entity'") fmt.Printf("❌ You provided %d levels, maximum is 4 levels + entity\n", len(parts)-1) os.Exit(1) } // Format names entityName = strings.Title(entityName) // PascalCase entity name entityLower := strings.ToLower(entityName) entityUpper := strings.ToUpper(entityName) // FHIR Resource name (capitalize first letter only) fhirResource := strings.Title(strings.ToLower(entityName)) data := SatuSehatHandlerData{ Name: entityName, NameLower: entityLower, NameUpper: entityUpper, Category: category, CategoryPath: category, CategoryParts: categoryParts, ModuleName: "api-service", DirectoryDepth: len(categoryParts), FhirResource: fhirResource, Timestamp: time.Now().Format("2006-01-02 15:04:05"), } // Set methods and endpoints for FHIR resources for _, m := range methods { switch strings.ToLower(m) { case "get": data.HasGet = true data.GetEndpoint = fmt.Sprintf("%s/{id}", fhirResource) case "post": data.HasPost = true data.PostEndpoint = fhirResource case "put": data.HasPut = true data.PutEndpoint = fmt.Sprintf("%s/{id}", fhirResource) case "patch": data.HasPatch = true data.PatchEndpoint = fmt.Sprintf("%s/{id}", fhirResource) case "delete": data.HasDelete = true data.DeleteEndpoint = fmt.Sprintf("%s/{id}", fhirResource) } } // Create directories with multi-level support var handlerDir, modelDir string if category != "" { // Multi-level directory support handlerDirParts := append([]string{"internal", "handlers", "satusehat"}, categoryParts...) modelDirParts := append([]string{"internal", "models", "satusehat"}, categoryParts...) handlerDir = filepath.Join(handlerDirParts...) modelDir = filepath.Join(modelDirParts...) } else { // No category: direct internal/handlers/satusehat/ handlerDir = filepath.Join("internal", "handlers", "satusehat") modelDir = filepath.Join("internal", "models", "satusehat") } // Create directories for _, d := range []string{handlerDir, modelDir} { if err := os.MkdirAll(d, 0755); err != nil { panic(err) } } // Generate files generateOptimizedSatuSehatHandlerFile(data, handlerDir) generateOptimizedSatuSehatModelFile(data, modelDir) // updateOptimizedSatuSehatRoutesFile(data) fmt.Printf("✅ Successfully generated optimized Satu Sehat handler: %s\n", entityName) if category != "" { fmt.Printf("📁 Category Path: %s (%d levels deep)\n", category, data.DirectoryDepth) } fmt.Printf("📁 FHIR Resource: %s\n", fhirResource) fmt.Printf("📁 Handler: %s\n", filepath.Join(handlerDir, entityLower+".go")) fmt.Printf("📁 Model: %s\n", filepath.Join(modelDir, entityLower+".go")) } // ================= OPTIMIZED HANDLER GENERATION ===================== func generateOptimizedSatuSehatHandlerFile(data SatuSehatHandlerData, handlerDir string) { var modelsImportPath string if data.Category != "" { modelsImportPath = data.ModuleName + "/internal/models/satusehat/" + data.Category } else { modelsImportPath = data.ModuleName + "/internal/models/satusehat" } handlerContent := `package handlers import ( "context" "fmt" "net/http" "time" "` + data.ModuleName + `/internal/config" "` + modelsImportPath + `" services "` + data.ModuleName + `/internal/services/satusehat" "` + data.ModuleName + `/pkg/logger" "` + data.ModuleName + `/pkg/validator" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/go-playground/validator/v10" ) // ` + data.Name + `Handler handles ` + data.NameLower + ` Satu Sehat FHIR services with multi-level organization // Generated for FHIR Resource: ` + data.FhirResource + ` // Path: ` + data.Category + ` // Directory depth: ` + fmt.Sprintf("%d", data.DirectoryDepth) + ` levels type ` + data.Name + `Handler struct { service services.SatuSehatService validator *validator.Validate logger logger.Logger config *config.SatuSehatConfig } // HandlerConfig contains configuration for ` + data.Name + `Handler type ` + data.Name + `HandlerConfig struct { SatuSehatConfig *config.SatuSehatConfig Logger logger.Logger Validator *validator.Validate } // New` + data.Name + `Handler creates a new optimized ` + data.Name + `Handler for Satu Sehat FHIR func New` + data.Name + `Handler(cfg *` + data.Name + `HandlerConfig) *` + data.Name + `Handler { return &` + data.Name + `Handler{ service: services.NewSatuSehatService(cfg.SatuSehatConfig), validator: cfg.Validator, logger: cfg.Logger, config: cfg.SatuSehatConfig, } }` // Add optimized methods based on flags if data.HasPost { handlerContent += generateOptimizedSatuSehatCreateMethod(data) } if data.HasPut { handlerContent += generateOptimizedSatuSehatUpdateMethod(data) } if data.HasPatch { handlerContent += generateOptimizedSatuSehatPatchMethod(data) } if data.HasDelete { handlerContent += generateOptimizedSatuSehatDeleteMethod(data) } if data.HasGet { handlerContent += generateOptimizedSatuSehatGetMethod(data) handlerContent += generateOptimizedSatuSehatSearchMethod(data) } // Add helper methods handlerContent += generateSatuSehatHelperMethods(data) writeFile(filepath.Join(handlerDir, data.NameLower+".go"), handlerContent) } func generateOptimizedSatuSehatCreateMethod(data SatuSehatHandlerData) string { var routePath, tagName string if data.Category != "" { routePath = "satusehat/" + data.Category + "/" + data.NameLower tagParts := append([]string{"SatuSehat"}, data.CategoryParts...) tagParts = append(tagParts, strings.Title(data.NameLower)) tagName = strings.Join(tagParts, "-") } else { routePath = "satusehat/" + data.NameLower tagName = "SatuSehat-" + strings.Title(data.NameLower) } return ` // Create` + data.Name + ` creates a new FHIR ` + data.FhirResource + ` resource // @Summary Create a new FHIR ` + data.FhirResource + ` resource // @Description Create a new ` + data.FhirResource + ` resource in Satu Sehat ecosystem with FHIR R4 compliance // @Description FHIR Resource: ` + data.FhirResource + ` | Path: ` + data.Category + ` // @Tags ` + tagName + ` // @Accept json // @Produce json // @Param Authorization header string true "Bearer token from Satu Sehat OAuth2" // @Param request body models.` + data.Name + `CreateRequest true "` + data.FhirResource + ` FHIR resource creation request" // @Success 201 {object} models.` + data.Name + `Response "` + data.FhirResource + ` resource created successfully" // @Failure 400 {object} models.FhirOperationOutcome "Bad request - validation error" // @Failure 401 {object} models.FhirOperationOutcome "Unauthorized - invalid or expired token" // @Failure 422 {object} models.FhirOperationOutcome "Unprocessable entity - FHIR validation error" // @Failure 500 {object} models.FhirOperationOutcome "Internal server error" // @Router /api/v1/` + routePath + ` [post] func (h *` + data.Name + `Handler) Create` + data.Name + `(c *gin.Context) { requestID := uuid.New().String() startTime := time.Now() h.logger.Info("Creating FHIR ` + data.FhirResource + ` resource", map[string]interface{}{ "request_id": requestID, "timestamp": startTime, "fhir_resource": "` + data.FhirResource + `", "category_path": "` + data.Category + `", "directory_depth": ` + fmt.Sprintf("%d", data.DirectoryDepth) + `, }) var req models.` + data.Name + `CreateRequest req.RequestID = requestID req.Timestamp = startTime // Enhanced JSON binding with FHIR validation if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Failed to bind FHIR JSON", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "path": "` + routePath + `", }) h.sendFhirErrorResponse(c, http.StatusBadRequest, "structure", "Invalid FHIR resource structure", err.Error(), requestID) return } // FHIR resource validation if err := req.ValidateFhir(); err != nil { h.logger.Error("FHIR validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "path": "` + routePath + `", }) h.sendFhirErrorResponse(c, http.StatusUnprocessableEntity, "business-rule", "FHIR resource validation failed", err.Error(), requestID) return } // Struct validation if err := h.validator.Struct(&req); err != nil { h.logger.Error("Struct validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "path": "` + routePath + `", }) h.sendFhirErrorResponse(c, http.StatusBadRequest, "structure", "Resource structure validation failed", h.formatValidationError(err), requestID) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() var fhirResponse models.FhirResponse if err := h.service.CreateResource(ctx, "` + data.PostEndpoint + `", req, &fhirResponse); err != nil { h.logger.Error("Failed to create FHIR resource", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "endpoint": "` + data.PostEndpoint + `", "path": "` + routePath + `", }) statusCode, errorCode := h.categorizeFhirError(err) h.sendFhirErrorResponse(c, statusCode, errorCode, "Failed to create ` + data.FhirResource + ` resource", err.Error(), requestID) return } // Check for FHIR OperationOutcome if fhirResponse.ResourceType == "OperationOutcome" { h.logger.Warn("Satu Sehat returned OperationOutcome", map[string]interface{}{ "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "path": "` + routePath + `", "outcome": fhirResponse, }) h.sendFhirOperationOutcome(c, http.StatusUnprocessableEntity, fhirResponse, requestID) return } duration := time.Since(startTime) h.logger.Info("FHIR ` + data.FhirResource + ` resource created successfully", map[string]interface{}{ "request_id": requestID, "duration": duration.String(), "fhir_resource": "` + data.FhirResource + `", "resource_id": fhirResponse.ID, "path": "` + routePath + `", }) h.sendFhirSuccessResponse(c, http.StatusCreated, "` + data.FhirResource + ` resource created successfully", fhirResponse, requestID) }` } func generateOptimizedSatuSehatUpdateMethod(data SatuSehatHandlerData) string { var routePath, tagName string if data.Category != "" { routePath = "satusehat/" + data.Category + "/" + data.NameLower tagParts := append([]string{"SatuSehat"}, data.CategoryParts...) tagParts = append(tagParts, strings.Title(data.NameLower)) tagName = strings.Join(tagParts, "-") } else { routePath = "satusehat/" + data.NameLower tagName = "SatuSehat-" + strings.Title(data.NameLower) } return ` // Update` + data.Name + ` updates an existing FHIR ` + data.FhirResource + ` resource // @Summary Update (replace) an existing FHIR ` + data.FhirResource + ` resource // @Description Update an existing ` + data.FhirResource + ` resource in Satu Sehat ecosystem with FHIR R4 compliance // @Description FHIR Resource: ` + data.FhirResource + ` | Path: ` + data.Category + ` // @Tags ` + tagName + ` // @Accept json // @Produce json // @Param Authorization header string true "Bearer token from Satu Sehat OAuth2" // @Param id path string true "` + data.FhirResource + ` resource ID" // @Param request body models.` + data.Name + `UpdateRequest true "` + data.FhirResource + ` FHIR resource update request" // @Success 200 {object} models.` + data.Name + `Response "` + data.FhirResource + ` resource updated successfully" // @Failure 400 {object} models.FhirOperationOutcome "Bad request - validation error" // @Failure 401 {object} models.FhirOperationOutcome "Unauthorized - invalid or expired token" // @Failure 404 {object} models.FhirOperationOutcome "Resource not found" // @Failure 422 {object} models.FhirOperationOutcome "Unprocessable entity - FHIR validation error" // @Failure 500 {object} models.FhirOperationOutcome "Internal server error" // @Router /api/v1/` + routePath + `/{id} [put] func (h *` + data.Name + `Handler) Update` + data.Name + `(c *gin.Context) { requestID := uuid.New().String() startTime := time.Now() id := c.Param("id") h.logger.Info("Updating FHIR ` + data.FhirResource + ` resource", map[string]interface{}{ "request_id": requestID, "timestamp": startTime, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "category_path": "` + data.Category + `", "directory_depth": ` + fmt.Sprintf("%d", data.DirectoryDepth) + `, }) if id == "" { h.sendFhirErrorResponse(c, http.StatusBadRequest, "required", "Resource ID is required", "", requestID) return } var req models.` + data.Name + `UpdateRequest req.RequestID = requestID req.Timestamp = startTime req.ID = id if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Failed to bind FHIR JSON for update", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", }) h.sendFhirErrorResponse(c, http.StatusBadRequest, "structure", "Invalid FHIR resource structure", err.Error(), requestID) return } if err := req.ValidateFhir(); err != nil { h.logger.Error("FHIR update validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", }) h.sendFhirErrorResponse(c, http.StatusUnprocessableEntity, "business-rule", "FHIR resource validation failed", err.Error(), requestID) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() endpoint := fmt.Sprintf("` + strings.Replace(data.PutEndpoint, "{id}", "%s", 1) + `", id) var fhirResponse models.FhirResponse if err := h.service.UpdateResource(ctx, endpoint, req, &fhirResponse); err != nil { h.logger.Error("Failed to update FHIR resource", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", }) statusCode, errorCode := h.categorizeFhirError(err) h.sendFhirErrorResponse(c, statusCode, errorCode, "Failed to update ` + data.FhirResource + ` resource", err.Error(), requestID) return } if fhirResponse.ResourceType == "OperationOutcome" { h.logger.Warn("Satu Sehat returned OperationOutcome for update", map[string]interface{}{ "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", "outcome": fhirResponse, }) h.sendFhirOperationOutcome(c, http.StatusUnprocessableEntity, fhirResponse, requestID) return } duration := time.Since(startTime) h.logger.Info("FHIR ` + data.FhirResource + ` resource updated successfully", map[string]interface{}{ "request_id": requestID, "duration": duration.String(), "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", }) h.sendFhirSuccessResponse(c, http.StatusOK, "` + data.FhirResource + ` resource updated successfully", fhirResponse, requestID) }` } func generateOptimizedSatuSehatPatchMethod(data SatuSehatHandlerData) string { var routePath, tagName string if data.Category != "" { routePath = "satusehat/" + data.Category + "/" + data.NameLower tagParts := append([]string{"SatuSehat"}, data.CategoryParts...) tagParts = append(tagParts, strings.Title(data.NameLower)) tagName = strings.Join(tagParts, "-") } else { routePath = "satusehat/" + data.NameLower tagName = "SatuSehat-" + strings.Title(data.NameLower) } return ` // Patch` + data.Name + ` partially updates an existing FHIR ` + data.FhirResource + ` resource // @Summary Patch (partial update) an existing FHIR ` + data.FhirResource + ` resource // @Description Partially update an existing ` + data.FhirResource + ` resource in Satu Sehat ecosystem with FHIR R4 compliance // @Description FHIR Resource: ` + data.FhirResource + ` | Path: ` + data.Category + ` // @Tags ` + tagName + ` // @Accept json-patch+json // @Produce json // @Param Authorization header string true "Bearer token from Satu Sehat OAuth2" // @Param id path string true "` + data.FhirResource + ` resource ID" // @Param request body models.` + data.Name + `PatchRequest true "` + data.FhirResource + ` FHIR resource patch request" // @Success 200 {object} models.` + data.Name + `Response "` + data.FhirResource + ` resource patched successfully" // @Failure 400 {object} models.FhirOperationOutcome "Bad request - validation error" // @Failure 401 {object} models.FhirOperationOutcome "Unauthorized - invalid or expired token" // @Failure 404 {object} models.FhirOperationOutcome "Resource not found" // @Failure 422 {object} models.FhirOperationOutcome "Unprocessable entity - FHIR validation error" // @Failure 500 {object} models.FhirOperationOutcome "Internal server error" // @Router /api/v1/` + routePath + `/{id} [patch] func (h *` + data.Name + `Handler) Patch` + data.Name + `(c *gin.Context) { requestID := uuid.New().String() startTime := time.Now() id := c.Param("id") h.logger.Info("Patching FHIR ` + data.FhirResource + ` resource", map[string]interface{}{ "request_id": requestID, "timestamp": startTime, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "category_path": "` + data.Category + `", "directory_depth": ` + fmt.Sprintf("%d", data.DirectoryDepth) + `, }) if id == "" { h.sendFhirErrorResponse(c, http.StatusBadRequest, "required", "Resource ID is required", "", requestID) return } var req models.` + data.Name + `PatchRequest req.RequestID = requestID req.Timestamp = startTime req.ID = id if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Failed to bind FHIR patch JSON", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", }) h.sendFhirErrorResponse(c, http.StatusBadRequest, "structure", "Invalid FHIR patch structure", err.Error(), requestID) return } if err := req.ValidateFhirPatch(); err != nil { h.logger.Error("FHIR patch validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", }) h.sendFhirErrorResponse(c, http.StatusUnprocessableEntity, "business-rule", "FHIR patch validation failed", err.Error(), requestID) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() endpoint := fmt.Sprintf("` + strings.Replace(data.PatchEndpoint, "{id}", "%s", 1) + `", id) var fhirResponse models.FhirResponse if err := h.service.PatchResource(ctx, endpoint, req, &fhirResponse); err != nil { h.logger.Error("Failed to patch FHIR resource", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", }) statusCode, errorCode := h.categorizeFhirError(err) h.sendFhirErrorResponse(c, statusCode, errorCode, "Failed to patch ` + data.FhirResource + ` resource", err.Error(), requestID) return } if fhirResponse.ResourceType == "OperationOutcome" { h.logger.Warn("Satu Sehat returned OperationOutcome for patch", map[string]interface{}{ "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", "outcome": fhirResponse, }) h.sendFhirOperationOutcome(c, http.StatusUnprocessableEntity, fhirResponse, requestID) return } duration := time.Since(startTime) h.logger.Info("FHIR ` + data.FhirResource + ` resource patched successfully", map[string]interface{}{ "request_id": requestID, "duration": duration.String(), "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", }) h.sendFhirSuccessResponse(c, http.StatusOK, "` + data.FhirResource + ` resource patched successfully", fhirResponse, requestID) }` } func generateOptimizedSatuSehatDeleteMethod(data SatuSehatHandlerData) string { var routePath, tagName string if data.Category != "" { routePath = "satusehat/" + data.Category + "/" + data.NameLower tagParts := append([]string{"SatuSehat"}, data.CategoryParts...) tagParts = append(tagParts, strings.Title(data.NameLower)) tagName = strings.Join(tagParts, "-") } else { routePath = "satusehat/" + data.NameLower tagName = "SatuSehat-" + strings.Title(data.NameLower) } return ` // Delete` + data.Name + ` deletes an existing FHIR ` + data.FhirResource + ` resource // @Summary Delete an existing FHIR ` + data.FhirResource + ` resource // @Description Delete an existing ` + data.FhirResource + ` resource in Satu Sehat ecosystem with FHIR R4 compliance // @Description FHIR Resource: ` + data.FhirResource + ` | Path: ` + data.Category + ` // @Tags ` + tagName + ` // @Accept json // @Produce json // @Param Authorization header string true "Bearer token from Satu Sehat OAuth2" // @Param id path string true "` + data.FhirResource + ` resource ID" // @Success 204 "` + data.FhirResource + ` resource deleted successfully" // @Failure 400 {object} models.FhirOperationOutcome "Bad request - invalid ID" // @Failure 401 {object} models.FhirOperationOutcome "Unauthorized - invalid or expired token" // @Failure 404 {object} models.FhirOperationOutcome "Resource not found" // @Failure 500 {object} models.FhirOperationOutcome "Internal server error" // @Router /api/v1/` + routePath + `/{id} [delete] func (h *` + data.Name + `Handler) Delete` + data.Name + `(c *gin.Context) { requestID := uuid.New().String() startTime := time.Now() id := c.Param("id") h.logger.Info("Deleting FHIR ` + data.FhirResource + ` resource", map[string]interface{}{ "request_id": requestID, "timestamp": startTime, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "category_path": "` + data.Category + `", "directory_depth": ` + fmt.Sprintf("%d", data.DirectoryDepth) + `, }) if id == "" { h.sendFhirErrorResponse(c, http.StatusBadRequest, "required", "Resource ID is required", "", requestID) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() endpoint := fmt.Sprintf("` + strings.Replace(data.DeleteEndpoint, "{id}", "%s", 1) + `", id) if err := h.service.DeleteResource(ctx, endpoint); err != nil { h.logger.Error("Failed to delete FHIR resource", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", }) statusCode, errorCode := h.categorizeFhirError(err) h.sendFhirErrorResponse(c, statusCode, errorCode, "Failed to delete ` + data.FhirResource + ` resource", err.Error(), requestID) return } duration := time.Since(startTime) h.logger.Info("FHIR ` + data.FhirResource + ` resource deleted successfully", map[string]interface{}{ "request_id": requestID, "duration": duration.String(), "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", }) c.Status(http.StatusNoContent) }` } func generateOptimizedSatuSehatGetMethod(data SatuSehatHandlerData) string { var routePath, tagName string if data.Category != "" { routePath = "satusehat/" + data.Category + "/" + data.NameLower tagParts := append([]string{"SatuSehat"}, data.CategoryParts...) tagParts = append(tagParts, strings.Title(data.NameLower)) tagName = strings.Join(tagParts, "-") } else { routePath = "satusehat/" + data.NameLower tagName = "SatuSehat-" + strings.Title(data.NameLower) } return ` // Get` + data.Name + ` retrieves a specific FHIR ` + data.FhirResource + ` resource by ID // @Summary Get a specific FHIR ` + data.FhirResource + ` resource by ID // @Description Retrieve a specific ` + data.FhirResource + ` resource from Satu Sehat ecosystem with FHIR R4 compliance // @Description FHIR Resource: ` + data.FhirResource + ` | Path: ` + data.Category + ` // @Tags ` + tagName + ` // @Accept json // @Produce json // @Param Authorization header string true "Bearer token from Satu Sehat OAuth2" // @Param id path string true "` + data.FhirResource + ` resource ID" // @Success 200 {object} models.` + data.Name + `Response "` + data.FhirResource + ` resource retrieved successfully" // @Failure 400 {object} models.FhirOperationOutcome "Bad request - invalid ID" // @Failure 401 {object} models.FhirOperationOutcome "Unauthorized - invalid or expired token" // @Failure 404 {object} models.FhirOperationOutcome "Resource not found" // @Failure 500 {object} models.FhirOperationOutcome "Internal server error" // @Router /api/v1/` + routePath + `/{id} [get] func (h *` + data.Name + `Handler) Get` + data.Name + `(c *gin.Context) { requestID := uuid.New().String() startTime := time.Now() id := c.Param("id") h.logger.Info("Getting FHIR ` + data.FhirResource + ` resource", map[string]interface{}{ "request_id": requestID, "timestamp": startTime, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "category_path": "` + data.Category + `", "directory_depth": ` + fmt.Sprintf("%d", data.DirectoryDepth) + `, }) if id == "" { h.sendFhirErrorResponse(c, http.StatusBadRequest, "required", "Resource ID is required", "", requestID) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() endpoint := fmt.Sprintf("` + strings.Replace(data.GetEndpoint, "{id}", "%s", 1) + `", id) var fhirResponse models.FhirResponse if err := h.service.GetResource(ctx, endpoint, &fhirResponse); err != nil { h.logger.Error("Failed to get FHIR resource", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", }) statusCode, errorCode := h.categorizeFhirError(err) h.sendFhirErrorResponse(c, statusCode, errorCode, "Failed to get ` + data.FhirResource + ` resource", err.Error(), requestID) return } if fhirResponse.ResourceType == "OperationOutcome" { h.logger.Info("FHIR ` + data.FhirResource + ` resource not found", map[string]interface{}{ "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", "outcome": fhirResponse, }) h.sendFhirOperationOutcome(c, http.StatusNotFound, fhirResponse, requestID) return } duration := time.Since(startTime) h.logger.Info("FHIR ` + data.FhirResource + ` resource retrieved successfully", map[string]interface{}{ "request_id": requestID, "duration": duration.String(), "fhir_resource": "` + data.FhirResource + `", "resource_id": id, "path": "` + routePath + `", }) h.sendFhirSuccessResponse(c, http.StatusOK, "` + data.FhirResource + ` resource retrieved successfully", fhirResponse, requestID) }` } func generateOptimizedSatuSehatSearchMethod(data SatuSehatHandlerData) string { var routePath, tagName string if data.Category != "" { routePath = "satusehat/" + data.Category + "/" + data.NameLower tagParts := append([]string{"SatuSehat"}, data.CategoryParts...) tagParts = append(tagParts, strings.Title(data.NameLower)) tagName = strings.Join(tagParts, "-") } else { routePath = "satusehat/" + data.NameLower tagName = "SatuSehat-" + strings.Title(data.NameLower) } return ` // Search` + data.Name + ` searches for FHIR ` + data.FhirResource + ` resources with parameters // @Summary Search for FHIR ` + data.FhirResource + ` resources // @Description Search for ` + data.FhirResource + ` resources in Satu Sehat ecosystem with FHIR R4 search parameters // @Description FHIR Resource: ` + data.FhirResource + ` | Path: ` + data.Category + ` // @Tags ` + tagName + ` // @Accept json // @Produce json // @Param Authorization header string true "Bearer token from Satu Sehat OAuth2" // @Param _count query integer false "Number of results to return (default: 10)" // @Param _page query integer false "Page number for pagination (default: 1)" // @Param _include query string false "Include referenced resources" // @Param _revinclude query string false "Reverse include referenced resources" // @Success 200 {object} models.FhirBundleResponse "` + data.FhirResource + ` resources search results" // @Failure 400 {object} models.FhirOperationOutcome "Bad request - invalid search parameters" // @Failure 401 {object} models.FhirOperationOutcome "Unauthorized - invalid or expired token" // @Failure 500 {object} models.FhirOperationOutcome "Internal server error" // @Router /api/v1/` + routePath + ` [get] func (h *` + data.Name + `Handler) Search` + data.Name + `(c *gin.Context) { requestID := uuid.New().String() startTime := time.Now() h.logger.Info("Searching FHIR ` + data.FhirResource + ` resources", map[string]interface{}{ "request_id": requestID, "timestamp": startTime, "fhir_resource": "` + data.FhirResource + `", "query_params": c.Request.URL.Query(), "category_path": "` + data.Category + `", "directory_depth": ` + fmt.Sprintf("%d", data.DirectoryDepth) + `, }) ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() // Build search parameters searchParams := make(map[string]string) for key, values := range c.Request.URL.Query() { if len(values) > 0 { searchParams[key] = values[0] } } var fhirBundle models.FhirBundleResponse if err := h.service.SearchResources(ctx, "` + data.FhirResource + `", searchParams, &fhirBundle); err != nil { h.logger.Error("Failed to search FHIR resources", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "fhir_resource": "` + data.FhirResource + `", "search_params": searchParams, "path": "` + routePath + `", }) statusCode, errorCode := h.categorizeFhirError(err) h.sendFhirErrorResponse(c, statusCode, errorCode, "Failed to search ` + data.FhirResource + ` resources", err.Error(), requestID) return } duration := time.Since(startTime) h.logger.Info("FHIR ` + data.FhirResource + ` resources search completed", map[string]interface{}{ "request_id": requestID, "duration": duration.String(), "fhir_resource": "` + data.FhirResource + `", "total_results": fhirBundle.Total, "search_params": searchParams, "path": "` + routePath + `", }) h.sendFhirBundleResponse(c, http.StatusOK, "` + data.FhirResource + ` resources search completed", fhirBundle, requestID) }` } func generateSatuSehatHelperMethods(data SatuSehatHandlerData) string { return ` // Helper methods for ` + data.Name + `Handler with FHIR support func (h *` + data.Name + `Handler) sendFhirSuccessResponse(c *gin.Context, statusCode int, message string, data interface{}, requestID string) { response := models.` + data.Name + `Response{ FhirResponse: models.FhirResponse{ ResourceType: "` + data.FhirResource + `", Meta: models.FhirMeta{ LastUpdated: time.Now().Format(time.RFC3339), VersionId: "1", }, }, BaseResponse: models.BaseResponse{ Status: "success", Message: message, Data: data, Metadata: &models.ResponseMetadata{ Timestamp: time.Now(), Version: "FHIR R4", RequestID: requestID, Path: "` + data.Category + `", Depth: ` + fmt.Sprintf("%d", data.DirectoryDepth) + `, FhirResource: "` + data.FhirResource + `", }, }, } c.JSON(statusCode, response) } func (h *` + data.Name + `Handler) sendFhirErrorResponse(c *gin.Context, statusCode int, errorCode, message, details, requestID string) { operationOutcome := models.FhirOperationOutcome{ ResourceType: "OperationOutcome", ID: requestID, Meta: models.FhirMeta{ LastUpdated: time.Now().Format(time.RFC3339), VersionId: "1", }, Issue: []models.FhirOperationOutcomeIssue{{ Severity: h.mapHttpStatusToSeverity(statusCode), Code: errorCode, Details: models.FhirCodeableConcept{ Text: message, }, Diagnostics: details, }}, } c.JSON(statusCode, operationOutcome) } func (h *` + data.Name + `Handler) sendFhirOperationOutcome(c *gin.Context, statusCode int, outcome models.FhirResponse, requestID string) { c.JSON(statusCode, outcome) } func (h *` + data.Name + `Handler) sendFhirBundleResponse(c *gin.Context, statusCode int, message string, bundle models.FhirBundleResponse, requestID string) { bundle.Meta = models.FhirMeta{ LastUpdated: time.Now().Format(time.RFC3339), VersionId: "1", } c.JSON(statusCode, bundle) } func (h *` + data.Name + `Handler) formatValidationError(err error) string { if validationErrors, ok := err.(validator.ValidationErrors); ok { var messages []string for _, e := range validationErrors { switch e.Tag() { case "required": messages = append(messages, fmt.Sprintf("Field '%s' is required", e.Field())) case "min": messages = append(messages, fmt.Sprintf("Field '%s' must be at least %s characters", e.Field(), e.Param())) case "max": messages = append(messages, fmt.Sprintf("Field '%s' must be at most %s characters", e.Field(), e.Param())) case "oneof": messages = append(messages, fmt.Sprintf("Field '%s' must be one of: %s", e.Field(), e.Param())) case "url": messages = append(messages, fmt.Sprintf("Field '%s' must be a valid URL", e.Field())) case "uuid": messages = append(messages, fmt.Sprintf("Field '%s' must be a valid UUID", e.Field())) default: messages = append(messages, fmt.Sprintf("Field '%s' is invalid", e.Field())) } } return fmt.Sprintf("FHIR validation failed: %v", messages) } return err.Error() } func (h *` + data.Name + `Handler) categorizeFhirError(err error) (int, string) { if err == nil { return http.StatusOK, "informational" } errStr := err.Error() // FHIR-specific error categorization if strings.Contains(errStr, "unauthorized") || strings.Contains(errStr, "invalid token") { return http.StatusUnauthorized, "security" } if strings.Contains(errStr, "not found") || strings.Contains(errStr, "404") { return http.StatusNotFound, "not-found" } if strings.Contains(errStr, "validation") || strings.Contains(errStr, "invalid") { return http.StatusUnprocessableEntity, "business-rule" } if h.isTimeoutError(err) { return http.StatusRequestTimeout, "timeout" } if h.isNetworkError(err) { return http.StatusBadGateway, "transient" } return http.StatusInternalServerError, "exception" } func (h *` + data.Name + `Handler) mapHttpStatusToSeverity(statusCode int) string { switch { case statusCode >= 500: return "fatal" case statusCode >= 400: return "error" case statusCode >= 300: return "warning" default: return "information" } } func (h *` + data.Name + `Handler) isTimeoutError(err error) bool { return err != nil && (strings.Contains(err.Error(), "timeout") || strings.Contains(err.Error(), "deadline exceeded")) } func (h *` + data.Name + `Handler) isNetworkError(err error) bool { return err != nil && (strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "no such host") || strings.Contains(err.Error(), "network unreachable")) }` } func generateOptimizedSatuSehatModelFile(data SatuSehatHandlerData, modelDir string) { modelContent := `package models import ( "encoding/json" "fmt" "time" "regexp" ) // ` + data.Name + ` Satu Sehat FHIR R4 Models with Enhanced Multi-Level Support // Generated at: ` + data.Timestamp + ` // FHIR Resource: ` + data.FhirResource + ` // Category Path: ` + data.Category + ` // Directory Depth: ` + fmt.Sprintf("%d", data.DirectoryDepth) + ` levels // Base FHIR structures type FhirResource struct { ResourceType string ` + "`json:\"resourceType\"`" + ` ID string ` + "`json:\"id,omitempty\"`" + ` Meta FhirMeta ` + "`json:\"meta,omitempty\"`" + ` } type FhirMeta struct { VersionId string ` + "`json:\"versionId,omitempty\"`" + ` LastUpdated string ` + "`json:\"lastUpdated,omitempty\"`" + ` Profile []string ` + "`json:\"profile,omitempty\"`" + ` Security []FhirCoding ` + "`json:\"security,omitempty\"`" + ` Tag []FhirCoding ` + "`json:\"tag,omitempty\"`" + ` } type FhirCoding struct { System string ` + "`json:\"system,omitempty\"`" + ` Version string ` + "`json:\"version,omitempty\"`" + ` Code string ` + "`json:\"code,omitempty\"`" + ` Display string ` + "`json:\"display,omitempty\"`" + ` } type FhirCodeableConcept struct { Coding []FhirCoding ` + "`json:\"coding,omitempty\"`" + ` Text string ` + "`json:\"text,omitempty\"`" + ` } type FhirReference struct { Reference string ` + "`json:\"reference,omitempty\"`" + ` Type string ` + "`json:\"type,omitempty\"`" + ` Identifier FhirIdentifier ` + "`json:\"identifier,omitempty\"`" + ` Display string ` + "`json:\"display,omitempty\"`" + ` } type FhirIdentifier struct { Use string ` + "`json:\"use,omitempty\"`" + ` Type FhirCodeableConcept ` + "`json:\"type,omitempty\"`" + ` System string ` + "`json:\"system,omitempty\"`" + ` Value string ` + "`json:\"value,omitempty\"`" + ` Period FhirPeriod ` + "`json:\"period,omitempty\"`" + ` Assigner FhirReference ` + "`json:\"assigner,omitempty\"`" + ` } type FhirPeriod struct { Start string ` + "`json:\"start,omitempty\"`" + ` End string ` + "`json:\"end,omitempty\"`" + ` } // FHIR OperationOutcome for error handling type FhirOperationOutcome struct { ResourceType string ` + "`json:\"resourceType\"`" + ` ID string ` + "`json:\"id,omitempty\"`" + ` Meta FhirMeta ` + "`json:\"meta,omitempty\"`" + ` Issue []FhirOperationOutcomeIssue ` + "`json:\"issue\"`" + ` } type FhirOperationOutcomeIssue struct { Severity string ` + "`json:\"severity\"`" + ` Code string ` + "`json:\"code\"`" + ` Details FhirCodeableConcept ` + "`json:\"details,omitempty\"`" + ` Diagnostics string ` + "`json:\"diagnostics,omitempty\"`" + ` Location []string ` + "`json:\"location,omitempty\"`" + ` Expression []string ` + "`json:\"expression,omitempty\"`" + ` } // FHIR Bundle for search results type FhirBundleResponse struct { ResourceType string ` + "`json:\"resourceType\"`" + ` ID string ` + "`json:\"id,omitempty\"`" + ` Meta FhirMeta ` + "`json:\"meta,omitempty\"`" + ` Type string ` + "`json:\"type\"`" + ` Total int ` + "`json:\"total,omitempty\"`" + ` Link []FhirBundleLink ` + "`json:\"link,omitempty\"`" + ` Entry []FhirBundleEntry ` + "`json:\"entry,omitempty\"`" + ` } type FhirBundleLink struct { Relation string ` + "`json:\"relation\"`" + ` URL string ` + "`json:\"url\"`" + ` } type FhirBundleEntry struct { FullURL string ` + "`json:\"fullUrl,omitempty\"`" + ` Resource interface{} ` + "`json:\"resource,omitempty\"`" + ` Search FhirBundleEntrySearch ` + "`json:\"search,omitempty\"`" + ` } type FhirBundleEntrySearch struct { Mode string ` + "`json:\"mode,omitempty\"`" + ` Score string ` + "`json:\"score,omitempty\"`" + ` } // Base request/response structures with FHIR integration type BaseRequest struct { RequestID string ` + "`json:\"request_id,omitempty\"`" + ` Timestamp time.Time ` + "`json:\"timestamp,omitempty\"`" + ` } type BaseResponse struct { Status string ` + "`json:\"status\"`" + ` Message string ` + "`json:\"message\"`" + ` Data interface{} ` + "`json:\"data,omitempty\"`" + ` Error *ErrorResponse ` + "`json:\"error,omitempty\"`" + ` Metadata *ResponseMetadata ` + "`json:\"metadata,omitempty\"`" + ` } type ErrorResponse struct { Code string ` + "`json:\"code\"`" + ` Message string ` + "`json:\"message\"`" + ` Details string ` + "`json:\"details,omitempty\"`" + ` } type ResponseMetadata struct { Timestamp time.Time ` + "`json:\"timestamp\"`" + ` Version string ` + "`json:\"version\"`" + ` RequestID string ` + "`json:\"request_id,omitempty\"`" + ` Path string ` + "`json:\"path,omitempty\"`" + ` Depth int ` + "`json:\"depth,omitempty\"`" + ` FhirResource string ` + "`json:\"fhir_resource,omitempty\"`" + ` } // ` + data.Name + ` Response Structure with FHIR integration type ` + data.Name + `Response struct { FhirResource BaseResponse } // Generic FHIR Response type FhirResponse struct { ResourceType string ` + "`json:\"resourceType\"`" + ` ID string ` + "`json:\"id,omitempty\"`" + ` Meta FhirMeta ` + "`json:\"meta,omitempty\"`" + ` Content map[string]interface{} ` + "`json:\"-\"`" + ` // For dynamic content }` // Add CRUD request structures based on methods if data.HasPost { modelContent += ` // ` + data.Name + ` CREATE Request Structure with FHIR R4 Validation type ` + data.Name + `CreateRequest struct { BaseRequest ResourceType string ` + "`json:\"resourceType\" binding:\"required\" validate:\"required,eq=` + data.FhirResource + `\"`" + ` // Core FHIR ` + data.FhirResource + ` fields - customize based on specific resource // Path: ` + data.Category + ` Meta FhirMeta ` + "`json:\"meta,omitempty\"`" + ` Identifier []FhirIdentifier ` + "`json:\"identifier,omitempty\" validate:\"dive\"`" + ` Active *bool ` + "`json:\"active,omitempty\"`" + ` Name []FhirHumanName ` + "`json:\"name,omitempty\" validate:\"dive\"`" + ` Telecom []FhirContactPoint ` + "`json:\"telecom,omitempty\" validate:\"dive\"`" + ` Gender string ` + "`json:\"gender,omitempty\" validate:\"omitempty,oneof=male female other unknown\"`" + ` BirthDate string ` + "`json:\"birthDate,omitempty\" validate:\"omitempty,datetime=2006-01-02\"`" + ` Address []FhirAddress ` + "`json:\"address,omitempty\" validate:\"dive\"`" + ` } // Additional FHIR data types for ` + data.FhirResource + ` type FhirHumanName struct { Use string ` + "`json:\"use,omitempty\" validate:\"omitempty,oneof=usual official temp nickname anonymous old maiden\"`" + ` Text string ` + "`json:\"text,omitempty\"`" + ` Family string ` + "`json:\"family,omitempty\"`" + ` Given []string ` + "`json:\"given,omitempty\"`" + ` Prefix []string ` + "`json:\"prefix,omitempty\"`" + ` Suffix []string ` + "`json:\"suffix,omitempty\"`" + ` Period FhirPeriod ` + "`json:\"period,omitempty\"`" + ` } type FhirContactPoint struct { System string ` + "`json:\"system,omitempty\" validate:\"omitempty,oneof=phone fax email pager url sms other\"`" + ` Value string ` + "`json:\"value,omitempty\"`" + ` Use string ` + "`json:\"use,omitempty\" validate:\"omitempty,oneof=home work temp old mobile\"`" + ` Rank int ` + "`json:\"rank,omitempty\" validate:\"omitempty,min=1\"`" + ` Period FhirPeriod ` + "`json:\"period,omitempty\"`" + ` } type FhirAddress struct { Use string ` + "`json:\"use,omitempty\" validate:\"omitempty,oneof=home work temp old billing\"`" + ` Type string ` + "`json:\"type,omitempty\" validate:\"omitempty,oneof=postal physical both\"`" + ` Text string ` + "`json:\"text,omitempty\"`" + ` Line []string ` + "`json:\"line,omitempty\"`" + ` City string ` + "`json:\"city,omitempty\"`" + ` District string ` + "`json:\"district,omitempty\"`" + ` State string ` + "`json:\"state,omitempty\"`" + ` PostalCode string ` + "`json:\"postalCode,omitempty\"`" + ` Country string ` + "`json:\"country,omitempty\"`" + ` Period FhirPeriod ` + "`json:\"period,omitempty\"`" + ` } // ValidateFhir validates the ` + data.Name + `CreateRequest with FHIR R4 business rules func (r *` + data.Name + `CreateRequest) ValidateFhir() error { if r.ResourceType != "` + data.FhirResource + `" { return fmt.Errorf("invalid resourceType: expected ` + data.FhirResource + `, got %s", r.ResourceType) } // Validate required identifiers for Indonesian context if len(r.Identifier) == 0 { return fmt.Errorf("at least one identifier is required for ` + data.FhirResource + ` resource") } // Validate NIK if present for _, identifier := range r.Identifier { if identifier.System == "https://fhir.kemkes.go.id/id/nik" { if !isValidNIK(identifier.Value) { return fmt.Errorf("invalid NIK format: %s", identifier.Value) } } } // Path: ` + data.Category + ` return nil } // ToJSON converts struct to JSON string func (r *` + data.Name + `CreateRequest) ToJSON() (string, error) { data, err := json.Marshal(r) return string(data), err }` } if data.HasPut { modelContent += ` // ` + data.Name + ` UPDATE Request Structure with FHIR R4 Validation type ` + data.Name + `UpdateRequest struct { BaseRequest ID string ` + "`json:\"id\" binding:\"required\" validate:\"required,uuid\"`" + ` ResourceType string ` + "`json:\"resourceType\" binding:\"required\" validate:\"required,eq=` + data.FhirResource + `\"`" + ` Meta FhirMeta ` + "`json:\"meta,omitempty\"`" + ` Identifier []FhirIdentifier ` + "`json:\"identifier,omitempty\" validate:\"dive\"`" + ` Active *bool ` + "`json:\"active,omitempty\"`" + ` Name []FhirHumanName ` + "`json:\"name,omitempty\" validate:\"dive\"`" + ` Telecom []FhirContactPoint ` + "`json:\"telecom,omitempty\" validate:\"dive\"`" + ` Gender string ` + "`json:\"gender,omitempty\" validate:\"omitempty,oneof=male female other unknown\"`" + ` BirthDate string ` + "`json:\"birthDate,omitempty\" validate:\"omitempty,datetime=2006-01-02\"`" + ` Address []FhirAddress ` + "`json:\"address,omitempty\" validate:\"dive\"`" + ` } // ValidateFhir validates the ` + data.Name + `UpdateRequest with FHIR R4 business rules func (r *` + data.Name + `UpdateRequest) ValidateFhir() error { if r.ResourceType != "` + data.FhirResource + `" { return fmt.Errorf("invalid resourceType: expected ` + data.FhirResource + `, got %s", r.ResourceType) } if r.ID == "" { return fmt.Errorf("resource ID is required for update operation") } // Path: ` + data.Category + ` return nil } // ToJSON converts struct to JSON string func (r *` + data.Name + `UpdateRequest) ToJSON() (string, error) { data, err := json.Marshal(r) return string(data), err }` } if data.HasPatch { modelContent += ` // ` + data.Name + ` PATCH Request Structure with FHIR R4 JSON Patch type ` + data.Name + `PatchRequest struct { BaseRequest ID string ` + "`json:\"id\" binding:\"required\" validate:\"required,uuid\"`" + ` Patches []FhirJsonPatch ` + "`json:\"patches\" binding:\"required\" validate:\"required,dive\"`" + ` } type FhirJsonPatch struct { Op string ` + "`json:\"op\" binding:\"required\" validate:\"required,oneof=add remove replace move copy test\"`" + ` Path string ` + "`json:\"path\" binding:\"required\" validate:\"required\"`" + ` Value interface{} ` + "`json:\"value,omitempty\"`" + ` From string ` + "`json:\"from,omitempty\"`" + ` } // ValidateFhirPatch validates the ` + data.Name + `PatchRequest with FHIR R4 patch rules func (r *` + data.Name + `PatchRequest) ValidateFhirPatch() error { if r.ID == "" { return fmt.Errorf("resource ID is required for patch operation") } if len(r.Patches) == 0 { return fmt.Errorf("at least one patch operation is required") } // Validate each patch operation for i, patch := range r.Patches { if patch.Path == "" { return fmt.Errorf("patch[%d]: path is required", i) } if patch.Op == "move" || patch.Op == "copy" { if patch.From == "" { return fmt.Errorf("patch[%d]: from is required for %s operation", i, patch.Op) } } if patch.Op != "remove" && patch.Op != "test" && patch.Value == nil { return fmt.Errorf("patch[%d]: value is required for %s operation", i, patch.Op) } } // Path: ` + data.Category + ` return nil } // ToJSON converts struct to JSON string func (r *` + data.Name + `PatchRequest) ToJSON() (string, error) { data, err := json.Marshal(r) return string(data), err }` } // Add validation helper functions modelContent += ` // FHIR validation helper functions for Indonesian context func isValidNIK(nik string) bool { if len(nik) != 16 { return false } // Check if all characters are digits matched, _ := regexp.MatchString("^[0-9]{16}$", nik) return matched } func isValidFhirDate(date string) bool { // FHIR date format: YYYY, YYYY-MM, or YYYY-MM-DD patterns := []string{ "^[0-9]{4}$", // Year only "^[0-9]{4}-[0-9]{2}$", // Year-Month "^[0-9]{4}-[0-9]{2}-[0-9]{2}$", // Full date } for _, pattern := range patterns { matched, _ := regexp.MatchString(pattern, date) if matched { return true } } return false } func isValidFhirDateTime(datetime string) bool { // FHIR datetime format with timezone pattern := "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|[+-][0-9]{2}:[0-9]{2})$" matched, _ := regexp.MatchString(pattern, datetime) return matched } func isValidFhirID(id string) bool { // FHIR ID: 1-64 characters, alphanumeric, dash, dot if len(id) < 1 || len(id) > 64 { return false } pattern := "^[A-Za-z0-9\\-\\.]{1,64}$" matched, _ := regexp.MatchString(pattern, id) return matched } // GetPathInfo returns information about the multi-level FHIR path func GetFhirPathInfo() map[string]interface{} { return map[string]interface{}{ "fhir_resource": "` + data.FhirResource + `", "path": "` + data.Category + `", "depth": ` + fmt.Sprintf("%d", data.DirectoryDepth) + `, "parts": []string{` + `"` + strings.Join(data.CategoryParts, `", "`) + `"` + `}, "base_url": "https://api-satusehat-stg.dto.kemkes.go.id/fhir-r4/v1", "profile_url": "https://fhir.kemkes.go.id/r4/StructureDefinition/` + data.FhirResource + `", } }` writeFile(filepath.Join(modelDir, data.NameLower+".go"), modelContent) } // Continue with model generation, routes generation, and utility functions... // [Model generation and other functions would continue here following the same pattern] func writeFile(filename, content string) { if err := os.WriteFile(filename, []byte(content), 0644); err != nil { fmt.Printf("❌ Error creating file %s: %v\n", filename, err) return } fmt.Printf("✅ Generated Satu Sehat FHIR file: %s\n", filename) }