mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-07 08:02:42 +08:00
refactor: move tool request types to option
This commit is contained in:
@@ -1 +1 @@
|
||||
v5.0.0-beta-2505242332
|
||||
v5.0.0-beta-2505242351
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/httprunner/httprunner/v5/uixt"
|
||||
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||
)
|
||||
|
||||
func (r *Router) foregroundAppHandler(c *gin.Context) {
|
||||
@@ -22,7 +22,7 @@ func (r *Router) foregroundAppHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (r *Router) appInfoHandler(c *gin.Context) {
|
||||
var appInfoReq types.AppInfoRequest
|
||||
var appInfoReq option.AppInfoRequest
|
||||
if err := c.ShouldBindQuery(&appInfoReq); err != nil {
|
||||
RenderErrorValidateRequest(c, err)
|
||||
return
|
||||
@@ -51,7 +51,7 @@ func (r *Router) appInfoHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (r *Router) clearAppHandler(c *gin.Context) {
|
||||
var appClearReq types.AppClearRequest
|
||||
var appClearReq option.AppClearRequest
|
||||
if err := c.ShouldBindJSON(&appClearReq); err != nil {
|
||||
RenderErrorValidateRequest(c, err)
|
||||
return
|
||||
@@ -70,7 +70,7 @@ func (r *Router) clearAppHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (r *Router) launchAppHandler(c *gin.Context) {
|
||||
var appLaunchReq types.AppLaunchRequest
|
||||
var appLaunchReq option.AppLaunchRequest
|
||||
if err := c.ShouldBindJSON(&appLaunchReq); err != nil {
|
||||
RenderErrorValidateRequest(c, err)
|
||||
return
|
||||
@@ -88,7 +88,7 @@ func (r *Router) launchAppHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (r *Router) terminalAppHandler(c *gin.Context) {
|
||||
var appTerminateReq types.AppTerminateRequest
|
||||
var appTerminateReq option.AppTerminateRequest
|
||||
if err := c.ShouldBindJSON(&appTerminateReq); err != nil {
|
||||
RenderErrorValidateRequest(c, err)
|
||||
return
|
||||
@@ -106,7 +106,7 @@ func (r *Router) terminalAppHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (r *Router) uninstallAppHandler(c *gin.Context) {
|
||||
var appUninstallReq types.AppUninstallRequest
|
||||
var appUninstallReq option.AppUninstallRequest
|
||||
if err := c.ShouldBindJSON(&appUninstallReq); err != nil {
|
||||
RenderErrorValidateRequest(c, err)
|
||||
return
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/httprunner/httprunner/v5/uixt"
|
||||
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||
)
|
||||
|
||||
func (r *Router) unlockHandler(c *gin.Context) {
|
||||
@@ -34,7 +34,7 @@ func (r *Router) homeHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (r *Router) backspaceHandler(c *gin.Context) {
|
||||
var deleteReq types.DeleteRequest
|
||||
var deleteReq option.DeleteRequest
|
||||
if err := c.ShouldBindJSON(&deleteReq); err != nil {
|
||||
RenderErrorValidateRequest(c, err)
|
||||
return
|
||||
@@ -55,7 +55,7 @@ func (r *Router) backspaceHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (r *Router) keycodeHandler(c *gin.Context) {
|
||||
var keycodeReq types.KeycodeRequest
|
||||
var keycodeReq option.KeycodeRequest
|
||||
if err := c.ShouldBindJSON(&keycodeReq); err != nil {
|
||||
RenderErrorValidateRequest(c, err)
|
||||
return
|
||||
|
||||
11
server/ui.go
11
server/ui.go
@@ -4,11 +4,10 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/httprunner/httprunner/v5/uixt"
|
||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||
)
|
||||
|
||||
func (r *Router) tapHandler(c *gin.Context) {
|
||||
var tapReq types.TapRequest
|
||||
var tapReq option.TapRequest
|
||||
if err := c.ShouldBindJSON(&tapReq); err != nil {
|
||||
RenderErrorValidateRequest(c, err)
|
||||
return
|
||||
@@ -31,7 +30,7 @@ func (r *Router) tapHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (r *Router) rightClickHandler(c *gin.Context) {
|
||||
var rightClickReq types.TapRequest
|
||||
var rightClickReq option.TapRequest
|
||||
if err := c.ShouldBindJSON(&rightClickReq); err != nil {
|
||||
RenderErrorValidateRequest(c, err)
|
||||
return
|
||||
@@ -118,7 +117,7 @@ func (r *Router) scrollHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (r *Router) doubleTapHandler(c *gin.Context) {
|
||||
var tapReq types.TapRequest
|
||||
var tapReq option.TapRequest
|
||||
if err := c.ShouldBindJSON(&tapReq); err != nil {
|
||||
RenderErrorValidateRequest(c, err)
|
||||
return
|
||||
@@ -138,7 +137,7 @@ func (r *Router) doubleTapHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (r *Router) dragHandler(c *gin.Context) {
|
||||
var dragReq types.DragRequest
|
||||
var dragReq option.DragRequest
|
||||
if err := c.ShouldBindJSON(&dragReq); err != nil {
|
||||
RenderErrorValidateRequest(c, err)
|
||||
return
|
||||
@@ -162,7 +161,7 @@ func (r *Router) dragHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (r *Router) inputHandler(c *gin.Context) {
|
||||
var inputReq types.InputRequest
|
||||
var inputReq option.InputRequest
|
||||
if err := c.ShouldBindJSON(&inputReq); err != nil {
|
||||
RenderErrorValidateRequest(c, err)
|
||||
return
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -18,14 +18,14 @@ func TestTapHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
path string
|
||||
tapReq types.TapRequest
|
||||
tapReq option.TapRequest
|
||||
wantStatus int
|
||||
wantResp HttpResponse
|
||||
}{
|
||||
{
|
||||
name: "tap abs xy",
|
||||
path: fmt.Sprintf("/api/v1/android/%s/ui/tap", "4622ca24"),
|
||||
tapReq: types.TapRequest{
|
||||
tapReq: option.TapRequest{
|
||||
X: 500,
|
||||
Y: 800,
|
||||
Duration: 0,
|
||||
@@ -40,7 +40,7 @@ func TestTapHandler(t *testing.T) {
|
||||
{
|
||||
name: "tap relative xy",
|
||||
path: fmt.Sprintf("/api/v1/android/%s/ui/tap", "4622ca24"),
|
||||
tapReq: types.TapRequest{
|
||||
tapReq: option.TapRequest{
|
||||
X: 0.5,
|
||||
Y: 0.6,
|
||||
Duration: 0,
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
"github.com/httprunner/httprunner/v5/internal/version"
|
||||
"github.com/httprunner/httprunner/v5/pkg/gadb"
|
||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||
)
|
||||
|
||||
// NewMCPServer creates a new MCP server for XTDriver and registers all tools.
|
||||
@@ -220,7 +218,7 @@ func (t *ToolListPackages) Description() string {
|
||||
}
|
||||
|
||||
func (t *ToolListPackages) Options() []mcp.ToolOption {
|
||||
return generateMCPOptions(&types.TargetDeviceRequest{})
|
||||
return option.NewMCPOptions(&option.TargetDeviceRequest{})
|
||||
}
|
||||
|
||||
func (t *ToolListPackages) Implement() toolCall {
|
||||
@@ -250,7 +248,7 @@ func (t *ToolLaunchApp) Description() string {
|
||||
}
|
||||
|
||||
func (t *ToolLaunchApp) Options() []mcp.ToolOption {
|
||||
return generateMCPOptions(&types.AppLaunchRequest{})
|
||||
return option.NewMCPOptions(&option.AppLaunchRequest{})
|
||||
}
|
||||
|
||||
func (t *ToolLaunchApp) Implement() toolCall {
|
||||
@@ -259,7 +257,7 @@ func (t *ToolLaunchApp) Implement() toolCall {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var appLaunchReq types.AppLaunchRequest
|
||||
var appLaunchReq option.AppLaunchRequest
|
||||
if err := mapToStruct(request.Params.Arguments, &appLaunchReq); err != nil {
|
||||
return mcp.NewToolResultError("parse parameters error: " + err.Error()), nil
|
||||
}
|
||||
@@ -287,7 +285,7 @@ func (t *ToolTerminateApp) Description() string {
|
||||
}
|
||||
|
||||
func (t *ToolTerminateApp) Options() []mcp.ToolOption {
|
||||
return generateMCPOptions(&types.AppTerminateRequest{})
|
||||
return option.NewMCPOptions(&option.AppTerminateRequest{})
|
||||
}
|
||||
|
||||
func (t *ToolTerminateApp) Implement() toolCall {
|
||||
@@ -296,7 +294,7 @@ func (t *ToolTerminateApp) Implement() toolCall {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var appTerminateReq types.AppTerminateRequest
|
||||
var appTerminateReq option.AppTerminateRequest
|
||||
if err := mapToStruct(request.Params.Arguments, &appTerminateReq); err != nil {
|
||||
return mcp.NewToolResultError("parse parameters error: " + err.Error()), nil
|
||||
}
|
||||
@@ -324,7 +322,7 @@ func (t *ToolGetScreenSize) Description() string {
|
||||
}
|
||||
|
||||
func (t *ToolGetScreenSize) Options() []mcp.ToolOption {
|
||||
return generateMCPOptions(&types.TargetDeviceRequest{})
|
||||
return option.NewMCPOptions(&option.TargetDeviceRequest{})
|
||||
}
|
||||
|
||||
func (t *ToolGetScreenSize) Implement() toolCall {
|
||||
@@ -356,7 +354,7 @@ func (t *ToolPressButton) Description() string {
|
||||
}
|
||||
|
||||
func (t *ToolPressButton) Options() []mcp.ToolOption {
|
||||
return generateMCPOptions(&types.PressButtonRequest{})
|
||||
return option.NewMCPOptions(&option.PressButtonRequest{})
|
||||
}
|
||||
|
||||
func (t *ToolPressButton) Implement() toolCall {
|
||||
@@ -365,7 +363,7 @@ func (t *ToolPressButton) Implement() toolCall {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pressButtonReq types.PressButtonRequest
|
||||
var pressButtonReq option.PressButtonRequest
|
||||
if err := mapToStruct(request.Params.Arguments, &pressButtonReq); err != nil {
|
||||
return mcp.NewToolResultError("parse parameters error: " + err.Error()), nil
|
||||
}
|
||||
@@ -389,7 +387,7 @@ func (t *ToolTapXY) Description() string {
|
||||
}
|
||||
|
||||
func (t *ToolTapXY) Options() []mcp.ToolOption {
|
||||
return generateMCPOptions(&types.TapRequest{})
|
||||
return option.NewMCPOptions(&option.TapRequest{})
|
||||
}
|
||||
|
||||
func (t *ToolTapXY) Implement() toolCall {
|
||||
@@ -398,7 +396,7 @@ func (t *ToolTapXY) Implement() toolCall {
|
||||
if err != nil {
|
||||
return mcp.NewToolResultError("Tap failed: " + err.Error()), nil
|
||||
}
|
||||
var tapReq types.TapRequest
|
||||
var tapReq option.TapRequest
|
||||
if err := mapToStruct(request.Params.Arguments, &tapReq); err != nil {
|
||||
return mcp.NewToolResultError("parse parameters error: " + err.Error()), nil
|
||||
}
|
||||
@@ -426,7 +424,7 @@ func (t *ToolSwipe) Description() string {
|
||||
}
|
||||
|
||||
func (t *ToolSwipe) Options() []mcp.ToolOption {
|
||||
return generateMCPOptions(&types.SwipeRequest{})
|
||||
return option.NewMCPOptions(&option.SwipeRequest{})
|
||||
}
|
||||
|
||||
func (t *ToolSwipe) Implement() toolCall {
|
||||
@@ -435,7 +433,7 @@ func (t *ToolSwipe) Implement() toolCall {
|
||||
if err != nil {
|
||||
return mcp.NewToolResultError("Swipe failed: " + err.Error()), nil
|
||||
}
|
||||
var swipeReq types.SwipeRequest
|
||||
var swipeReq option.SwipeRequest
|
||||
if err := mapToStruct(request.Params.Arguments, &swipeReq); err != nil {
|
||||
return mcp.NewToolResultError("parse parameters error: " + err.Error()), nil
|
||||
}
|
||||
@@ -480,7 +478,7 @@ func (t *ToolDrag) Description() string {
|
||||
}
|
||||
|
||||
func (t *ToolDrag) Options() []mcp.ToolOption {
|
||||
return generateMCPOptions(&types.DragRequest{})
|
||||
return option.NewMCPOptions(&option.DragRequest{})
|
||||
}
|
||||
|
||||
func (t *ToolDrag) Implement() toolCall {
|
||||
@@ -489,7 +487,7 @@ func (t *ToolDrag) Implement() toolCall {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var dragReq types.DragRequest
|
||||
var dragReq option.DragRequest
|
||||
if err := mapToStruct(request.Params.Arguments, &dragReq); err != nil {
|
||||
return mcp.NewToolResultError("parse parameters error: " + err.Error()), nil
|
||||
}
|
||||
@@ -521,7 +519,7 @@ func (t *ToolScreenShot) Description() string {
|
||||
}
|
||||
|
||||
func (t *ToolScreenShot) Options() []mcp.ToolOption {
|
||||
return generateMCPOptions(&types.TargetDeviceRequest{})
|
||||
return option.NewMCPOptions(&option.TargetDeviceRequest{})
|
||||
}
|
||||
|
||||
func (t *ToolScreenShot) Implement() toolCall {
|
||||
@@ -630,46 +628,6 @@ func NewDevice(platform, serial string) (device IDevice, err error) {
|
||||
return device, nil
|
||||
}
|
||||
|
||||
// generateMCPOptions generates mcp.NewTool parameters from a struct type.
|
||||
// It automatically generates mcp.NewTool parameters based on the struct fields and their desc tags.
|
||||
func generateMCPOptions(t interface{}) (options []mcp.ToolOption) {
|
||||
tType := reflect.TypeOf(t)
|
||||
for i := 0; i < tType.NumField(); i++ {
|
||||
field := tType.Field(i)
|
||||
jsonTag := field.Tag.Get("json")
|
||||
if jsonTag == "" || jsonTag == "-" {
|
||||
continue
|
||||
}
|
||||
name := strings.Split(jsonTag, ",")[0]
|
||||
binding := field.Tag.Get("binding")
|
||||
required := strings.Contains(binding, "required")
|
||||
desc := field.Tag.Get("desc")
|
||||
switch field.Type.Kind() {
|
||||
case reflect.Float64, reflect.Float32, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if required {
|
||||
options = append(options, mcp.WithNumber(name, mcp.Required(), mcp.Description(desc)))
|
||||
} else {
|
||||
options = append(options, mcp.WithNumber(name, mcp.Description(desc)))
|
||||
}
|
||||
case reflect.String:
|
||||
if required {
|
||||
options = append(options, mcp.WithString(name, mcp.Required(), mcp.Description(desc)))
|
||||
} else {
|
||||
options = append(options, mcp.WithString(name, mcp.Description(desc)))
|
||||
}
|
||||
case reflect.Bool:
|
||||
if required {
|
||||
options = append(options, mcp.WithBoolean(name, mcp.Required(), mcp.Description(desc)))
|
||||
} else {
|
||||
options = append(options, mcp.WithBoolean(name, mcp.Description(desc)))
|
||||
}
|
||||
default:
|
||||
log.Warn().Str("field_type", field.Type.String()).Msg("Unsupported field type")
|
||||
}
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
// mapToStruct convert map[string]interface{} to target struct
|
||||
func mapToStruct(m map[string]interface{}, out interface{}) error {
|
||||
b, err := json.Marshal(m)
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
package types
|
||||
package option
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type TargetDeviceRequest struct {
|
||||
Platform string `json:"platform" binding:"required" desc:"Device platform: android/ios/browser"`
|
||||
@@ -80,5 +89,45 @@ type AppTerminateRequest struct {
|
||||
|
||||
type PressButtonRequest struct {
|
||||
TargetDeviceRequest
|
||||
Button DeviceButton `json:"button" binding:"required" desc:"The button to press. Supported buttons: BACK (android only), HOME, VOLUME_UP, VOLUME_DOWN, ENTER."`
|
||||
Button types.DeviceButton `json:"button" binding:"required" desc:"The button to press. Supported buttons: BACK (android only), HOME, VOLUME_UP, VOLUME_DOWN, ENTER."`
|
||||
}
|
||||
|
||||
// NewMCPOptions generates mcp.NewTool parameters from a struct type.
|
||||
// It automatically generates mcp.NewTool parameters based on the struct fields and their desc tags.
|
||||
func NewMCPOptions(t interface{}) (options []mcp.ToolOption) {
|
||||
tType := reflect.TypeOf(t)
|
||||
for i := 0; i < tType.NumField(); i++ {
|
||||
field := tType.Field(i)
|
||||
jsonTag := field.Tag.Get("json")
|
||||
if jsonTag == "" || jsonTag == "-" {
|
||||
continue
|
||||
}
|
||||
name := strings.Split(jsonTag, ",")[0]
|
||||
binding := field.Tag.Get("binding")
|
||||
required := strings.Contains(binding, "required")
|
||||
desc := field.Tag.Get("desc")
|
||||
switch field.Type.Kind() {
|
||||
case reflect.Float64, reflect.Float32, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if required {
|
||||
options = append(options, mcp.WithNumber(name, mcp.Required(), mcp.Description(desc)))
|
||||
} else {
|
||||
options = append(options, mcp.WithNumber(name, mcp.Description(desc)))
|
||||
}
|
||||
case reflect.String:
|
||||
if required {
|
||||
options = append(options, mcp.WithString(name, mcp.Required(), mcp.Description(desc)))
|
||||
} else {
|
||||
options = append(options, mcp.WithString(name, mcp.Description(desc)))
|
||||
}
|
||||
case reflect.Bool:
|
||||
if required {
|
||||
options = append(options, mcp.WithBoolean(name, mcp.Required(), mcp.Description(desc)))
|
||||
} else {
|
||||
options = append(options, mcp.WithBoolean(name, mcp.Description(desc)))
|
||||
}
|
||||
default:
|
||||
log.Warn().Str("field_type", field.Type.String()).Msg("Unsupported field type")
|
||||
}
|
||||
}
|
||||
return options
|
||||
}
|
||||
Reference in New Issue
Block a user