package controllers import ( "encoding/json" "log" orpcmodels "git.ourworld.tf/herocode/heroagent/pkg/openrpc/models" uimodels "git.ourworld.tf/herocode/heroagent/pkg/servers/ui/models" "github.com/gofiber/fiber/v2" ) // OpenRPCController handles requests related to OpenRPC specifications type OpenRPCController struct { openrpcManager uimodels.OpenRPCUIManager } // NewOpenRPCController creates a new instance of OpenRPCController func NewOpenRPCController(openrpcManager uimodels.OpenRPCUIManager) *OpenRPCController { return &OpenRPCController{ openrpcManager: openrpcManager, } } // OpenRPCPageData represents the data needed for the OpenRPC UI pages type OpenRPCPageData struct { Title string Specs []string SelectedSpec string Methods []string SelectedMethod string Method *orpcmodels.Method SocketPath string ExampleParams string Result string Error string } // ShowOpenRPCUI renders the OpenRPC UI page func (c *OpenRPCController) ShowOpenRPCUI(ctx *fiber.Ctx) error { // Get query parameters selectedSpec := ctx.Query("spec", "") selectedMethod := ctx.Query("method", "") socketPath := ctx.Query("socketPath", "") // Get all specs specs := c.openrpcManager.ListSpecs() // Initialize page data using fiber.Map instead of struct pageData := fiber.Map{ "Title": "OpenRPC UI", "SpecList": specs, "SelectedSpec": selectedSpec, "SocketPath": socketPath, } // If a spec is selected, get its methods if selectedSpec != "" { methods := c.openrpcManager.ListMethods(selectedSpec) pageData["Methods"] = methods pageData["SelectedMethod"] = selectedMethod // If a method is selected, get its details if selectedMethod != "" { method := c.openrpcManager.GetMethod(selectedSpec, selectedMethod) if method != nil { pageData["Method"] = method // Generate example parameters if available if len(method.Examples) > 0 { exampleParams, err := json.MarshalIndent(method.Examples[0].Params, "", " ") if err == nil { pageData["ExampleParams"] = string(exampleParams) } } else if len(method.Params) > 0 { // Generate example from parameter schema exampleParams := generateExampleParams(method.Params) jsonParams, err := json.MarshalIndent(exampleParams, "", " ") if err == nil { pageData["ExampleParams"] = string(jsonParams) } } } } } return ctx.Render("pages/rpcui", pageData) } // ExecuteRPC handles RPC execution requests func (c *OpenRPCController) ExecuteRPC(ctx *fiber.Ctx) error { // Parse request var request struct { Spec string `json:"spec"` Method string `json:"method"` SocketPath string `json:"socketPath"` Params json.RawMessage `json:"params"` } if err := ctx.BodyParser(&request); err != nil { return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Invalid request: " + err.Error(), }) } // Validate request if request.Spec == "" || request.Method == "" || request.SocketPath == "" { return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Missing required fields: spec, method, or socketPath", }) } // Parse params var params interface{} if len(request.Params) > 0 { if err := json.Unmarshal(request.Params, ¶ms); err != nil { return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Invalid parameters: " + err.Error(), }) } } // Execute RPC result, err := c.openrpcManager.ExecuteRPC(request.Spec, request.Method, request.SocketPath, params) if err != nil { log.Printf("Error executing RPC: %v", err) return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": err.Error(), }) } // Return result return ctx.JSON(fiber.Map{ "result": result, }) } // generateExampleParams generates example parameters from parameter schemas func generateExampleParams(params []orpcmodels.Parameter) map[string]interface{} { example := make(map[string]interface{}) for _, param := range params { example[param.Name] = generateExampleValue(param.Schema) } return example } // generateExampleValue generates an example value from a schema func generateExampleValue(schema orpcmodels.SchemaObject) interface{} { switch schema.Type { case "string": return "example" case "number": return 0 case "integer": return 0 case "boolean": return false case "array": if schema.Items != nil { return []interface{}{generateExampleValue(*schema.Items)} } return []interface{}{} case "object": obj := make(map[string]interface{}) for name, propSchema := range schema.Properties { obj[name] = generateExampleValue(propSchema) } return obj default: return nil } }