heroagent/pkg/servers/ui/controllers/openrpc_controller.go
2025-05-24 10:33:50 +04:00

174 lines
4.7 KiB
Go

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, &params); 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
}
}