diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml index 332b9da54..9a16024e8 100644 --- a/.github/workflows/makefile.yml +++ b/.github/workflows/makefile.yml @@ -3,7 +3,7 @@ name: demo site deploy on: workflow_dispatch # pull_request: - # branches: [ "main" ] +# branches: [ "main" ] # push: # branches: [ "main" ] @@ -12,14 +12,14 @@ jobs: runs-on: self-hosted defaults: run: - working-directory: /home/ubuntu/rd/project/deeptest + working-directory: /rd/project/deeptest steps: - name: checkout backend codes run: git pull - name: checkout frontend codes run: git pull - working-directory: /home/ubuntu/rd/project/deeptest-ui + working-directory: /rd/project/deeptest-ui - name: make server run: ENV=dp make compile_server_linux @@ -32,15 +32,16 @@ jobs: - name: copy files run: | - cp -f bin/linux/deeptest-server /home/ubuntu/rd/server/ - cp -f client/bin/linux/deeptest-agent /home/ubuntu/rd/server/ + mkdir -p /rd/server + cp -f bin/linux/deeptest-server /rd/server/ + cp -f client/bin/linux/deeptest-agent /rd/server/ - rm -rf /home/ubuntu/rd/server/deeptest-ui - cp -fr client/ui /home/ubuntu/rd/server/deeptest-ui + rm -rf /rd/server/deeptest-ui + cp -fr client/ui /rd/server/deeptest-ui - name: start service run: | ps -ef | grep 'deeptest-' | grep -v grep | awk '{print $2}' | xargs --no-run-if-empty kill -9 - cd /home/ubuntu/rd/server + cd /rd/server RUNNER_TRACKING_ID="" && nohup ./deeptest-server > server.log 2>&1 & RUNNER_TRACKING_ID="" && nohup ./deeptest-agent > agent.log 2>&1 & \ No newline at end of file diff --git a/Makefile b/Makefile index 37bfc26db..74d235d91 100644 --- a/Makefile +++ b/Makefile @@ -48,12 +48,19 @@ GIT_HASH=`git show -s --format=%H` BUILD_CMD_UNIX=go build -ldflags "-X 'main.AppVersion=${VERSION}' -X 'main.BuildTime=${BUILD_TIME}' -X 'main.GoVersion=${GO_VERSION}' -X 'main.GitHash=${GIT_HASH}'" BUILD_CMD_WIN=go build -ldflags "-s -w -X 'main.AppVersion=${VERSION}' -X 'main.BuildTime=${BUILD_TIME}' -X 'main.GoVersion=${GO_VERSION}' -X 'main.GitHash=${GIT_HASH}'" -default: win64 win32 linux mac +default: compile_ui_web win64 win32 linux mac -win64: prepare build_gui_win64 compile_launcher_win64 compile_server_win64 copy_files_win64 zip_win64 zip_win64_upgrade -win32: prepare build_gui_win32 compile_launcher_win32 compile_server_win32 copy_files_win32 zip_win32 zip_win32_upgrade -linux: prepare build_gui_linux compile_server_linux copy_files_linux zip_linux zip_linux_upgrade -mac: prepare build_gui_mac compile_server_mac copy_files_mac zip_mac zip_mac_upgrade +# 非客户端版本打包 +win64: prepare compile_agent_win64 compile_server_win64 zip_web_win64 +win32: prepare compile_agent_win32 compile_server_win32 zip_web_win32 +linux: prepare compile_agent_linux compile_server_linux zip_web_linux +mac: prepare compile_agent_mac compile_server_mac zip_web_mac + +# 客户端版本打包 +dp-win64: prepare build_gui_win64 compile_launcher_win64 compile_server_win64 copy_files_win64 zip_win64 zip_win64_upgrade +dp-win32: prepare build_gui_win32 compile_launcher_win32 compile_server_win32 copy_files_win32 zip_win32 zip_win32_upgrade +dp-linux: prepare build_gui_linux compile_server_linux copy_files_linux zip_linux zip_linux_upgrade +dp-mac: prepare build_gui_mac compile_server_mac copy_files_mac zip_mac zip_mac_upgrade # 乐研 打包 ly-win64: prepare compile_ly_ui_client build_gui_win64 compile_ly_launcher_win64 compile_server_win64 copy_files_win64 zip_win64 zip_win64_upgrade @@ -61,13 +68,11 @@ ly-win32: prepare compile_ly_ui_client build_gui_win32 compile_ly_launcher_win32 ly-linux: prepare compile_ly_ui_client build_gui_linux compile_server_linux copy_files_linux zip_linux zip_linux_upgrade ly-mac: prepare compile_ly_ui_client build_gui_mac compile_server_mac copy_files_mac zip_mac zip_mac_upgrade -build_agent: compile_agent_win64 compile_agent_win32 compile_agent_linux compile_agent_mac - prepare: init_client_project update_version # 初始化客户端项目 init_client_project: - @sh ./init.project.sh + @sh ./init.project.sh && yarn config set ignore-engines true update_version: gen_version_file @@ -78,6 +83,8 @@ gen_version_file: compile_ui: @cd ui && yarn build --dest ../client/ui && cd .. +compile_ui_web: + @cd ../leyanapi-frontend && yarn build:web --dest ../leyanapi-backend/client/ui && cd ../leyanapi-backend compile_ui_demo: @cd ../deeptest-ui && yarn build:demo --dest ../deeptest/client/ui && cd ../deeptest compile_ui_client: @@ -233,7 +240,72 @@ copy_files_linux: copy_files_mac: @echo 'start copy files darwin' -# zip files +# zip web files +zip_web_win64: + @echo 'start zip win64' + @find . -name .DS_Store -print0 | xargs -0 rm -f + @mkdir -p ${QINIU_DIST_DIR}win64 && rm -rf ${QINIU_DIST_DIR}win64/${PROJECT}-web.zip + + @cp -rf config ${BIN_DIR}win64/ + @cp -rf ${CLIENT_BIN_DIR}win32/deeptest-agent.exe ${BIN_DIR}win64/ + @rm -rf ${BIN_DIR}win64/deeptest-ui && mkdir ${BIN_DIR}win64/deeptest-ui + @cp -rf client/ui/* ${BIN_DIR}win64/deeptest-ui + + @cd ${BIN_DIR}win64/ && \ + zip -ry ${QINIU_DIST_DIR}win64/${PROJECT}-web.zip ./* && \ + md5sum ${QINIU_DIST_DIR}win64/${PROJECT}-web.zip | awk '{print $$1}' | \ + xargs echo > ${QINIU_DIST_DIR}win64/${PROJECT}-web.zip.md5 && \ + cd ../..; \ + +zip_web_win32: + @echo 'start zip win32' + @find . -name .DS_Store -print0 | xargs -0 rm -f + @mkdir -p ${QINIU_DIST_DIR}win32 && rm -rf ${QINIU_DIST_DIR}win32/${PROJECT}-web.zip + + @cp -rf config ${BIN_DIR}win32/ + @cp -rf ${CLIENT_BIN_DIR}win32/deeptest-agent.exe ${BIN_DIR}win32/ + @rm -rf ${BIN_DIR}win32/deeptest-ui && mkdir ${BIN_DIR}win32/deeptest-ui + @cp -rf client/ui/* ${BIN_DIR}win32/deeptest-ui + + @cd ${BIN_DIR}win32/ && \ + zip -ry ${QINIU_DIST_DIR}win32/${PROJECT}-web.zip ./* && \ + md5sum ${QINIU_DIST_DIR}win32/${PROJECT}-web.zip | awk '{print $$1}' | \ + xargs echo > ${QINIU_DIST_DIR}win32/${PROJECT}-web.zip.md5 && \ + cd ../..; \ + +zip_web_linux: + @echo 'start zip linux' + @find . -name .DS_Store -print0 | xargs -0 rm -f + @mkdir -p ${QINIU_DIST_DIR}linux && rm -rf ${QINIU_DIST_DIR}linux/${PROJECT}-web.zip + + @cp -rf config ${BIN_DIR}linux/ + @cp -rf ${CLIENT_BIN_DIR}linux/deeptest-agent ${BIN_DIR}linux/ + @rm -rf ${BIN_DIR}linux/deeptest-ui && mkdir ${BIN_DIR}linux/deeptest-ui + @cp -rf client/ui/* ${BIN_DIR}linux/deeptest-ui + + @cd ${BIN_DIR}linux/ && \ + zip -ry ${QINIU_DIST_DIR}linux/${PROJECT}-web.zip ./* && \ + md5sum ${QINIU_DIST_DIR}linux/${PROJECT}-web.zip | awk '{print $$1}' | \ + xargs echo > ${QINIU_DIST_DIR}linux/${PROJECT}-web.zip.md5 && \ + cd ../..; \ + +zip_web_mac: + @echo 'start zip darwin' + @find . -name .DS_Store -print0 | xargs -0 rm -f + @mkdir -p ${QINIU_DIST_DIR}darwin && rm -rf ${QINIU_DIST_DIR}darwin/${PROJECT}-web.zip + + @cp -rf config ${BIN_DIR}darwin/ + @cp -rf ${CLIENT_BIN_DIR}darwin/deeptest-agent ${BIN_DIR}darwin/ + @rm -rf ${BIN_DIR}darwin/deeptest-ui && mkdir ${BIN_DIR}darwin/deeptest-ui + @cp -rf client/ui/* ${BIN_DIR}darwin/deeptest-ui + + @cd ${BIN_DIR}darwin/ && \ + zip -ry ${QINIU_DIST_DIR}darwin/${PROJECT}-web.zip ./* && \ + md5sum ${QINIU_DIST_DIR}darwin/${PROJECT}-web.zip | awk '{print $$1}' | \ + xargs echo > ${QINIU_DIST_DIR}darwin/${PROJECT}-web.zip.md5 && \ + cd ../..; \ + +# zip client files zip_win64: @echo 'start zip win64' @find . -name .DS_Store -print0 | xargs -0 rm -f diff --git a/backend-config b/backend-config index 188cd29d7..e39b7c200 160000 --- a/backend-config +++ b/backend-config @@ -1 +1 @@ -Subproject commit 188cd29d770cf5422e58d75409d270de1e8e98d3 +Subproject commit e39b7c20019b120e8ab36a1eb99d9f6a45c4b0e3 diff --git a/cmd/agent/v1/domain/invocation.go b/cmd/agent/v1/domain/invocation.go index e9498276c..b4ffae5be 100644 --- a/cmd/agent/v1/domain/invocation.go +++ b/cmd/agent/v1/domain/invocation.go @@ -1,20 +1,9 @@ package agentDomain import ( - "github.com/aaronchen2k/deeptest/internal/pkg/consts" "github.com/aaronchen2k/deeptest/internal/pkg/domain" - "github.com/kataras/iris/v12" ) -type InterfaceCall struct { - ExecUuid string `json:"execUuid"` - ServerUrl string `json:"serverUrl"` - Token string `json:"token"` - TenantId consts.TenantId `json:"tenantId"` - Data domain.DebugData `json:"data"` - LocalVarsCache iris.Map `json:"localVarsCache"` -} - type InvokeRequest struct { ServerUrl string `json:"serverUrl"` Token string `json:"token"` diff --git a/cmd/agent/v1/domain/webscoket.go b/cmd/agent/v1/domain/webscoket.go index 4d190f63a..18c340a28 100644 --- a/cmd/agent/v1/domain/webscoket.go +++ b/cmd/agent/v1/domain/webscoket.go @@ -1,19 +1 @@ package agentDomain - -import ( - agentExec "github.com/aaronchen2k/deeptest/internal/agent/exec" - "github.com/aaronchen2k/deeptest/internal/pkg/consts" - "github.com/kataras/iris/v12" -) - -type WsReq struct { - Act consts.ExecType `json:"act"` - - ScenarioExecReq agentExec.ScenarioExecReq `json:"scenarioExecReq"` - PlanExecReq agentExec.PlanExecReq `json:"planExecReq"` - CasesExecReq agentExec.CasesExecReq `json:"casesExecReq"` - - MessageReq agentExec.MessageExecReq `json:"messageReq"` - LocalVarsCache iris.Map `json:"localVarsCache"` - TenantId consts.TenantId `json:"tenantId"` -} diff --git a/cmd/agent/v1/handler/exec-by-websocket.go b/cmd/agent/v1/handler/exec-by-websocket.go index 4e7359c18..d4c94d66b 100644 --- a/cmd/agent/v1/handler/exec-by-websocket.go +++ b/cmd/agent/v1/handler/exec-by-websocket.go @@ -2,7 +2,7 @@ package handler import ( "encoding/json" - agentDomain "github.com/aaronchen2k/deeptest/cmd/agent/v1/domain" + agentExec "github.com/aaronchen2k/deeptest/internal/agent/exec" execUtils "github.com/aaronchen2k/deeptest/internal/agent/exec/utils/exec" "github.com/aaronchen2k/deeptest/internal/agent/service" "github.com/aaronchen2k/deeptest/internal/pkg/consts" @@ -60,7 +60,7 @@ func (c *ExecByWebSocketCtrl) OnChat(wsMsg websocket.Message) (err error) { ctx := websocket.GetContext(c.Conn) _logUtils.Infof("WebSocket OnChat: remote address=%s, room=%s, msg=%s", ctx.RemoteAddr(), wsMsg.Room, string(wsMsg.Body)) - req := agentDomain.WsReq{} + req := agentExec.WsReq{} err = json.Unmarshal(wsMsg.Body, &req) if err != nil { execUtils.SendErrorMsg(err, consts.Processor, &wsMsg) diff --git a/cmd/agent/v1/handler/exec-interface.go b/cmd/agent/v1/handler/exec-interface.go index 94fa32262..4bff19f48 100644 --- a/cmd/agent/v1/handler/exec-interface.go +++ b/cmd/agent/v1/handler/exec-interface.go @@ -1,9 +1,9 @@ package handler import ( - "github.com/aaronchen2k/deeptest/cmd/agent/v1/domain" "github.com/aaronchen2k/deeptest/internal/agent/service" "github.com/aaronchen2k/deeptest/internal/pkg/consts" + "github.com/aaronchen2k/deeptest/internal/pkg/domain" "github.com/aaronchen2k/deeptest/pkg/domain" "github.com/aaronchen2k/deeptest/saas/common" "github.com/kataras/iris/v12" @@ -14,7 +14,7 @@ type ExecInterfaceCtrl struct { // Call func (c *ExecInterfaceCtrl) Call(ctx iris.Context) { - req := agentDomain.InterfaceCall{} + req := domain.InterfaceCall{} req.TenantId = c.getTenantId(ctx) err := ctx.ReadJSON(&req) if err != nil { diff --git a/cmd/command/action/run.go b/cmd/command/action/run.go index cf2a95fdb..b2fde62ae 100644 --- a/cmd/command/action/run.go +++ b/cmd/command/action/run.go @@ -1,32 +1,27 @@ package action -import ( - agentExec "github.com/aaronchen2k/deeptest/internal/agent/exec" - "github.com/aaronchen2k/deeptest/internal/agent/service" -) - func Run(scenario, plan, env int, server, token string) { //s.ExecScenarioService.ExecScenario(scenarioId, nil) - - if scenario > 0 { - req := agentExec.ScenarioExecReq{ - ServerUrl: server, - Token: token, - ScenarioId: scenario, - EnvironmentId: env, - } - - service.RunScenario(&req, nil) - - } else if plan > 0 { - req := agentExec.PlanExecReq{ - ServerUrl: server, - Token: token, - PlanId: plan, - EnvironmentId: env, - } - - service.RunPlan(&req, nil) - - } + // + //if scenario > 0 { + // req := agentExec.ScenarioExecReq{ + // ServerUrl: server, + // Token: token, + // ScenarioId: scenario, + // EnvironmentId: env, + // } + // + // service.RunScenario(nil, nil, &req, nil) + // + //} else if plan > 0 { + // req := agentExec.PlanExecReq{ + // ServerUrl: server, + // Token: token, + // PlanId: plan, + // EnvironmentId: env, + // } + // + // service.RunPlan(nil, nil, &req, nil) + // + //} } diff --git a/cmd/launcher/main.go b/cmd/launcher/main.go index a5eac7953..ddeeba27d 100644 --- a/cmd/launcher/main.go +++ b/cmd/launcher/main.go @@ -29,7 +29,7 @@ func main() { err := cmd.Run() if err != nil { - fmt.Printf("Failed to start ztf gui, path %s, err %s", pth, err.Error()) + fmt.Printf("Failed to start gui, path %s, err %s", pth, err.Error()) } } diff --git a/cmd/server/serve/serve.go b/cmd/server/serve/serve.go index 56e34cdc0..a26ee8951 100644 --- a/cmd/server/serve/serve.go +++ b/cmd/server/serve/serve.go @@ -7,6 +7,7 @@ import ( "github.com/aaronchen2k/deeptest/internal/pkg/core/module" "github.com/aaronchen2k/deeptest/internal/server/middleware" logUtils "github.com/aaronchen2k/deeptest/pkg/lib/log" + "github.com/snowlyg/helper/arr" "strings" "sync" @@ -14,7 +15,6 @@ import ( "github.com/kataras/iris/v12/context" "github.com/kataras/iris/v12/core/router" "github.com/kataras/iris/v12/middleware/pprof" - "github.com/snowlyg/helper/arr" ) // WebServer 服务器 @@ -70,12 +70,15 @@ func (webServer *WebServer) GetSources() []map[string]string { r := r // 去除非接口路径 - handerNames := context.HandlersNames(r.Handlers) - if !arr.InArrayS([]string{"GET", "POST", "PUT", "DELETE"}, r.Method) || - !arr.InArrayS(strings.Split(handerNames, ","), "github.com/snowlyg/multi.(*Verifier).Verify.func1") { + handlerNames := context.HandlersNames(r.Handlers) + + if !isApiMethod(r.Method) || !hasPerm(handlerNames) { + logUtils.Infof("NoPerm NAME: %s, PATH: %s, METHOD: %s, NAMES: %s ", r.Name, r.Path, r.Method, handlerNames) + routeLen-- continue } + go func(r *router.Route) { route := map[string]string{ "path": r.Path, @@ -124,3 +127,20 @@ func DebugParty() module.WebModule { } return module.NewModule("/debug", handler) } + +func isApiMethod(method string) bool { + return arr.InArrayS([]string{"GET", "POST", "PUT", "DELETE"}, method) +} +func hasPerm(handlerNames string) bool { + names := strings.Split(handlerNames, ",") + + hasPerm := false + for _, name := range names { + if strings.Index(name, ".Casbin") > -1 { + hasPerm = true + break + } + } + + return hasPerm +} diff --git a/cmd/server/v1/domain/interface-grpc.go b/cmd/server/v1/domain/interface-grpc.go new file mode 100644 index 000000000..3928ebefc --- /dev/null +++ b/cmd/server/v1/domain/interface-grpc.go @@ -0,0 +1,39 @@ +package serverDomain + +type GrpcReq struct { + Address string `json:"address"` + Service string `json:"service"` + Method string `json:"method"` + + UseTls bool `json:"useTls"` + RestartConn bool `json:"restartConn"` + MetaData []MetaDataItem `json:"metaData"` + + IsClientStreaming bool `json:"isClientStreaming"` + IsServerStreaming bool `json:"isServerStreaming"` + + Message string `json:"message"` // for invoke + + ProtoSrc string `json:"protoSrc"` + ProtoName string `json:"protoName"` + ProtoPath string `json:"protoPath"` + + ExecUuid string `json:"execUuid"` +} +type MetaDataItem struct { + Key string `json:"key"` + Value string `json:"value"` +} + +type GrpcParseResp struct { + Services []GrpcService `json:"services"` + Methods []GrpcMethod `json:"methods"` +} + +type GrpcService struct { + Name string `json:"name"` +} + +type GrpcMethod struct { + Name string `json:"name"` +} diff --git a/cmd/server/v1/domain/interface-websocket.go b/cmd/server/v1/domain/interface-websocket.go new file mode 100644 index 000000000..b5a055ab2 --- /dev/null +++ b/cmd/server/v1/domain/interface-websocket.go @@ -0,0 +1 @@ +package serverDomain diff --git a/cmd/server/v1/domain/snippet.go b/cmd/server/v1/domain/snippet.go new file mode 100644 index 000000000..d7b17986d --- /dev/null +++ b/cmd/server/v1/domain/snippet.go @@ -0,0 +1,8 @@ +package serverDomain + +type SnippetRes struct { + Label string `json:"label""` + Value string `json:"value""` + Desc string `json:"desc""` + Children []SnippetRes `json:"children""` +} diff --git a/cmd/server/v1/handler/file.go b/cmd/server/v1/handler/file.go index 1f95eca14..23294494a 100644 --- a/cmd/server/v1/handler/file.go +++ b/cmd/server/v1/handler/file.go @@ -1,13 +1,13 @@ package handler import ( + "github.com/aaronchen2k/deeptest/internal/pkg/consts" "github.com/aaronchen2k/deeptest/internal/pkg/service" commUtils "github.com/aaronchen2k/deeptest/internal/pkg/utils" "github.com/aaronchen2k/deeptest/internal/server/modules/service" "github.com/aaronchen2k/deeptest/pkg/domain" logUtils "github.com/aaronchen2k/deeptest/pkg/lib/log" "github.com/kataras/iris/v12" - "github.com/snowlyg/helper/dir" "go.uber.org/zap" "path/filepath" ) @@ -49,7 +49,8 @@ func (c *FileCtrl) Upload(ctx iris.Context) { var data interface{} if isDatapool { - absPath := filepath.Join(dir.GetCurrentAbPath(), pth) + //absPath := filepath.Join(dir.GetCurrentAbPath(), pth) + absPath := filepath.Join(consts.WorkDir, pth) data, err = c.DatapoolService.ReadExcel(absPath) if err != nil { diff --git a/cmd/server/v1/handler/interface-grpc.go b/cmd/server/v1/handler/interface-grpc.go new file mode 100644 index 000000000..eccbe9d35 --- /dev/null +++ b/cmd/server/v1/handler/interface-grpc.go @@ -0,0 +1,128 @@ +package handler + +import ( + serverDomain "github.com/aaronchen2k/deeptest/cmd/server/v1/domain" + "github.com/aaronchen2k/deeptest/internal/pkg/domain" + service "github.com/aaronchen2k/deeptest/internal/server/modules/service" + _domain "github.com/aaronchen2k/deeptest/pkg/domain" + "github.com/kataras/iris/v12" +) + +type GrpcInterfaceCtrl struct { + GrpcInterfaceService *service.GrpcInterfaceService `inject:""` + + BaseCtrl +} + +func (c *GrpcInterfaceCtrl) GetDebugData(ctx iris.Context) { + tenantId := c.getTenantId(ctx) + diagnoseInterfaceId, _ := ctx.URLParamInt("diagnoseInterfaceId") + + data, err := c.GrpcInterfaceService.GetDebugData(tenantId, diagnoseInterfaceId) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.SystemErr.Code, Msg: err.Error()}) + return + } + + ctx.JSON(_domain.Response{Code: _domain.NoErr.Code, Data: data, Msg: _domain.NoErr.Msg}) +} + +func (c *GrpcInterfaceCtrl) SaveDebugData(ctx iris.Context) { + tenantId := c.getTenantId(ctx) + + req := domain.GrpcDebugData{} + err := ctx.ReadJSON(&req) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.ParamErr.Code, Msg: err.Error()}) + return + } + + err = c.GrpcInterfaceService.SaveDebugData(req, tenantId) + + ctx.JSON(_domain.Response{Code: _domain.NoErr.Code, Msg: _domain.NoErr.Msg}) +} + +func (c *GrpcInterfaceCtrl) ParseProto(ctx iris.Context) { + req := serverDomain.GrpcReq{} + err := ctx.ReadJSON(&req) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.ParamErr.Code, Msg: err.Error()}) + return + } + + data, err := c.GrpcInterfaceService.ParseProto(req) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.ParamErr.Code, Msg: err.Error()}) + return + } + + ctx.JSON(_domain.Response{Code: _domain.NoErr.Code, Data: data}) +} + +func (c *GrpcInterfaceCtrl) DescribeFunc(ctx iris.Context) { + req := serverDomain.GrpcReq{} + err := ctx.ReadJSON(&req) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.ParamErr.Code, Msg: err.Error()}) + return + } + + data, err := c.GrpcInterfaceService.DescribeFunction(req) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.ParamErr.Code, Msg: err.Error()}) + return + } + + ctx.JSON(_domain.Response{Code: _domain.NoErr.Code, Data: data}) +} + +func (c *GrpcInterfaceCtrl) InvokeFunc(ctx iris.Context) { + req := serverDomain.GrpcReq{} + err := ctx.ReadJSON(&req) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.ParamErr.Code, Msg: err.Error()}) + return + } + + results, err := c.GrpcInterfaceService.InvokeFunc(req) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.ParamErr.Code, Msg: err.Error()}) + return + } + + ctx.JSON(_domain.Response{Code: _domain.NoErr.Code, Data: results}) +} + +func (c *GrpcInterfaceCtrl) ListConn(ctx iris.Context) { + req := serverDomain.GrpcReq{} + err := ctx.ReadJSON(&req) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.ParamErr.Code, Msg: err.Error()}) + return + } + + data, err := c.GrpcInterfaceService.ListActiveConn(req) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.ParamErr.Code, Data: data}) + return + } + + ctx.JSON(_domain.Response{Code: _domain.NoErr.Code}) +} + +func (c *GrpcInterfaceCtrl) DeleteHandle(ctx iris.Context) { + req := serverDomain.GrpcReq{} + err := ctx.ReadJSON(&req) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.ParamErr.Code, Msg: err.Error()}) + return + } + + err = c.GrpcInterfaceService.DeleteHandle(req) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.ParamErr.Code, Msg: err.Error()}) + return + } + + ctx.JSON(_domain.Response{Code: _domain.NoErr.Code}) +} diff --git a/cmd/server/v1/handler/interface-websocket.go b/cmd/server/v1/handler/interface-websocket.go new file mode 100644 index 000000000..07fc3fcf0 --- /dev/null +++ b/cmd/server/v1/handler/interface-websocket.go @@ -0,0 +1,42 @@ +package handler + +import ( + "github.com/aaronchen2k/deeptest/internal/pkg/domain" + service "github.com/aaronchen2k/deeptest/internal/server/modules/service" + _domain "github.com/aaronchen2k/deeptest/pkg/domain" + "github.com/kataras/iris/v12" +) + +type WebsocketInterfaceCtrl struct { + WebsocketInterfaceService *service.WebsocketInterfaceService `inject:""` + + BaseCtrl +} + +func (c *WebsocketInterfaceCtrl) GetDebugData(ctx iris.Context) { + tenantId := c.getTenantId(ctx) + diagnoseInterfaceId, _ := ctx.URLParamInt("diagnoseInterfaceId") + + data, err := c.WebsocketInterfaceService.GetDebugData(tenantId, diagnoseInterfaceId) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.SystemErr.Code, Msg: err.Error()}) + return + } + + ctx.JSON(_domain.Response{Code: _domain.NoErr.Code, Data: data, Msg: _domain.NoErr.Msg}) +} + +func (c *WebsocketInterfaceCtrl) SaveDebugData(ctx iris.Context) { + tenantId := c.getTenantId(ctx) + + req := domain.WebsocketDebugData{} + err := ctx.ReadJSON(&req) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.ParamErr.Code, Msg: err.Error()}) + return + } + + err = c.WebsocketInterfaceService.SaveDebugData(req, tenantId) + + ctx.JSON(_domain.Response{Code: _domain.NoErr.Code, Msg: _domain.NoErr.Msg}) +} diff --git a/cmd/server/v1/handler/snippet.go b/cmd/server/v1/handler/snippet.go index 5f2660f72..8f02fde0e 100644 --- a/cmd/server/v1/handler/snippet.go +++ b/cmd/server/v1/handler/snippet.go @@ -1,15 +1,18 @@ package handler import ( + "github.com/aaronchen2k/deeptest/internal/pkg/domain" scriptHelper "github.com/aaronchen2k/deeptest/internal/pkg/helper/script" service "github.com/aaronchen2k/deeptest/internal/server/modules/service" "github.com/aaronchen2k/deeptest/pkg/domain" "github.com/kataras/iris/v12" + "github.com/snowlyg/multi" "time" ) type SnippetCtrl struct { - SnippetService *service.SnippetService `inject:""` + SnippetService *service.SnippetService `inject:""` + DebugInterfaceService *service.DebugInterfaceService `inject:""` BaseCtrl } @@ -92,3 +95,44 @@ func (c *SnippetCtrl) GetJslibsForAgent(ctx iris.Context) { ctx.JSON(_domain.Response{Code: _domain.NoErr.Code, Data: snippets}) } + +func (c *SnippetCtrl) ListVar(ctx iris.Context) { + tenantId := c.getTenantId(ctx) + req := domain.DebugInfo{} + err := ctx.ReadJSON(&req) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.ParamErr.Code, Msg: _domain.ParamErr.Msg}) + return + } + + req.UserId = multi.GetUserId(ctx) + data := c.SnippetService.ListVar(tenantId, req) + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.SystemErr.Code, Msg: err.Error()}) + return + } + + ctx.JSON(_domain.Response{Code: _domain.NoErr.Code, Data: data, Msg: _domain.NoErr.Msg}) +} + +func (c *SnippetCtrl) ListMock(ctx iris.Context) { + tenantId := c.getTenantId(ctx) + list := c.SnippetService.ListMock(tenantId) + ctx.JSON(_domain.Response{Code: _domain.NoErr.Code, Data: list, Msg: _domain.NoErr.Msg}) +} + +func (c *SnippetCtrl) ListSysFunc(ctx iris.Context) { + list := c.SnippetService.ListSysFunc() + ctx.JSON(_domain.Response{Code: _domain.NoErr.Code, Data: list, Msg: _domain.NoErr.Msg}) +} + +func (c *SnippetCtrl) ListCustomFunc(ctx iris.Context) { + tenantId := c.getTenantId(ctx) + projectId, err := ctx.URLParamInt("currProjectId") + if err != nil { + ctx.JSON(_domain.Response{Code: _domain.ParamErr.Code, Msg: err.Error()}) + return + } + list := c.SnippetService.ListCustomFunc(tenantId, uint(projectId)) + ctx.JSON(_domain.Response{Code: _domain.NoErr.Code, Data: list, Msg: _domain.NoErr.Msg}) +} diff --git a/cmd/server/v1/handler/websocket.go b/cmd/server/v1/handler/websocket.go index b5642bd97..2eeae21b7 100644 --- a/cmd/server/v1/handler/websocket.go +++ b/cmd/server/v1/handler/websocket.go @@ -2,7 +2,7 @@ package handler import ( "encoding/json" - "github.com/aaronchen2k/deeptest/internal/pkg/consts" + "fmt" "github.com/aaronchen2k/deeptest/internal/pkg/helper/websocket" "github.com/aaronchen2k/deeptest/internal/server/modules/service" "github.com/aaronchen2k/deeptest/pkg/domain" @@ -13,7 +13,6 @@ import ( const ( result = "result" - outPut = "output" ) var ( @@ -28,7 +27,7 @@ type WebSocketCtrl struct { } func NewWebsocketCtrl() *WebSocketCtrl { - inst := &WebSocketCtrl{Namespace: consts.WsDefaultNamespace} + inst := &WebSocketCtrl{Namespace: "TestNamespace"} return inst } @@ -65,5 +64,11 @@ func (c *WebSocketCtrl) OnChat(wsMsg websocket.Message) (err error) { _logUtils.Infof("WebSocket OnChat: remote address=%s, room=%s, msg=%s", ctx.RemoteAddr(), wsMsg.Room, string(wsMsg.Body)) + resp := _domain.WsResp{Msg: fmt.Sprintf("from server: OnChat msg is \"%s\"", string(wsMsg.Body))} + bytes, _ := json.Marshal(resp) + mqData := _domain.MqMsg{Namespace: wsMsg.Namespace, Room: wsMsg.Room, Event: wsMsg.Event, Content: string(bytes)} + + websocketHelper.PubMsg(mqData) + return } diff --git a/cmd/server/v1/index.go b/cmd/server/v1/index.go index 84603cd87..525de932d 100644 --- a/cmd/server/v1/index.go +++ b/cmd/server/v1/index.go @@ -24,6 +24,9 @@ type IndexModule struct { DebugInvokeModule *router.DebugInvokeModule `inject:""` DiagnoseInterfaceModule *router.DiagnoseInterfaceModule `inject:""` + WebsocketInterfaceModule *router.WebsocketInterfaceModule `inject:""` + GrpcInterfaceModule *router.GrpcInterfaceModule `inject:""` + ProjectModule *router.ProjectModule `inject:""` ProjectPerModule *router.ProjectPermModule `inject:""` ProjectMenuModule *router.ProjectMenuModule `inject:""` @@ -158,7 +161,11 @@ func (m *IndexModule) ApiParty() module.WebModule { m.DebugInterfaceModule.Party(), m.DebugInvokeModule.Party(), + m.DiagnoseInterfaceModule.Party(), + m.WebsocketInterfaceModule.Party(), + m.GrpcInterfaceModule.Party(), + m.MessageModule.Party(), m.DocumentModule.Party(), m.HealthzModule.Party(), diff --git a/cmd/server/v1/router/interface-grpc.go b/cmd/server/v1/router/interface-grpc.go new file mode 100644 index 000000000..44d3573e5 --- /dev/null +++ b/cmd/server/v1/router/interface-grpc.go @@ -0,0 +1,32 @@ +package router + +import ( + "github.com/aaronchen2k/deeptest/cmd/server/v1/handler" + "github.com/aaronchen2k/deeptest/internal/pkg/core/module" + "github.com/aaronchen2k/deeptest/internal/server/middleware" + "github.com/kataras/iris/v12" +) + +type GrpcInterfaceModule struct { + GrpcInterfaceCtrl *handler.GrpcInterfaceCtrl `inject:""` +} + +// Party 脚本 +func (m *GrpcInterfaceModule) Party() module.WebModule { + handler := func(index iris.Party) { + index.Use(middleware.InitCheck(), middleware.JwtHandler(), middleware.OperationRecord(), middleware.Casbin(), middleware.ProjectPerm()) + + index.Get("/getDebugData", m.GrpcInterfaceCtrl.GetDebugData).Name = "获取gRPC测试接口" + index.Put("/saveDebugData", m.GrpcInterfaceCtrl.SaveDebugData).Name = "保存gRPC测试接口" + + index.Post("/parseProto", m.GrpcInterfaceCtrl.ParseProto).Name = "解析proto" + index.Post("/describeFunc", m.GrpcInterfaceCtrl.DescribeFunc).Name = "解析proto" + + index.Post("/listConn", m.GrpcInterfaceCtrl.ListConn).Name = "列出连接" + index.Post("/deleteHandle", m.GrpcInterfaceCtrl.DeleteHandle).Name = "移除Handle" + + index.Post("/invokeFunc", m.GrpcInterfaceCtrl.InvokeFunc).Name = "调用方法" + } + + return module.NewModule("/grpcInterfaces", handler) +} diff --git a/cmd/server/v1/router/interface-websocket.go b/cmd/server/v1/router/interface-websocket.go new file mode 100644 index 000000000..90aef78f3 --- /dev/null +++ b/cmd/server/v1/router/interface-websocket.go @@ -0,0 +1,23 @@ +package router + +import ( + "github.com/aaronchen2k/deeptest/cmd/server/v1/handler" + "github.com/aaronchen2k/deeptest/internal/pkg/core/module" + "github.com/aaronchen2k/deeptest/internal/server/middleware" + "github.com/kataras/iris/v12" +) + +type WebsocketInterfaceModule struct { + WebsocketInterfaceCtrl *handler.WebsocketInterfaceCtrl `inject:""` +} + +// Party 脚本 +func (m *WebsocketInterfaceModule) Party() module.WebModule { + handler := func(index iris.Party) { + index.Use(middleware.InitCheck(), middleware.JwtHandler(), middleware.OperationRecord(), middleware.Casbin(), middleware.ProjectPerm()) + + index.Get("/getDebugData", m.WebsocketInterfaceCtrl.GetDebugData).Name = "获取Websocket测试接口" + index.Put("/saveDebugData", m.WebsocketInterfaceCtrl.SaveDebugData).Name = "保存Websocket测试接口" + } + return module.NewModule("/websocketInterfaces", handler) +} diff --git a/cmd/server/v1/router/snippet.go b/cmd/server/v1/router/snippet.go index 016fc84af..8ca579a9a 100644 --- a/cmd/server/v1/router/snippet.go +++ b/cmd/server/v1/router/snippet.go @@ -21,6 +21,11 @@ func (m *SnippetModule) Party() module.WebModule { index.Get("/listJslibNames", m.SnippetCtrl.ListJslibNames).Name = "获取所有自定义库名称" index.Get("/getJslibs", m.SnippetCtrl.GetJslibs).Name = "获取用户自定义脚本库" index.Post("/getJslibsForAgent", m.SnippetCtrl.GetJslibsForAgent).Name = "获取用户自定义脚本库" + index.Get("/listVar", m.SnippetCtrl.ListVar).Name = "获取变量列表" + index.Get("/listMock", m.SnippetCtrl.ListMock).Name = "获取mock规则" + index.Get("/listSysFunc", m.SnippetCtrl.ListSysFunc).Name = "获取系统函数" + index.Get("/ListCustomFunc", m.SnippetCtrl.ListCustomFunc).Name = "创建代码片段" + } return module.NewModule("/snippets", handler) } diff --git a/cmd/test/goja/main.go b/cmd/test/goja/main.go index 8227b0946..995d03aab 100644 --- a/cmd/test/goja/main.go +++ b/cmd/test/goja/main.go @@ -1,80 +1,41 @@ package main import ( - "github.com/dop251/goja" - "github.com/dop251/goja_nodejs/require" + "fmt" "log" + "os" ) -var ( - Myvm MyVM - Myrequire *require.RequireModule -) - -type MyVM struct { - JsRuntime *goja.Runtime - Script string -} - func main() { - registry := new(require.Registry) - vm := goja.New() - - //req := registry.Enable(vm) - //mock, err := req.Require("./cmd/test/goja/lib/mock.js") - //vm.Set("mock", mock) - // - //script := ` - // function Mock(str) { - // const obj = JSON.parse(str); - // var data = mock.mock(obj) - // return data; - // } - // ` - //_, err = vm.RunString(script) - //if err != nil { - // log.Panic(err) - //} - // - //var Mock func(p interface{}) interface{} - //err = vm.ExportTo(vm.Get("Mock"), &Mock) - //if err != nil { - // panic(err) - //} - // - //str := `{"list|1-10": [{"id|+1": 1}]}` - // - //out := Mock(str) - // - //fmt.Println(out) - - // test functions - //req := registry.Enable(vm) - //module, err := req.Require("./cmd/test/goja/lib/funcs.js") - // - //vm.Set("math", module) - // - //script := `math.add(1, 1)` - //out, err := vm.RunString(script) - //if err != nil { - // log.Println(err) - //} - // - //fmt.Println(out) - - req := registry.Enable(vm) - - out, err := vm.RunString(` - var m = require("./cmd/test/goja/lib/funcs.js"); - m.add(1,2); - `) - + // 打开TS文件 + f, err := os.Open("math.d.ts") if err != nil { - log.Println(err.Error()) + log.Fatal(err) + } + defer f.Close() + + // 创建解析器 + p := astits.NewParser(f) + + for { + p, err := p.ParseOne() + if err != nil { + if err == astits.ErrEOF { + break + } + log.Fatal(err) + } + + // 处理PAT和PMT + if pat, ok := p.(*astits.PAT); ok { + fmt.Printf("Found PAT: %+v\n", pat) + for _, pmtr := range pat.Programs { + if pmt, err := astitools.PmtFromRunningFile(f, pmtr.PID); err == nil { + fmt.Printf("Found PMT for program %d: %+v\n", pmtr.ProgramNumber, pmt) + } else { + fmt.Printf("Error while retrieving PMT for program %d: %v\n", pmtr.ProgramNumber, err) + } + } + } } - - log.Println(out) - - m, err := req.Require("./m.js") - _, _ = m, err } diff --git a/cmd/test/test/reg_test.go b/cmd/test/test/reg_test.go new file mode 100644 index 000000000..876ffd3b7 --- /dev/null +++ b/cmd/test/test/reg_test.go @@ -0,0 +1,17 @@ +package test + +import ( + "log" + "regexp" + "testing" +) + +func TestReg(t *testing.T) { + str := "${g_var1} - {{'url is ' + escape('https://baidu.com'))}} {{1+1}}" + + reg := regexp.MustCompile(`\${(\+?[_A-Za-z][_A-Za-z0-9]*)}|(?U:{{(.+)}})`) + + arr := reg.FindAllStringSubmatch(str, -1) + + log.Println(arr) +} diff --git a/cmd/test/test/variable_expression_test.go b/cmd/test/test/variable_expression_test.go new file mode 100644 index 000000000..80286785f --- /dev/null +++ b/cmd/test/test/variable_expression_test.go @@ -0,0 +1,135 @@ +package test + +import ( + "fmt" + commUtils "github.com/aaronchen2k/deeptest/internal/pkg/utils" + "log" + "regexp" + "strings" + "testing" +) + +func Test123(t *testing.T) { + //str1 := "${var_name}" + // + //str2 := `${_mock("@date('yyyy-MM-dd')")}` // 不支持变量参数 + // + //str3 := "${_url_encode(${url})}" + //str4 := "${_url_encode('http://baidu.com')}" + //str5 := `${_url_encode("http://baidu.com")}` + + str6 := "${math.add(${p1}, 1)} + ${_url_encode(${url})}" + + //isFunc, name, params := parseSingleVariableToken(str1) + //log.Println(isFunc, name, params) + // + //isFunc, name, params = parseSingleVariableToken(str2) + //log.Println(isFunc, name, params) + // + //isFunc, name, params = parseSingleVariableToken(str3) + //log.Println(isFunc, name, params) + // + //isFunc, name, params = parseSingleVariableToken(str4) + //log.Println(isFunc, name, params) + // + //isFunc, name, params = parseSingleVariableToken(str5) + //log.Println(isFunc, name, params) + + value1 := replaceVariableToken(str6) + variablePlaceholders := commUtils.GetVariablesInExpressionPlaceholder(value1) + + for _, placeholder := range variablePlaceholders { + isFunc, name, params := parseSingleVariableToken(placeholder) + log.Println(isFunc, name, params) + } +} + +func parseSingleVariableToken(str string) (isFunc bool, name string, params []TokenParam) { + regx0 := regexp.MustCompile(`(?U)\${(\+?[A-Za-z0-9]+)}`) + arr0 := regx0.FindAllStringSubmatch(str, -1) + if len(arr0) > 0 { + for _, item := range arr0 { + str = strings.Replace(str, + fmt.Sprintf("${%s}", item[1]), + fmt.Sprintf("#[%s]", item[1]), 1) + } + } + + regx1 := regexp.MustCompile(`\${(\+?.+)}`) + arr1 := regx1.FindAllStringSubmatch(str, -1) + if len(arr1) == 0 { + return + } + + token := strings.TrimSpace(arr1[0][1]) + + regx2 := regexp.MustCompile(`^((?U).+)\((\+?.+)\)$`) + arr2 := regx2.FindAllStringSubmatch(token, -1) + + if len(arr2) == 0 { // is not func + name = token + return + } + + // is func + isFunc = true + name = strings.TrimSpace(arr2[0][1]) + arr3 := strings.Split(arr2[0][2], ",") + for _, item := range arr3 { + paramName := strings.TrimSpace(item) + if paramName == "" { + continue + } + + paramType := TokenParamTypeString + + if name == "_mock" { + paramType = "mock" + paramName = strings.Trim(paramName, `"'`) + } else { + regx4 := regexp.MustCompile(`#\[(\+?[A-Za-z0-9]+)\]`) + arr4 := regx4.FindAllStringSubmatch(item, -1) + + if len(arr4) > 0 { + paramType = TokenParamTypeVariable + paramName = arr4[0][1] + } else { + paramNameTrim := strings.Trim(paramName, `"'`) + if paramNameTrim == paramName { // number + paramType = TokenParamTypeNumber + } + } + } + + p := TokenParam{ + Name: paramName, + Type: paramType, + } + + params = append(params, p) + } + + return +} + +func replaceVariableToken(str string) (ret string) { + ret = str + + reg := regexp.MustCompile(`(?U)\${(\+?[A-Za-z0-9]+)}`) + ret = reg.ReplaceAllString(ret, "#[$1]") + + return +} + +type TokenParam struct { + Name string + Type TokenParamType +} + +type TokenParamType string + +const ( + TokenParamTypeVariable TokenParamType = "variable" + TokenParamTypeString TokenParamType = "string" + TokenParamTypeNumber TokenParamType = "number" +) diff --git a/env.dp b/env.dp index b2c1b38ce..1e4e7c81f 100644 --- a/env.dp +++ b/env.dp @@ -1,2 +1,2 @@ -VERSION=1.1.1 -PROJECT=deeptest \ No newline at end of file +VERSION=3.0 +PROJECT=deeptest diff --git a/go.mod b/go.mod index 3833d02fa..31f3f20ec 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,18 @@ module github.com/aaronchen2k/deeptest go 1.19 +require ( + github.com/fullstorydev/grpcurl v1.9.1 + github.com/jhump/protoreflect v1.16.0 + google.golang.org/grpc v1.63.2 +) + +replace ( + github.com/fullstorydev/grpcurl v1.9.1 => github.com/fullstorydev/grpcurl v1.3.2 + github.com/jhump/protoreflect v1.16.0 => github.com/jhump/protoreflect v1.5.0 + google.golang.org/grpc v1.63.2 => google.golang.org/grpc v1.21.0 +) + require ( github.com/474420502/requests v1.35.0 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible @@ -16,9 +28,9 @@ require ( github.com/casbin/gorm-adapter/v3 v3.4.2 github.com/clbanning/mxj v1.8.4 github.com/d5/tengo/v2 v2.13.0 - github.com/dlclark/regexp2 v1.7.0 - github.com/dop251/goja v0.0.0-20230203172422-5460598cfa32 - github.com/dop251/goja_nodejs v0.0.0-20230121151440-041f29a15066 + github.com/dlclark/regexp2 v1.11.0 + github.com/dop251/goja v0.0.0-20240516125602-ccbae20bcec2 + github.com/dop251/goja_nodejs v0.0.0-20240418154818-2aae10d4cbcf github.com/emirpasic/gods v1.12.0 github.com/facebookgo/inject v0.0.0-20180706035515-f23751cae28b github.com/fatih/color v1.13.0 @@ -28,7 +40,7 @@ require ( github.com/go-ozzo/ozzo-routing/v2 v2.4.0 github.com/go-playground/validator/v10 v10.9.0 github.com/go-redis/redis/v8 v8.11.5 - github.com/go-sourcemap/sourcemap v2.1.3+incompatible + github.com/go-sourcemap/sourcemap v2.1.4+incompatible github.com/go-sql-driver/mysql v1.7.0 github.com/goccy/go-json v0.10.2 github.com/gofrs/uuid v4.2.0+incompatible @@ -66,9 +78,9 @@ require ( github.com/xuri/excelize/v2 v2.6.1 github.com/zwgblue/yaml-encoder v0.0.0-20221226083717-a0bdbda0d998 go.uber.org/zap v1.19.1 - golang.org/x/crypto v0.11.0 - golang.org/x/net v0.12.0 - golang.org/x/text v0.11.0 + golang.org/x/crypto v0.14.0 + golang.org/x/net v0.17.0 + golang.org/x/text v0.15.0 google.golang.org/protobuf v1.31.0 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/yaml.v3 v3.0.1 @@ -113,14 +125,16 @@ require ( github.com/go-playground/universal-translator v0.18.0 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect - github.com/gobwas/ws v1.1.0 // indirect + github.com/gobwas/ws v1.2.1 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/mock v1.6.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-querystring v1.1.0 // indirect + github.com/google/pprof v0.0.0-20240521024322-9665fa269a30 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/mux v1.8.0 // indirect @@ -200,10 +214,11 @@ require ( github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/sys v0.10.0 // indirect + golang.org/x/sys v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.11.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 56d4a3188..8d74f2e4e 100644 --- a/go.sum +++ b/go.sum @@ -124,8 +124,11 @@ github.com/chindeo/pkg v0.0.2-0.20210813090029-854fe1390d17 h1:K+0ziK/nFNvBguKeG github.com/chindeo/pkg v0.0.2-0.20210813090029-854fe1390d17/go.mod h1:a9wCi+nCkjKTiWIvozPVt27AEEi8Gm5W4i77u3bHiBw= github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -160,16 +163,26 @@ github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+ github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= +github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja v0.0.0-20221118162653-d4bf6fde1b86/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs= github.com/dop251/goja v0.0.0-20230203172422-5460598cfa32 h1:audXtK7nV3y4W9ckAxRBE+eQV5Bljf5Non4NTa9kLVE= github.com/dop251/goja v0.0.0-20230203172422-5460598cfa32/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs= +github.com/dop251/goja v0.0.0-20231014103939-873a1496dc8e h1:lCjFpJwrCCaDOyQ4RKYNOIexG+yrjxai//OlcMQEGqg= +github.com/dop251/goja v0.0.0-20231014103939-873a1496dc8e/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= +github.com/dop251/goja v0.0.0-20240516125602-ccbae20bcec2 h1:OFTHt+yJDo/uaIKMGjEKzc3DGhrpQZoqvMUIloZv6ZY= +github.com/dop251/goja v0.0.0-20240516125602-ccbae20bcec2/go.mod h1:o31y53rb/qiIAONF7w3FHJZRqqP3fzHUr1HqanthByw= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= github.com/dop251/goja_nodejs v0.0.0-20230121151440-041f29a15066 h1:Xfx4AWxqtJBs2SiZlkIbELiMdKnxAwihZKKY09qe9/A= github.com/dop251/goja_nodejs v0.0.0-20230121151440-041f29a15066/go.mod h1:0tlktQL7yHfYEtjcRGi/eiOkbDR5XF7gyFFvbC5//E0= +github.com/dop251/goja_nodejs v0.0.0-20240418154818-2aae10d4cbcf h1:2JoVYP9iko8uuIW33BQafzaylDixXbdXCRw/vCoxL+s= +github.com/dop251/goja_nodejs v0.0.0-20240418154818-2aae10d4cbcf/go.mod h1:bhGPmCgCCTSRfiMYWjpS46IDo9EUZXlsuUaPXSWGbv0= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= @@ -214,6 +227,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fullstorydev/grpcurl v1.3.2 h1:cJKWsBYMocdxXQvgbnhtLG810SL5MhKT4K7BagxRih8= +github.com/fullstorydev/grpcurl v1.3.2/go.mod h1:kvk8xPCXOrwVd9zYdjy+xSOT4YWm6kyth4Y9NMfBns4= github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/gavv/httpexpect/v2 v2.3.1 h1:sGLlKMn8AuHS9ztK9Sb7AJ7OxIL8v2PcLdyxfKt1Fo4= github.com/gavv/httpexpect/v2 v2.3.1/go.mod h1:yOE8m/aqFYQDNrgprMeXgq4YynfN9h1NgcE1+1suV64= @@ -263,6 +278,8 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= +github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= @@ -278,6 +295,8 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= +github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -369,6 +388,9 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= +github.com/google/pprof v0.0.0-20240521024322-9665fa269a30 h1:r6YdmbD41tGHeCWDyHF691LWtL7D1iSTyJaKejTWwVU= +github.com/google/pprof v0.0.0-20240521024322-9665fa269a30/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -415,6 +437,7 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/imkira/go-interpol v1.0.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= @@ -498,6 +521,8 @@ github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f h1:UWGE8Vi+1Agt0lrvnd7UsmvwqWKRzb9byK9iQmsbY0Y= github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f/go.mod h1:u+9Snq0w+ZdYKi8BBoaxnEwWu0fY4Kvu9ByFpM51t1s= +github.com/jhump/protoreflect v1.5.0 h1:NgpVT+dX71c8hZnxHof2M7QDK7QtohIJ7DYycjnkyfc= +github.com/jhump/protoreflect v1.5.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= @@ -804,7 +829,6 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -957,10 +981,11 @@ golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5 golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1002,6 +1027,7 @@ golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1063,6 +1089,8 @@ golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1161,6 +1189,7 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1172,6 +1201,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1186,11 +1217,16 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1303,6 +1339,7 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1344,10 +1381,14 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= diff --git a/internal/agent/exec/condition-checkpoint.go b/internal/agent/exec/condition-checkpoint.go index e6681b6e6..769457b25 100644 --- a/internal/agent/exec/condition-checkpoint.go +++ b/internal/agent/exec/condition-checkpoint.go @@ -8,20 +8,26 @@ import ( "github.com/aaronchen2k/deeptest/internal/pkg/domain" extractorHelper "github.com/aaronchen2k/deeptest/internal/pkg/helper/extractor" _stringUtils "github.com/aaronchen2k/deeptest/pkg/lib/string" + "strconv" "strings" ) -func ExecCheckPoint(checkpoint *domain.CheckpointBase, resp domain.DebugResponse, processorId uint, execUuid string) (err error) { +func ExecCheckPoint(checkpoint *domain.CheckpointBase, resp domain.DebugResponse, processorId uint, session *ExecSession) (err error) { checkpoint.ResultStatus = consts.Pass // Judgement 表达式 if checkpoint.Type == consts.Judgement { - result, variablesArr := computerExpr(checkpoint.Expression, execUuid, processorId) + boolResult, _, err1 := computerExpr(checkpoint.Expression, session) + //checkpoint.Variables = getVariableArrDesc(variablesArr) + checkpoint.ActualResult = fmt.Sprintf("%v", boolResult) - checkpoint.Variables = getVariableArrDesc(variablesArr) - checkpoint.ActualResult = fmt.Sprintf("%v", result) + if err1 != nil { + checkpoint.ResultStatus = consts.Fail + err = err1 + return + } - ret, ok := result.(bool) + ret, ok := boolResult.(bool) if ok && ret { checkpoint.ResultStatus = consts.Pass } else { @@ -32,20 +38,37 @@ func ExecCheckPoint(checkpoint *domain.CheckpointBase, resp domain.DebugResponse } // 计算表达式 - checkpointValue, variablesArr := computerExpr(checkpoint.Value, execUuid, processorId) + checkpointValue := ReplaceVariableValue(checkpoint.Value, session) //非表达试,支持${abc}abc + //checkpointValue, variablesArr, err := computerExpr(checkpoint.Value, session) + //checkpoint.Variables = getVariableArrDesc(variablesArr) checkpointValue = _stringUtils.InterfToStr(checkpointValue) - checkpoint.Variables = getVariableArrDesc(variablesArr) // Response ResultStatus if checkpoint.Type == consts.ResponseStatus { - expectCodeNum := _stringUtils.ParseInt(fmt.Sprintf("%v", checkpointValue)) - checkpoint.ActualResult = fmt.Sprintf("%d", resp.StatusCode) + if err != nil { + checkpoint.ExpectResult = fmt.Sprintf("%v", checkpointValue) + checkpoint.ResultStatus = consts.Fail + return + } + + checkpointValueStr := fmt.Sprintf("%v", checkpointValue) + expectCodeNum, err1 := strconv.Atoi(checkpointValueStr) + expectValue := fmt.Sprintf("%d", expectCodeNum) + if err1 != nil { + expectValue = checkpointValueStr + checkpoint.ResultStatus = consts.Fail + checkpoint.ExpectResult = fmt.Sprintf("%v", expectValue) + return + } + + checkpoint.ExpectResult = fmt.Sprintf("%v", expectValue) + checkpoint.ResultStatus = consts.Fail if checkpoint.Operator == consts.Equal && resp.StatusCode == expectCodeNum { checkpoint.ResultStatus = consts.Pass - } else { - checkpoint.ResultStatus = consts.Fail + } else if checkpoint.Operator == consts.NotEqual && resp.StatusCode != expectCodeNum { + checkpoint.ResultStatus = consts.Pass } return @@ -60,8 +83,13 @@ func ExecCheckPoint(checkpoint *domain.CheckpointBase, resp domain.DebugResponse break } } - checkpoint.ActualResult = headerValue + checkpoint.ExpectResult = _stringUtils.InterfToStr(checkpointValue) + if err != nil { + checkpoint.ExpectResult = fmt.Sprintf("%v", checkpointValue) + checkpoint.ResultStatus = consts.Fail + return + } if checkpoint.Operator == consts.Equal && headerValue == checkpointValue { checkpoint.ResultStatus = consts.Pass @@ -76,13 +104,18 @@ func ExecCheckPoint(checkpoint *domain.CheckpointBase, resp domain.DebugResponse return } - var jsonData interface{} - json.Unmarshal([]byte(resp.Content), &jsonData) - - checkpoint.ActualResult = "" - // Response Body if checkpoint.Type == consts.ResponseBody { + var jsonData interface{} + json.Unmarshal([]byte(resp.Content), &jsonData) + + checkpoint.ActualResult = "" + checkpoint.ExpectResult = _stringUtils.InterfToStr(checkpointValue) + if err != nil { + checkpoint.ResultStatus = consts.Fail + return + } + if checkpoint.Operator == consts.Equal && resp.Content == checkpointValue { checkpoint.ResultStatus = consts.Pass } else if checkpoint.Operator == consts.NotEqual && resp.Content != checkpointValue { @@ -96,11 +129,16 @@ func ExecCheckPoint(checkpoint *domain.CheckpointBase, resp domain.DebugResponse return } - // ExtractorVari + // Extractor Vari if checkpoint.Type == consts.ExtractorVari { - variable, _ := GetVariable(GetCurrScenarioProcessorId(execUuid), checkpoint.ExtractorVariable, execUuid) + variable, _ := GetVariable(checkpoint.ExtractorVariable, session.GetCurrScenarioProcessorId(), session) checkpoint.ActualResult = fmt.Sprintf("%v", variable.Value) + checkpoint.ExpectResult = _stringUtils.InterfToStr(checkpointValue) + if err != nil { + checkpoint.ResultStatus = consts.Fail + return + } checkpoint.ResultStatus = agentUtils.Compare(checkpoint.Operator, checkpoint.ActualResult, checkpointValue) @@ -116,6 +154,7 @@ func ExecCheckPoint(checkpoint *domain.CheckpointBase, resp domain.DebugResponse extractorHelper.Extract(&extractor, resp) checkpoint.ActualResult = fmt.Sprintf("%v", extractor.Result) + checkpoint.ExpectResult = _stringUtils.InterfToStr(checkpointValue) checkpoint.ResultStatus = agentUtils.Compare(checkpoint.Operator, checkpoint.ActualResult, checkpointValue) @@ -125,19 +164,10 @@ func ExecCheckPoint(checkpoint *domain.CheckpointBase, resp domain.DebugResponse return } -func computerExpr(expression, execUuid string, processorId uint) (result interface{}, variablesArr domain.VarKeyValuePair) { - expr := ReplaceDatapoolVariInGovaluateExpress(expression, execUuid) +func computerExpr(expression string, session *ExecSession) ( + expectResult interface{}, params domain.VarKeyValuePair, err error) { - var err error - if processorId > 0 { // exec interface processor in scenario - result, variablesArr, err = EvaluateGovaluateExpressionByProcessorScope(expr, processorId, execUuid) - } else { // exec by interface invocation - result, variablesArr, err = EvaluateGovaluateExpressionWithDebugVariables(expr, execUuid) - } - - if err != nil { - result = expr - } + expectResult, params, err = NewGojaSimple().ExecJsFuncSimple(expression, session, true) return } diff --git a/internal/agent/exec/condition-exec.go b/internal/agent/exec/condition-exec.go index 909ceb490..d2f4d76fc 100644 --- a/internal/agent/exec/condition-exec.go +++ b/internal/agent/exec/condition-exec.go @@ -13,16 +13,15 @@ import ( "strings" ) -func ExecPreConditions(execObj *InterfaceExecObj, execUuid string) (err error) { +func ExecPreConditions(execObj *InterfaceExecObj, session *ExecSession) (err error) { preConditions := make([]domain.InterfaceExecCondition, 0) // will be changed and append items to it for _, condition := range execObj.PreConditions { if condition.Type == consts.ConditionTypeScript { - DealwithScriptCondition(condition, nil, execObj.DebugData.ProjectId, &preConditions, - execUuid, false, execObj.TenantId) + DealwithScriptCondition(condition, nil, &preConditions, false, session) } else if condition.Type == consts.ConditionTypeDatabase { - DealwithDatabaseCondition(condition, &preConditions, execUuid) + DealwithDatabaseCondition(condition, &preConditions, session) } } @@ -32,20 +31,19 @@ func ExecPreConditions(execObj *InterfaceExecObj, execUuid string) (err error) { return } -func ExecPostConditions(execObj *InterfaceExecObj, resp domain.DebugResponse, execUuid string) (resultStatus consts.ResultStatus, err error) { +func ExecPostConditions(execObj *InterfaceExecObj, resp domain.DebugResponse, session *ExecSession) (resultStatus consts.ResultStatus, err error) { resultStatus = consts.Pass postConditions := make([]domain.InterfaceExecCondition, 0) // will be changed and append items to it for _, condition := range execObj.PostConditions { if condition.Type == consts.ConditionTypeScript { - DealwithScriptCondition(condition, &resultStatus, execObj.DebugData.ProjectId, &postConditions, - execUuid, true, execObj.TenantId) + DealwithScriptCondition(condition, &resultStatus, &postConditions, true, session) } else if condition.Type == consts.ConditionTypeDatabase { - DealwithDatabaseCondition(condition, &postConditions, execUuid) + DealwithDatabaseCondition(condition, &postConditions, session) } else if condition.Type == consts.ConditionTypeExtractor { - DealwithExtractorCondition(condition, resp, &postConditions, execUuid) + DealwithExtractorCondition(condition, resp, &postConditions, session) } else if condition.Type == consts.ConditionTypeResponseDefine { //DealwithResponseDefineCondition(condition, resp, &resultStatus, &postConditions) @@ -54,7 +52,7 @@ func ExecPostConditions(execObj *InterfaceExecObj, resp domain.DebugResponse, ex for _, condition := range execObj.PostConditions { if condition.Type == consts.ConditionTypeCheckpoint { - DealwithDealwithCheckPointCondition(condition, resp, &resultStatus, &postConditions, execUuid) + DealwithDealwithCheckPointCondition(condition, resp, &resultStatus, &postConditions, session) } } @@ -65,25 +63,25 @@ func ExecPostConditions(execObj *InterfaceExecObj, resp domain.DebugResponse, ex } func DealwithScriptCondition(condition domain.InterfaceExecCondition, resultStatus *consts.ResultStatus, - projectId uint, conditions *[]domain.InterfaceExecCondition, execUuid string, isPostCondition bool, tenantId consts.TenantId) { + conditions *[]domain.InterfaceExecCondition, isPostCondition bool, session *ExecSession) { var scriptBase domain.ScriptBase json.Unmarshal(condition.Raw, &scriptBase) if scriptBase.Disabled { return } - err := ExecScript(&scriptBase, tenantId, projectId, execUuid) + err := ExecScript(&scriptBase, session) if err != nil { _logUtils.Info("script exec failed") } scriptHelper.GenResultMsg(&scriptBase) - scriptBase.VariableSettings = *GetGojaVariables(execUuid) + scriptBase.VariableSettings = *session.GetGojaVariables() condition.Raw, _ = json.Marshal(scriptBase) *conditions = append(*conditions, condition) - for _, item := range *GetGojaLogs(execUuid) { + for _, item := range *session.GetGojaLogs() { if isPostCondition { createAssertFromScriptResult(item, conditions, resultStatus, scriptBase.ConditionId, scriptBase.ConditionEntityId) } @@ -91,7 +89,7 @@ func DealwithScriptCondition(condition domain.InterfaceExecCondition, resultStat } func DealwithDatabaseCondition(condition domain.InterfaceExecCondition, - postConditions *[]domain.InterfaceExecCondition, execUuid string) { + postConditions *[]domain.InterfaceExecCondition, session *ExecSession) { status := consts.Pass @@ -101,7 +99,7 @@ func DealwithDatabaseCondition(condition domain.InterfaceExecCondition, return } - databaseOptBase.Sql = ReplaceVariableValue(databaseOptBase.Sql, execUuid) + databaseOptBase.Sql = ReplaceVariableValue(databaseOptBase.Sql, session) err := ExecDbOpt(&databaseOptBase) if err != nil || databaseOptBase.ResultStatus == consts.Fail { @@ -112,7 +110,7 @@ func DealwithDatabaseCondition(condition domain.InterfaceExecCondition, if databaseOptBase.JsonPath != "" && databaseOptBase.Variable != "" && status == consts.Pass { SetVariable(0, databaseOptBase.Variable, databaseOptBase.Result, databaseOptBase.ResultType, - consts.Public, execUuid) + consts.Public, session) } condition.Raw, _ = json.Marshal(databaseOptBase) @@ -120,7 +118,7 @@ func DealwithDatabaseCondition(condition domain.InterfaceExecCondition, } func DealwithExtractorCondition(condition domain.InterfaceExecCondition, resp domain.DebugResponse, - postConditions *[]domain.InterfaceExecCondition, execUuid string) { + postConditions *[]domain.InterfaceExecCondition, session *ExecSession) { var extractorBase domain.ExtractorBase json.Unmarshal(condition.Raw, &extractorBase) @@ -137,7 +135,7 @@ func DealwithExtractorCondition(condition domain.InterfaceExecCondition, resp do extractorHelper.GenResultMsg(&extractorBase) if extractorBase.ResultStatus == consts.Pass { - SetVariable(0, extractorBase.Variable, extractorBase.Result, extractorBase.ResultType, extractorBase.Scope, execUuid) + SetVariable(0, extractorBase.Variable, extractorBase.Result, extractorBase.ResultType, extractorBase.Scope, session) } condition.Raw, _ = json.Marshal(extractorBase) @@ -145,7 +143,7 @@ func DealwithExtractorCondition(condition domain.InterfaceExecCondition, resp do } func DealwithDealwithCheckPointCondition(condition domain.InterfaceExecCondition, resp domain.DebugResponse, - status *consts.ResultStatus, postConditions *[]domain.InterfaceExecCondition, execUuid string) { + status *consts.ResultStatus, postConditions *[]domain.InterfaceExecCondition, session *ExecSession) { var checkpointBase domain.CheckpointBase json.Unmarshal(condition.Raw, &checkpointBase) @@ -153,7 +151,7 @@ func DealwithDealwithCheckPointCondition(condition domain.InterfaceExecCondition return } - err := ExecCheckPoint(&checkpointBase, resp, 0, execUuid) + err := ExecCheckPoint(&checkpointBase, resp, 0, session) if err != nil || checkpointBase.ResultStatus == consts.Fail { *status = consts.Fail } diff --git a/internal/agent/exec/condition-script.go b/internal/agent/exec/condition-script.go index 77d654c61..a9245ddd0 100644 --- a/internal/agent/exec/condition-script.go +++ b/internal/agent/exec/condition-script.go @@ -5,24 +5,16 @@ import ( "fmt" "github.com/aaronchen2k/deeptest/internal/pkg/consts" "github.com/aaronchen2k/deeptest/internal/pkg/domain" - gojaUtils "github.com/aaronchen2k/deeptest/internal/pkg/goja" httpHelper "github.com/aaronchen2k/deeptest/internal/pkg/helper/http" - jslibHelper "github.com/aaronchen2k/deeptest/internal/pkg/helper/jslib" - scriptHelper "github.com/aaronchen2k/deeptest/internal/pkg/helper/script" - commUtils "github.com/aaronchen2k/deeptest/internal/pkg/utils" _commUtils "github.com/aaronchen2k/deeptest/pkg/lib/comm" - fileUtils "github.com/aaronchen2k/deeptest/pkg/lib/file" - logUtils "github.com/aaronchen2k/deeptest/pkg/lib/log" - "github.com/dop251/goja" "github.com/kataras/iris/v12" - "reflect" ) -func ExecScript(scriptObj *domain.ScriptBase, tenantId consts.TenantId, projectId uint, execUuid string) (err error) { - //InitJsRuntime(projectId, execUuid) - execRuntime, _ := jslibHelper.GetGojaRuntime(tenantId, projectId) - ResetGojaVariables(execUuid) - ResetGojaLogs(execUuid) +func ExecScript(scriptObj *domain.ScriptBase, session *ExecSession) (err error) { + execRuntime := session.GojaRuntime + + session.ResetGojaVariables() + session.ResetGojaLogs() var logs []string @@ -41,7 +33,8 @@ func ExecScript(scriptObj *domain.ScriptBase, tenantId consts.TenantId, projectI result = "空" } - logs = *GetGojaLogs(execUuid) + // get logs from js script execution + logs = *session.GetGojaLogs() if err != nil { scriptObj.ResultStatus = consts.Fail @@ -61,191 +54,24 @@ func ExecScript(scriptObj *domain.ScriptBase, tenantId consts.TenantId, projectI return } -func InitJsRuntime(tenantId consts.TenantId, projectId uint, execUuid string) { - jslibHelper.InitGojaRuntime(tenantId, projectId) - execRuntime, execRequire := jslibHelper.GetGojaRuntime(tenantId, projectId) - - jslibHelper.LoadChaiJslibs(execRuntime) - - defineJsFuncs(execUuid, tenantId, projectId) - defineGoFuncs(tenantId, projectId) - - // load global script - tmpPath := fmt.Sprintf("%s/deeptest.js", consts.TmpDirRelativeAgent) - tmpContent := scriptHelper.GetScript(scriptHelper.ScriptDeepTest) - fileUtils.WriteFileIfNotExist(tmpPath, tmpContent) - - dt, err := execRequire.Require("./" + tmpPath) - if err != nil { - logUtils.Infof("goja require failed, path: %s, err: %s.", tmpPath, err.Error()) - } - - execRuntime.Set("dt", dt) - - // import other custom libs - jslibHelper.RefreshRemoteAgentJslibs(execRuntime, execRequire, tenantId, projectId, GetServerUrl(execUuid), GetServerToken(execUuid)) -} - -func defineJsFuncs(execUuid string, tenantId consts.TenantId, projectId uint) (err error) { - execRuntime, _ := jslibHelper.GetGojaRuntime(tenantId, projectId) - - /* START: called by js */ - err = execRuntime.Set("getDatapoolVariable", func(dpName, field, seq string) (ret interface{}) { - execScene := GetExecScene(execUuid) - - rowIndex := getDatapoolRow(dpName, seq, execScene.Datapools, execUuid) - - if execScene.Datapools[dpName] == nil { - ret = consts.INVALID_VALUE - AppendGojaLogs(execUuid, - jsErrMsg("DATAPOOL_NOT_FOUND:"+dpName, "getDatapoolVariable", false)) - return - } - - if rowIndex > len(execScene.Datapools[dpName])-1 { - ret = consts.INVALID_VALUE - AppendGojaLogs(execUuid, - jsErrMsg("DATAPOOL_INDEX_OUT_OF_RANGE:"+dpName, "getDatapoolVariable", false)) - return - } - - ret = execScene.Datapools[dpName][rowIndex][field] - if ret == nil { - ret = consts.INVALID_VALUE - - AppendGojaLogs(execUuid, - jsErrMsg("DATAPOOL_VARIABLE_NOT_FOUND:"+field+"@"+dpName, "getDatapoolVariable", false)) - return - } - - return - }) - - err = execRuntime.Set("getVariable", func(name string) interface{} { - var scopeId uint - if GetCurrScenarioProcessor(execUuid) != nil { - scopeId = GetCurrScenarioProcessor(execUuid).ParentId - } - vari, err := GetVariable(scopeId, name, execUuid) - if err != nil { - vari.Value = consts.INVALID_VALUE - - AppendGojaLogs(execUuid, - jsErrMsg(err.Error(), "getVariable", false)) - - return vari.Value - } - - vari.Value, err = commUtils.ConvertValueForUse(vari.Value, vari.ValueType) - if err != nil { - vari.Value = consts.INVALID_VALUE - AppendGojaLogs(execUuid, - jsErrMsg(err.Error(), "getVariable", false)) - - return vari.Value - } - - return vari.Value - }) - err = execRuntime.Set("setVariable", func(name string, val interface{}) { - var scopeId uint - if GetCurrScenarioProcessor(execUuid) != nil { - scopeId = GetCurrScenarioProcessor(execUuid).ParentId - } - ret, err := SetVariable(scopeId, name, val, commUtils.ValueType(val), consts.Public, execUuid) - - if err == nil { - AppendGojaVariables(execUuid, ret) - } else { - AppendGojaLogs(execUuid, - jsErrMsg(err.Error(), "setVariable", false)) - } - - return - }) - err = execRuntime.Set("clearVariable", func(name string) { - var scopeId uint - if GetCurrScenarioProcessor(execUuid) != nil { - scopeId = GetCurrScenarioProcessor(execUuid).ParentId - } - - err := ClearVariable(scopeId, name, execUuid) - if err != nil { - AppendGojaLogs(execUuid, jsErrMsg(err.Error(), "clearVariable", false)) - } - }) +func GetReqValueFromGoja(session *ExecSession) (err error) { + execRuntime := session.GojaRuntime - // http request - err = execRuntime.Set("sendRequest", func(data goja.Value, cb func(interface{}, interface{})) { - req := gojaUtils.GenRequest(data, execRuntime) - - errOfCallbackParam := "" - - resp, err2 := Invoke(&req) - if err2 != nil { - // AppendGojaLogs(execUuid, jsErrMsg(err2.Error(), "sendRequest", false)) - errOfCallbackParam = jsErrMsg(err2.Error(), "sendRequest", false) - } - - cb(errOfCallbackParam, resp) - }) - - // log - err = execRuntime.Set("log", func(value interface{}) { - if value == nil { - AppendGojaLogs(execUuid, "空") - return - } - - typ := reflect.TypeOf(value).Name() - - if typ == "string" { - AppendGojaLogs(execUuid, value.(string)) - } else { - bytes, _ := json.Marshal(value) - AppendGojaLogs(execUuid, string(bytes)) - } - }) - /* END: called by js */ - - /* START: called by go */ - err = execRuntime.Set("getReqValueFromGoja", func(execUuid string, value domain.BaseRequest) { - SetCurrRequest(execUuid, value) - }) - err = execRuntime.Set("getRespValueFromGoja", func(execUuid string, value domain.DebugResponse) { - if httpHelper.IsJsonResp(value) { - bytes, _ := json.Marshal(value.Data) - value.Content = string(bytes) - SetCurrResponse(execUuid, value) - } else { - var ok bool - if value.Content, ok = value.Data.(string); ok { - - } - SetCurrResponse(execUuid, value) - - } - }) - /* END: called by go */ + _, err = execRuntime.RunString(fmt.Sprintf("getReqValueFromGoja('%s', dt.request);", session.ExecUuid)) return } +func GetRespValueFromGoja(session *ExecSession) (err error) { + execRuntime := session.GojaRuntime -func GetReqValueFromGoja(execUuid string, tenantId consts.TenantId, projectId uint) (err error) { - execRuntime, _ := jslibHelper.GetGojaRuntime(tenantId, projectId) - _, err = execRuntime.RunString(fmt.Sprintf("getReqValueFromGoja('%s', dt.request);", execUuid)) - return -} -func GetRespValueFromGoja(execUuid string, tenantId consts.TenantId, projectId uint) (err error) { - execRuntime, _ := jslibHelper.GetGojaRuntime(tenantId, projectId) - _, err = execRuntime.RunString(fmt.Sprintf("getRespValueFromGoja('%s', dt.response);", execUuid)) + _, err = execRuntime.RunString(fmt.Sprintf("getRespValueFromGoja('%s', dt.response);", session.ExecUuid)) return } -func SetReqValueToGoja(req *domain.BaseRequest) { - SetValueToGoja("request", req) +func SetReqValueToGoja(req *domain.BaseRequest, session *ExecSession) { + session.GojaSetValueFunc("request", req) } -func SetRespValueToGoja(resp *domain.DebugResponse) { +func SetRespValueToGoja(resp *domain.DebugResponse, session *ExecSession) { // set resp.Data to json object for goja edit if httpHelper.IsJsonResp(*resp) { var data interface{} @@ -260,30 +86,7 @@ func SetRespValueToGoja(resp *domain.DebugResponse) { resp.Data = resp.Content } - SetValueToGoja("response", resp) -} - -// call go SetValueToGoja = call js _setData -var ( - _setValueFunc func(name string, value interface{}) -) - -func SetValueToGoja(name string, value interface{}) { - _setValueFunc(name, value) -} -func defineGoFuncs(tenantId consts.TenantId, projectId uint) { - execRuntime, _ := jslibHelper.GetGojaRuntime(tenantId, projectId) - - // set data - script := `function _setData(name, val) { - dt[name] = val - }` - _, err := execRuntime.RunString(script) - if err != nil { - logUtils.Infof(err.Error()) - } - - err = execRuntime.ExportTo(execRuntime.Get("_setData"), &_setValueFunc) + session.GojaSetValueFunc("response", resp) } func jsErrMsg(msg string, category string, success bool) (ret string) { diff --git a/internal/agent/exec/context-cookie.go b/internal/agent/exec/context-cookie.go index e38efe964..df37f9322 100644 --- a/internal/agent/exec/context-cookie.go +++ b/internal/agent/exec/context-cookie.go @@ -5,14 +5,15 @@ import ( "time" ) -func ListScopeCookie(processorId uint, execUuid string) (cookies []domain.ExecCookie) { - scopeHierarchy := GetScopeHierarchy(execUuid) +func ListScopeCookie(processorId uint, session *ExecSession) (cookies []domain.ExecCookie) { + scopeHierarchy := session.ScenarioDebug.ScopeHierarchy + scopedCookies := session.ScenarioDebug.ScopedCookies allValidIds := scopeHierarchy[processorId] if allValidIds != nil { if scopeHierarchy[processorId] != nil { for _, id := range *scopeHierarchy[processorId] { - cookies = append(cookies, GetScopedCookies(execUuid)[id]...) + cookies = append(cookies, scopedCookies[id]...) } } } @@ -20,9 +21,9 @@ func ListScopeCookie(processorId uint, execUuid string) (cookies []domain.ExecCo return } -func GetCookie(processorId uint, cookieName, domain string, execUuid string) (cookie domain.ExecCookie) { - scopeHierarchy := GetScopeHierarchy(execUuid) - scopedCookies := GetScopedCookies(execUuid) +func GetCookie(processorId uint, cookieName, domain string, session *ExecSession) (cookie domain.ExecCookie) { + scopeHierarchy := session.ScenarioDebug.ScopeHierarchy + scopedCookies := session.ScenarioDebug.ScopedCookies allValidIds := scopeHierarchy[processorId] if allValidIds != nil { @@ -43,8 +44,8 @@ LABEL: return } -func SetCookie(processorId uint, cookieName string, cookieValue string, domainName string, expireTime *time.Time, execUuid string) (err error) { - scopedCookies := GetScopedCookies(execUuid) +func SetCookie(processorId uint, cookieName string, cookieValue string, domainName string, expireTime *time.Time, session *ExecSession) (err error) { + scopedCookies := session.ScenarioDebug.ScopedCookies found := false @@ -73,8 +74,8 @@ func SetCookie(processorId uint, cookieName string, cookieValue string, domainNa return } -func ClearCookie(processorId uint, cookieName string, execUuid string) (err error) { - scopedCookies := GetScopedCookies(execUuid) +func ClearCookie(processorId uint, cookieName string, session *ExecSession) (err error) { + scopedCookies := session.ScenarioDebug.ScopedCookies deleteIndex := -1 for index, item := range scopedCookies[processorId] { diff --git a/internal/agent/exec/context-exec.go b/internal/agent/exec/context-exec.go index 9a8af7bac..ad3f1547e 100644 --- a/internal/agent/exec/context-exec.go +++ b/internal/agent/exec/context-exec.go @@ -1,8 +1,8 @@ package agentExec import ( + "context" agentDomain "github.com/aaronchen2k/deeptest/internal/agent/exec/domain" - "github.com/aaronchen2k/deeptest/internal/pkg/domain" "sync" ) @@ -15,223 +15,55 @@ var ( ) func InitUserExecContext(execUuid string) { + ctx, cancel := context.WithCancel(context.Background()) val := UserContext{ - ScopedVariables: map[uint][]domain.ExecVariable{}, - ScopedCookies: map[uint][]domain.ExecCookie{}, - ScopeHierarchy: map[uint]*[]uint{}, - - ExecScene: domain.ExecScene{}, - DatapoolCursor: map[string]int{}, + ExecCtx: ctx, + ExecCancel: cancel, + InterfaceStat: &agentDomain.InterfaceStat{}, } ExecContextStore.Store(execUuid, &val) } -func ClearExecContext(execUuid string) { - ExecContextStore.Store(execUuid, nil) -} - func GetUserExecContext(execUuid string) (val *UserContext) { inf, ok := ExecContextStore.Load(execUuid) if !ok { - InitUserExecContext(execUuid) + return } - inf, _ = ExecContextStore.Load(execUuid) val = inf.(*UserContext) return } - -func SetIsRunning(execUuid string, val bool) { - entity := GetUserExecContext(execUuid) - entity.IsRunning = val - - //ExecContextStore.Store(execUuid, entity) -} -func GetIsRunning(execUuid string) (ret bool) { - userContext := GetUserExecContext(execUuid) - ret = userContext.ForceStopExec - - return -} - -func SetForceStopExec(execUuid string, val bool) { - entity := GetUserExecContext(execUuid) - entity.ForceStopExec = val - - //ExecContextStore.Store(execUuid, entity) -} -func GetForceStopExec(execUuid string) (ret bool) { - userContext := GetUserExecContext(execUuid) - ret = userContext.ForceStopExec - - return -} - -func SetScopedVariables(execUuid string, val map[uint][]domain.ExecVariable) { - entity := GetUserExecContext(execUuid) - entity.ScopedVariables = val - - //ExecContextStore.Store(execUuid, entity) -} -func GetScopedVariables(execUuid string) (ret map[uint][]domain.ExecVariable) { - userContext := GetUserExecContext(execUuid) - ret = userContext.ScopedVariables - - return -} - -func SetScopeHierarchy(execUuid string, val map[uint]*[]uint) { - entity := GetUserExecContext(execUuid) - entity.ScopeHierarchy = val - - //ExecContextStore.Store(execUuid, entity) -} -func GetScopeHierarchy(execUuid string) (ret map[uint]*[]uint) { - userContext := GetUserExecContext(execUuid) - ret = userContext.ScopeHierarchy - - return -} - -func SetExecScene(execUuid string, val domain.ExecScene) { - entity := GetUserExecContext(execUuid) - entity.ExecScene = val - - //ExecContextStore.Store(execUuid, entity) -} -func GetExecScene(execUuid string) (ret domain.ExecScene) { - userContext := GetUserExecContext(execUuid) - ret = userContext.ExecScene - - return -} - -func SetDatapoolCursor(execUuid string, val map[string]int) { - entity := GetUserExecContext(execUuid) - entity.DatapoolCursor = val - - //ExecContextStore.Store(execUuid, entity) -} -func GetDatapoolCursor(execUuid string) (ret map[string]int) { - userContext := GetUserExecContext(execUuid) - ret = userContext.DatapoolCursor - - return -} - -func SetScopedCookies(execUuid string, val map[uint][]domain.ExecCookie) { - entity := GetUserExecContext(execUuid) - entity.ScopedCookies = val - - //ExecContextStore.Store(execUuid, entity) -} -func GetScopedCookies(execUuid string) (ret map[uint][]domain.ExecCookie) { - userContext := GetUserExecContext(execUuid) - ret = userContext.ScopedCookies - - return -} - -func GetServerUrl(execUuid string) (ret string) { +func GetExecCtx(execUuid string) (ctx context.Context, cancel context.CancelFunc) { userContext := GetUserExecContext(execUuid) - ret = userContext.ServerUrl - - return -} -func SetServerUrl(execUuid string, val string) { - entity := GetUserExecContext(execUuid) - entity.ServerUrl = val - - //ExecContextStore.Store(execUuid, entity) -} -func SetServerToken(execUuid string, val string) { - entity := GetUserExecContext(execUuid) - entity.ServerToken = val - - //ExecContextStore.Store(execUuid, entity) -} -func GetServerToken(execUuid string) (ret string) { - userContext := GetUserExecContext(execUuid) - ret = userContext.ServerToken - - return -} - -func SetCurrRequest(execUuid string, val domain.BaseRequest) { - entity := GetUserExecContext(execUuid) - entity.CurrRequest = val - - //ExecContextStore.Store(execUuid, entity) -} -func GetCurrRequest(execUuid string) (ret domain.BaseRequest) { - userContext := GetUserExecContext(execUuid) - ret = userContext.CurrRequest - - return -} - -func SetCurrResponse(execUuid string, val domain.DebugResponse) { - entity := GetUserExecContext(execUuid) - entity.CurrResponse = val - - //ExecContextStore.Store(execUuid, entity) -} -func GetCurrResponse(execUuid string) (ret domain.DebugResponse) { - userContext := GetUserExecContext(execUuid) - ret = userContext.CurrResponse + if userContext != nil { + ctx = userContext.ExecCtx + cancel = userContext.ExecCancel + } return } -func SetCurrScenarioProcessor(execUuid string, val *Processor) { - entity := GetUserExecContext(execUuid) - entity.CurrScenarioProcessor = val - - //ExecContextStore.Store(execUuid, entity) -} -func GetCurrScenarioProcessor(execUuid string) (ret *Processor) { +func CancelExecCtx(execUuid string) { userContext := GetUserExecContext(execUuid) - ret = userContext.CurrScenarioProcessor - return -} + if userContext.ExecCancel != nil { + userContext.ExecCancel() + } -func SetCurrScenarioProcessorId(execUuid string, val uint) { - entity := GetUserExecContext(execUuid) - entity.CurrScenarioProcessorId = val + userContext.ExecCtx = nil + userContext.ExecCancel = nil - //ExecContextStore.Store(execUuid, entity) -} -func GetCurrScenarioProcessorId(execUuid string) (ret uint) { - userContext := GetUserExecContext(execUuid) - ret = userContext.CurrScenarioProcessorId + ExecContextStore.Store(execUuid, nil) return } - -func SetCurrEnvironmentId(execUuid string, id int) { - entity := GetUserExecContext(execUuid) - entity.CurrEnvironmentId = id -} -func GetCurrEnvironmentId(execUuid string) (id int) { +func IsExecCtxCancel(execUuid string) (ret bool) { userContext := GetUserExecContext(execUuid) - id = userContext.CurrEnvironmentId - return -} - -func SetCurrDebugInterfaceId(execUuid string, val uint) { - entity := GetUserExecContext(execUuid) - entity.CurrDebugInterfaceId = val - - //ExecContextStore.Store(execUuid, entity) -} -func GetCurrDebugInterfaceId(execUuid string) (ret uint) { - userContext := GetUserExecContext(execUuid) - ret = userContext.CurrDebugInterfaceId + ret = userContext.ExecCtx == nil return } @@ -247,93 +79,9 @@ func GetInterfaceStat(execUuid string) (ret *agentDomain.InterfaceStat) { return } -func GetGojaVariables(execUuid string) (ret *[]domain.ExecVariable) { - userContext := GetUserExecContext(execUuid) - ret = userContext.GojaVariables - - if ret == nil { - ret = &[]domain.ExecVariable{} - } - - return -} -func SetGojaVariables(execUuid string, val *[]domain.ExecVariable) { - entity := GetUserExecContext(execUuid) - entity.GojaVariables = val - - return -} -func ResetGojaVariables(execUuid string) { - entity := GetUserExecContext(execUuid) - entity.GojaVariables = nil - return -} -func AppendGojaVariables(execUuid string, val domain.ExecVariable) { - varis := GetGojaVariables(execUuid) - - *varis = append(*varis, val) - - return -} - -func GetGojaLogs(execUuid string) (ret *[]string) { - userContext := GetUserExecContext(execUuid) - ret = userContext.GojaLogs - - if ret == nil { - ret = &[]string{} - SetGojaLogs(execUuid, ret) - } - - return -} -func SetGojaLogs(execUuid string, val *[]string) { - entity := GetUserExecContext(execUuid) - entity.GojaLogs = val - - return -} -func ResetGojaLogs(execUuid string) { - entity := GetUserExecContext(execUuid) - entity.GojaLogs = nil - - return -} -func AppendGojaLogs(execUuid string, val string) { - logs := GetGojaLogs(execUuid) - - *logs = append(*logs, val) - - return -} - type UserContext struct { - IsRunning bool - ForceStopExec bool - - ServerUrl string - ServerToken string - - // used to exchange request and response data between goja and go - CurrRequest domain.BaseRequest - CurrResponse domain.DebugResponse - - CurrScenarioProcessor *Processor - CurrScenarioProcessorId uint - CurrDebugInterfaceId uint - CurrEnvironmentId int - - ScopedVariables map[uint][]domain.ExecVariable // for scenario and debug - ScopedCookies map[uint][]domain.ExecCookie // only for scenario - ScopeHierarchy map[uint]*[]uint // only for scenario (processId -> ancestorProcessIds) - - ExecScene domain.ExecScene // for scenario and debug, from server - DatapoolCursor map[string]int // only for scenario - - // for report data - InterfaceStat *agentDomain.InterfaceStat + ExecCtx context.Context + ExecCancel context.CancelFunc - // for goja js engine - GojaVariables *[]domain.ExecVariable - GojaLogs *[]string + InterfaceStat *agentDomain.InterfaceStat // for report data } diff --git a/internal/agent/exec/context.go b/internal/agent/exec/context.go index d5ce5dc2c..8869e4937 100644 --- a/internal/agent/exec/context.go +++ b/internal/agent/exec/context.go @@ -7,37 +7,14 @@ import ( _intUtils "github.com/aaronchen2k/deeptest/pkg/lib/int" ) -func InitDebugExecContext(execUuid string) (variables []domain.ExecVariable) { - SetScopedVariables(execUuid, map[uint][]domain.ExecVariable{}) - - return -} - -func InitScenarioExecContext(execObj *ScenarioExecObj) (variables []domain.ExecVariable) { - execUuid := execObj.ExecUuid - - scopeHierarchy := map[uint]*[]uint{} - ComputerScopeHierarchy(execObj.RootProcessor, &scopeHierarchy) - SetScopeHierarchy(execUuid, scopeHierarchy) - - SetExecScene(execUuid, execObj.ExecScene) - SetDatapoolCursor(execUuid, map[string]int{}) - - SetScopedVariables(execUuid, map[uint][]domain.ExecVariable{}) - SetScopedCookies(execUuid, map[uint][]domain.ExecCookie{}) - - return -} - -func GetValidScopeIds(processorId uint, execUuid string) (ret *[]uint) { +func GetValidScopeIds(processorId uint, session *ExecSession) (ret *[]uint) { if processorId == 0 { // return an arr with single 0 arr := []uint{processorId} ret = &arr return } - scopeHierarchy := GetScopeHierarchy(execUuid) - ret = scopeHierarchy[processorId] + ret = session.ScenarioDebug.ScopeHierarchy[processorId] return } diff --git a/internal/agent/exec/data.go b/internal/agent/exec/data.go index dc668294b..a5c56d0c6 100644 --- a/internal/agent/exec/data.go +++ b/internal/agent/exec/data.go @@ -80,8 +80,8 @@ func ReadDataFromExcel(url string) (ret []domain.VarKeyValuePair, err error) { return } -func DownloadUploadedFile(uri string, execUuid string) (ret string, err error) { - serverBaseUrl := strings.TrimSuffix(GetServerUrl(execUuid), ServerApiPath) +func DownloadUploadedFile(uri string, session *ExecSession) (ret string, err error) { + serverBaseUrl := strings.TrimSuffix(session.ServerUrl, ServerApiPath) url := _httpUtils.AddSepIfNeeded(serverBaseUrl) + uri diff --git a/internal/agent/exec/datapool.go b/internal/agent/exec/datapool.go index 4dd73c0e1..af0a1cf7e 100644 --- a/internal/agent/exec/datapool.go +++ b/internal/agent/exec/datapool.go @@ -11,8 +11,8 @@ import ( "time" ) -func getDatapoolValue(placeholder string, execUuid string) (ret string) { - execScene := GetExecScene(execUuid) +func getDatapoolValue(placeholder string, session *ExecSession) (ret string) { + execScene := session.ExecScene // _dp(name, col, 1 | seq | rand >) regex := regexp.MustCompile(fmt.Sprintf("(?Ui)%s\\((.+),(.+),(.+)\\)", consts.PlaceholderPrefixDatapool)) @@ -32,7 +32,7 @@ func getDatapoolValue(placeholder string, execUuid string) (ret string) { return } - rowIndex := getDatapoolRow(dpName, dpSeq, execScene.Datapools, execUuid) + rowIndex := getDatapoolRow(dpName, dpSeq, execScene.Datapools, session.ScenarioDebug.DatapoolCursor) if rowIndex > len(execScene.Datapools[dpName])-1 { ret = "OUT_OF_RANGE" @@ -49,9 +49,7 @@ func getDatapoolValue(placeholder string, execUuid string) (ret string) { return } -func getDatapoolRow(dpName, seq string, datapools domain.Datapools, execUuid string) (ret int) { - datapoolCursor := GetDatapoolCursor(execUuid) - +func getDatapoolRow(dpName, seq string, datapools domain.Datapools, datapoolCursor map[string]int) (ret int) { dp := datapools[dpName] if dp == nil { return diff --git a/internal/agent/exec/domain/result.go b/internal/agent/exec/domain/result.go index c653a1b43..ec54af9d0 100644 --- a/internal/agent/exec/domain/result.go +++ b/internal/agent/exec/domain/result.go @@ -53,7 +53,7 @@ type ScenarioExecResult struct { Stat InterfaceStat `json:"stat"` - EnvironmentId int `json:"environmentId,omitempty"` + EnvironmentId uint `json:"environmentId,omitempty"` LogId uuid.UUID `json:"logId,omitempty"` ParentLogId uuid.UUID `json:"parentLogId,omitempty"` @@ -66,11 +66,11 @@ type ScenarioExecResult struct { } type PlanExecResult struct { - ID int `json:"id" yaml:"id"` + ID uint `json:"id" yaml:"id"` Name string `json:"name"` Desc string `json:"desc,omitempty"` - EnvironmentId int `json:"environmentId"` + EnvironmentId uint `json:"environmentId"` Scenarios []*ScenarioExecResult `json:"scenarios"` Stat InterfaceStat `json:"stat"` diff --git a/internal/agent/exec/environment.go b/internal/agent/exec/environment.go index ab0a9d565..4ad1d4bd9 100644 --- a/internal/agent/exec/environment.go +++ b/internal/agent/exec/environment.go @@ -9,8 +9,8 @@ import ( "strings" ) -func GenRequestUrlWithBaseUrlAndPathParam(req *domain.BaseRequest, debugInterfaceId uint, baseUrl string, execUuid string) { - execScene := GetExecScene(execUuid) +func GenRequestUrlWithBaseUrlAndPathParam(req *domain.BaseRequest, debugInterfaceId uint, baseUrl string, session *ExecSession) { + execScene := session.ExecScene // get base url by key consts.KEY_BASE_URL in Environment Variables from server envId := execScene.DebugInterfaceToEnvMap[debugInterfaceId] diff --git a/internal/agent/exec/expression.go b/internal/agent/exec/expression.go index 82632e83a..c32a24fa7 100644 --- a/internal/agent/exec/expression.go +++ b/internal/agent/exec/expression.go @@ -3,10 +3,7 @@ package agentExec import ( "fmt" "github.com/Knetic/govaluate" - valueUtils "github.com/aaronchen2k/deeptest/internal/agent/exec/utils/value" "github.com/aaronchen2k/deeptest/internal/pkg/domain" - "github.com/aaronchen2k/deeptest/internal/pkg/utils" - logUtils "github.com/aaronchen2k/deeptest/pkg/lib/log" "github.com/aaronchen2k/deeptest/pkg/lib/string" "regexp" "strings" @@ -32,6 +29,7 @@ var ( } ) +/* // called by checkpoint func EvaluateGovaluateExpressionWithDebugVariables(expression string, execUuid string) (ret interface{}, params domain.VarKeyValuePair, err error) { // 1 @@ -56,7 +54,8 @@ func EvaluateGovaluateExpressionWithDebugVariables(expression string, execUuid s return } - +*/ +/* // called by agent processor interface func EvaluateGovaluateExpressionByProcessorScope(expression string, scopeId uint, execUuid string) (ret interface{}, params domain.VarKeyValuePair, err error) { // 1 @@ -78,6 +77,7 @@ func EvaluateGovaluateExpressionByProcessorScope(expression string, scopeId uint return } +*/ func convertGovaluateParamAndExpressionForProcessor(params domain.VarKeyValuePair, expr string) ( convertParams domain.VarKeyValuePair, convertExpr string) { @@ -100,6 +100,7 @@ func convertGovaluateParamAndExpressionForProcessor(params domain.VarKeyValuePai return } +/* // a.1 func generateGovaluateParamsByScope(expression string, scopeId uint, execUuid string) (ret domain.VarKeyValuePair, err error) { ret = domain.VarKeyValuePair{} @@ -127,7 +128,9 @@ func generateGovaluateParamsByScope(expression string, scopeId uint, execUuid st return } +*/ +/* // a.2 func generateGovaluateParamsWithVariables(expression string, execUuid string) (ret domain.VarKeyValuePair, err error) { ret = domain.VarKeyValuePair{} @@ -152,7 +155,9 @@ func generateGovaluateParamsWithVariables(expression string, execUuid string) (r return } +*/ +/* func ReplaceDatapoolVariInGovaluateExpress(expression string, execUuid string) (ret string) { ret = expression variablePlaceholders := commUtils.GetVariablesInExpressionPlaceholder(expression) @@ -178,3 +183,4 @@ func ReplaceDatapoolVariInGovaluateExpress(expression string, execUuid string) ( return } +*/ diff --git a/internal/agent/exec/goja-simple.go b/internal/agent/exec/goja-simple.go new file mode 100644 index 000000000..22b3e9242 --- /dev/null +++ b/internal/agent/exec/goja-simple.go @@ -0,0 +1,79 @@ +package agentExec + +import ( + "github.com/aaronchen2k/deeptest/internal/pkg/domain" + jslibHelper "github.com/aaronchen2k/deeptest/internal/pkg/helper/jslib" + scriptHelper "github.com/aaronchen2k/deeptest/internal/pkg/helper/script" + logUtils "github.com/aaronchen2k/deeptest/pkg/lib/log" + "github.com/dop251/goja" + "github.com/dop251/goja_nodejs/require" + "reflect" +) + +type GojaSimple struct { + execRuntime *goja.Runtime + execRequire *require.RequireModule + + _setValueFuncSimple func(name string, value interface{}) +} + +func NewGojaSimple() (ret *GojaSimple) { + s := GojaSimple{} + ret = &s + + return +} + +func (e *GojaSimple) ExecJsFuncSimple(content string, session *ExecSession, loadCustom bool) ( + ret interface{}, params domain.VarKeyValuePair, err error) { + + params = domain.VarKeyValuePair{} + + e.InitJsRuntimeSimple(session, loadCustom) + + // add variables to goja runtime + variables := GetAllValidVariables(session) + for _, variable := range variables { + e.execRuntime.Set(variable.Name, variable.Value) + + if reflect.TypeOf(variable.Value).Kind() == reflect.Func { + continue + } + params[variable.Name] = variable.Value + } + + resultVal, err := e.execRuntime.RunString(content) + if err != nil { + logUtils.Info(err.Error()) + ret = err.Error() + return + } + + ret = resultVal.Export() + if ret == nil { + ret = "空" + } + + return +} + +func (e *GojaSimple) InitJsRuntimeSimple(session *ExecSession, loadCustom bool) { + e.execRuntime, e.execRequire = GenerateGojaRuntime() + + // init e.execRuntime, not session.GojaRuntime + defineJsFuncs(e.execRuntime, e.execRequire, session, true) + loadDeeptestScript(e.execRuntime, e.execRequire, session, true) + defineJsSysFunc(e.execRuntime) + + // load buildin functions + content := scriptHelper.GetScript(scriptHelper.ScriptCustom) + _, err := e.execRuntime.RunString(content) + if err != nil { + logUtils.Infof("goja require buildin funcs failed, path: %s, err: %s.", scriptHelper.ScriptCustom, err.Error()) + } + + // import other custom libs + if loadCustom { + jslibHelper.RefreshRemoteAgentJslibs(e.execRuntime, e.execRequire, session.VuNo, session.TenantId, session.ProjectId, session.ServerUrl, session.ServerToken) + } +} diff --git a/internal/agent/exec/goja.go b/internal/agent/exec/goja.go new file mode 100644 index 000000000..cbd82ac5b --- /dev/null +++ b/internal/agent/exec/goja.go @@ -0,0 +1,244 @@ +package agentExec + +import ( + "encoding/json" + "fmt" + "github.com/aaronchen2k/deeptest/internal/pkg/consts" + "github.com/aaronchen2k/deeptest/internal/pkg/domain" + gojaUtils "github.com/aaronchen2k/deeptest/internal/pkg/goja" + httpHelper "github.com/aaronchen2k/deeptest/internal/pkg/helper/http" + jslibHelper "github.com/aaronchen2k/deeptest/internal/pkg/helper/jslib" + mockData "github.com/aaronchen2k/deeptest/internal/pkg/helper/openapi-mock/openapi/generator/data" + scriptHelper "github.com/aaronchen2k/deeptest/internal/pkg/helper/script" + commUtils "github.com/aaronchen2k/deeptest/internal/pkg/utils" + fileUtils "github.com/aaronchen2k/deeptest/pkg/lib/file" + logUtils "github.com/aaronchen2k/deeptest/pkg/lib/log" + _stringUtils "github.com/aaronchen2k/deeptest/pkg/lib/string" + "github.com/dop251/goja" + "github.com/dop251/goja_nodejs/require" + "path/filepath" + "reflect" +) + +func InitGojaRuntimeWithSession(session *ExecSession, vuNo int, tenantId consts.TenantId) { + session.GojaRuntime, session.GojaRequire = GenerateGojaRuntime() + + jslibHelper.LoadChaiJslibs(session.GojaRuntime) + + defineJsFuncs(session.GojaRuntime, session.GojaRequire, session, false) + + loadDeeptestScript(session.GojaRuntime, session.GojaRequire, session, false) + + defineGoFuncs(session) + + defineJsSysFunc(session.GojaRuntime) + + // import other custom libs + jslibHelper.RefreshRemoteAgentJslibs(session.GojaRuntime, session.GojaRequire, + vuNo, tenantId, session.ProjectId, + session.ServerUrl, session.ServerToken) + + return +} + +func loadDeeptestScript(runtime *goja.Runtime, require *require.RequireModule, session *ExecSession, + isSimple bool) (err error) { + + version := "v1" + + script := scriptHelper.ScriptDeepTest + if isSimple { + script = scriptHelper.ScriptDeepTestSimple + } + + content := scriptHelper.GetScript(script) + + pth := filepath.Join(consts.TmpDir, fmt.Sprintf("%s-%s.js", script, version)) + fileUtils.WriteFileIfNotExist(pth, content) + + dt, err := require.Require(pth) + if err != nil { + logUtils.Info(err.Error()) + return + } + + err = runtime.Set("dt", dt) + if err != nil { + logUtils.Info(err.Error()) + return + } + + return +} + +func defineJsFuncs(runtime *goja.Runtime, require *require.RequireModule, session *ExecSession, isSimple bool) (err error) { + /* START: called by js */ + err = runtime.Set("getDatapoolVariable", func(dpName, field, seq string) (ret interface{}) { + rowIndex := getDatapoolRow(dpName, seq, session.ExecScene.Datapools, session.ScenarioDebug.DatapoolCursor) + + if session.ExecScene.Datapools[dpName] == nil { + ret = consts.INVALID_VALUE + session.AppendGojaLog(jsErrMsg("DATAPOOL_NOT_FOUND:"+dpName, "getDatapoolVariable", false)) + return + } + + if rowIndex > len(session.ExecScene.Datapools[dpName])-1 { + ret = consts.INVALID_VALUE + session.AppendGojaLog(jsErrMsg("DATAPOOL_INDEX_OUT_OF_RANGE:"+dpName, "getDatapoolVariable", false)) + return + } + + ret = session.ExecScene.Datapools[dpName][rowIndex][field] + if ret == nil { + ret = consts.INVALID_VALUE + + session.AppendGojaLog(jsErrMsg("DATAPOOL_VARIABLE_NOT_FOUND:"+field+"@"+dpName, "getDatapoolVariable", false)) + return + } + + return + }) + + runtime.Set("_mock", func(rule string) (ret string) { + result, err := (&mockData.MockjsGenerator{}).GenerateByMockJsExpression(rule, "string") + if err == nil { + ret = _stringUtils.InterfToStr(result) + } + return + }) + + if isSimple { + return + } + + err = runtime.Set("getVariable", func(name string) interface{} { + var scopeId uint + if session.GetCurrScenarioProcessor() != nil { + scopeId = session.GetCurrScenarioProcessor().ParentId + } + + vari, err := GetVariable(name, scopeId, session) + if err != nil { + vari.Value = consts.INVALID_VALUE + + session.AppendGojaLog(jsErrMsg(err.Error(), "getVariable", false)) + + return vari.Value + } + + vari.Value, err = commUtils.ConvertValueForUse(vari.Value, vari.ValueType) + if err != nil { + vari.Value = consts.INVALID_VALUE + session.AppendGojaLog(jsErrMsg(err.Error(), "getVariable", false)) + + return vari.Value + } + + return vari.Value + }) + err = runtime.Set("setVariable", func(name string, val interface{}) { + var scopeId uint + if session.GetCurrScenarioProcessor() != nil { + scopeId = session.GetCurrScenarioProcessor().ParentId + } + + ret, err := SetVariable(scopeId, name, val, commUtils.ValueType(val), consts.Public, session) + + if err == nil { + session.AppendGojaVariables(ret) + } else { + session.AppendGojaLog(jsErrMsg(err.Error(), "setVariable", false)) + } + + return + }) + err = runtime.Set("clearVariable", func(name string) { + var scopeId uint + if session.GetCurrScenarioProcessor() != nil { + scopeId = session.GetCurrScenarioProcessor().ParentId + } + + err := ClearVariable(scopeId, name, session) + if err != nil { + session.AppendGojaLog(jsErrMsg(err.Error(), "clearVariable", false)) + } + }) + + // http request + err = runtime.Set("sendRequest", func(data goja.Value, cb func(interface{}, interface{})) { + req := gojaUtils.GenRequest(data, runtime) + + errOfCallbackParam := "" + + resp, err2 := Invoke(&req) + if err2 != nil { + // AppendGojaLog(execUuid, jsErrMsg(err2.Error(), "sendRequest", false)) + errOfCallbackParam = jsErrMsg(err2.Error(), "sendRequest", false) + } + + cb(errOfCallbackParam, resp) + }) + + // log + err = runtime.Set("log", func(value interface{}) { + if value == nil { + session.AppendGojaLog("空") + return + } + + typ := reflect.TypeOf(value).Name() + + if typ == "string" { + session.AppendGojaLog(value.(string)) + } else { + bytes, _ := json.Marshal(value) + session.AppendGojaLog(string(bytes)) + } + }) + /* END: called by js */ + + /* START: called by go */ + err = runtime.Set("getReqValueFromGoja", func(value domain.BaseRequest) { + session.SetCurrRequest(value) + }) + err = runtime.Set("getRespValueFromGoja", func(value domain.DebugResponse) { + if httpHelper.IsJsonResp(value) { + bytes, _ := json.Marshal(value.Data) + value.Content = string(bytes) + session.SetCurrResponse(value) + } else { + var ok bool + if value.Content, ok = value.Data.(string); ok { + + } + session.SetCurrResponse(value) + } + }) + /* END: called by go */ + + return +} + +func defineGoFuncs(session *ExecSession) { + // set data + script := `function _setData(name, val) { + dt[name] = val + }` + _, err := session.GojaRuntime.RunString(script) + if err != nil { + logUtils.Infof(err.Error()) + } + + err = session.GojaRuntime.ExportTo(session.GojaRuntime.Get("_setData"), &session.GojaSetValueFunc) + +} + +func GenerateGojaRuntime() (execRuntime *goja.Runtime, execRequire *require.RequireModule) { + execRuntime = goja.New() + execRuntime.SetFieldNameMapper(goja.TagFieldNameMapper("json", true)) + + registry := new(require.Registry) // registry 能夠被多个goja.Runtime共用 + execRequire = registry.Enable(execRuntime) + + return +} diff --git a/internal/agent/exec/processor-assertion.go b/internal/agent/exec/processor-assertion.go index e8dc94f88..d92281837 100644 --- a/internal/agent/exec/processor-assertion.go +++ b/internal/agent/exec/processor-assertion.go @@ -18,7 +18,7 @@ type ProcessorAssertion struct { Expression string `json:"expression" yaml:"expression"` } -func (entity ProcessorAssertion) Run(processor *Processor, session *Session) (err error) { +func (entity ProcessorAssertion) Run(processor *Processor, session *ExecSession) (err error) { defer func() { if errX := recover(); errX != nil { processor.Error(session, errX) @@ -41,10 +41,8 @@ func (entity ProcessorAssertion) Run(processor *Processor, session *Session) (er Round: processor.Round, } - expr := ReplaceDatapoolVariInGovaluateExpress(entity.Expression, session.ExecUuid) - ret, params, err := EvaluateGovaluateExpressionByProcessorScope(expr, processor.ID, session.ExecUuid) - - pass, _ := ret.(bool) + result, params, err := NewGojaSimple().ExecJsFuncSimple(entity.Expression, session, true) + pass, _ := result.(bool) var status string processor.Result.ResultStatus, status = getResultStatus(pass) @@ -62,7 +60,7 @@ func (entity ProcessorAssertion) Run(processor *Processor, session *Session) (er processor.Result.Detail = commonUtils.JsonEncode(detail) processor.AddResultToParent() - execUtils.SendExecMsg(*processor.Result, consts.Processor, session.WsMsg) + execUtils.SendExecMsg(*processor.Result, consts.Processor, session.ScenarioDebug.WsMsg) endTime := time.Now() processor.Result.EndTime = &endTime diff --git a/internal/agent/exec/processor-base.go b/internal/agent/exec/processor-base.go index 5bfd3ced6..cde84fd18 100644 --- a/internal/agent/exec/processor-base.go +++ b/internal/agent/exec/processor-base.go @@ -16,7 +16,7 @@ type ProcessorEntityBase struct { } type IProcessorEntity interface { - Run(*Processor, *Session) error + Run(*Processor, *ExecSession) error } func getPreviousBrother(processor Processor) (brother Processor, ok bool) { diff --git a/internal/agent/exec/processor-cookie.go b/internal/agent/exec/processor-cookie.go index c60cd4338..648c31c04 100644 --- a/internal/agent/exec/processor-cookie.go +++ b/internal/agent/exec/processor-cookie.go @@ -24,7 +24,7 @@ type ProcessorCookie struct { Children []interface{} `json:"children" yaml:"children" gorm:"-"` } -func (entity ProcessorCookie) Run(processor *Processor, session *Session) (err error) { +func (entity ProcessorCookie) Run(processor *Processor, session *ExecSession) (err error) { defer func() { if errX := recover(); errX != nil { processor.Error(session, errX) @@ -55,22 +55,22 @@ func (entity ProcessorCookie) Run(processor *Processor, session *Session) (err e detail := map[string]interface{}{"name": entity.Name, "cookieName": cookieName} if typ == consts.ProcessorCookieSet { - variableValue := ReplaceVariableValue(rightValue, session.ExecUuid) + variableValue := ReplaceVariableValue(rightValue, session) - SetCookie(processor.ParentId, cookieName, variableValue, domain, expireTime, session.ExecUuid) // set in parent scope + SetCookie(processor.ParentId, cookieName, variableValue, domain, expireTime, session) // set in parent scope processor.Result.Summary = fmt.Sprintf("%s为%v。", cookieName, variableValue) detail["variableValue"] = variableValue processor.Result.Detail = commonUtils.JsonEncode(detail) } else if typ == consts.ProcessorCookieClear { - ClearCookie(processor.ParentId, cookieName, session.ExecUuid) // set in parent scope + ClearCookie(processor.ParentId, cookieName, session) // set in parent scope processor.Result.Summary = fmt.Sprintf("%s。", cookieName) processor.Result.Detail = commonUtils.JsonEncode(detail) } processor.AddResultToParent() - execUtils.SendExecMsg(*processor.Result, consts.Processor, session.WsMsg) + execUtils.SendExecMsg(*processor.Result, consts.Processor, session.ScenarioDebug.WsMsg) endTime := time.Now() processor.Result.EndTime = &endTime diff --git a/internal/agent/exec/processor-custom-code.go b/internal/agent/exec/processor-custom-code.go index 633adb487..dae2240ec 100644 --- a/internal/agent/exec/processor-custom-code.go +++ b/internal/agent/exec/processor-custom-code.go @@ -20,7 +20,7 @@ type ProcessorCustomCode struct { Desc string `json:"desc" yaml:"desc"` } -func (entity ProcessorCustomCode) Run(processor *Processor, session *Session) (err error) { +func (entity ProcessorCustomCode) Run(processor *Processor, session *ExecSession) (err error) { defer func() { if errX := recover(); errX != nil { processor.Error(session, errX) @@ -47,12 +47,12 @@ func (entity ProcessorCustomCode) Run(processor *Processor, session *Session) (e Content: entity.Content, } - err = ExecScript(&scriptBase, session.TenantId, processor.ProjectId, session.ExecUuid) + err = ExecScript(&scriptBase, session) scriptHelper.GenResultMsg(&scriptBase) //scriptBase.VariableSettings = VariableSettings - for _, item := range *GetGojaVariables(session.ExecUuid) { - SetVariable(processor.ParentId, item.Name, item.Value, item.ValueType, consts.Public, session.ExecUuid) + for _, item := range *session.GetGojaVariables() { + SetVariable(processor.ParentId, item.Name, item.Value, item.ValueType, consts.Public, session) } processor.Result.Summary = scriptBase.ResultStatus.String() @@ -64,10 +64,10 @@ func (entity ProcessorCustomCode) Run(processor *Processor, session *Session) (e } detail := map[string]interface{}{"name": entity.Name, "content": entity.Content, "result": result, "output": scriptBase.Output} processor.Result.Detail = commonUtils.JsonEncode(detail) - execUtils.SendExecMsg(*processor.Result, consts.Processor, session.WsMsg) + execUtils.SendExecMsg(*processor.Result, consts.Processor, session.ScenarioDebug.WsMsg) - stat := CountScriptAssertionStat(session.ExecUuid, scriptBase.Output, processor.Result) - execUtils.SendStatMsg(stat, session.WsMsg) + stat := CountScriptAssertionStat(scriptBase.Output, processor.Result, session.ExecUuid) + execUtils.SendStatMsg(stat, session.ScenarioDebug.WsMsg) endTime := time.Now() processor.Result.EndTime = &endTime diff --git a/internal/agent/exec/processor-data.go b/internal/agent/exec/processor-data.go index 92cebb8bf..25b00cee9 100644 --- a/internal/agent/exec/processor-data.go +++ b/internal/agent/exec/processor-data.go @@ -37,7 +37,7 @@ type ProcessorData struct { VariableName string `json:"variableName,omitempty" yaml:"variableName,omitempty"` } -func (entity ProcessorData) Run(processor *Processor, session *Session) (err error) { +func (entity ProcessorData) Run(processor *Processor, session *ExecSession) (err error) { logUtils.Infof("data entity") startTime := time.Now() @@ -61,7 +61,7 @@ func (entity ProcessorData) Run(processor *Processor, session *Session) (err err processor.Result.Detail = commonUtils.JsonEncode(detail) processor.AddResultToParent() - execUtils.SendExecMsg(*processor.Result, consts.Processor, session.WsMsg) + execUtils.SendExecMsg(*processor.Result, consts.Processor, session.ScenarioDebug.WsMsg) entity.runDataItems(session, processor, processor.Result.Iterator) @@ -71,7 +71,7 @@ func (entity ProcessorData) Run(processor *Processor, session *Session) (err err return } -func (entity *ProcessorData) runDataItems(session *Session, processor *Processor, iterator agentDomain.ExecIterator) (err error) { +func (entity *ProcessorData) runDataItems(session *ExecSession, processor *Processor, iterator agentDomain.ExecIterator) (err error) { defer func() { if errX := recover(); errX != nil { processor.Error(session, errX) @@ -88,12 +88,12 @@ func (entity *ProcessorData) runDataItems(session *Session, processor *Processor break } - SetVariable(processor.ID, iterator.VariableName, item, consts.ExtractorResultTypeString, consts.Public, session.ExecUuid) + SetVariable(processor.ID, iterator.VariableName, item, consts.ExtractorResultTypeString, consts.Public, session) round := "" for _, child := range processor.Children { - if GetForceStopExec(session.ExecUuid) { + if IsExecCtxCancel(session.ExecUuid) { break } if child.Disable { @@ -112,13 +112,13 @@ func (entity *ProcessorData) runDataItems(session *Session, processor *Processor } } - stat := CountSkip(session.ExecUuid, executedProcessorIds, processor.Children) - execUtils.SendStatMsg(stat, session.WsMsg) + stat := CountSkip(executedProcessorIds, processor.Children, session) + execUtils.SendStatMsg(stat, session.ScenarioDebug.WsMsg) return } -func (entity *ProcessorData) getIterator(session *Session) (iterator agentDomain.ExecIterator, msg string) { +func (entity *ProcessorData) getIterator(session *ExecSession) (iterator agentDomain.ExecIterator, msg string) { if entity.ID == 0 { msg = "执行前请先配置处理器。" return @@ -132,9 +132,9 @@ func (entity *ProcessorData) getIterator(session *Session) (iterator agentDomain return } -func (entity *ProcessorData) GenerateLoopList(session *Session) (ret agentDomain.ExecIterator, err error) { +func (entity *ProcessorData) GenerateLoopList(session *ExecSession) (ret agentDomain.ExecIterator, err error) { if entity.Src == consts.SrcDatapool { - for name, dp := range GetExecScene(session.ExecUuid).Datapools { + for name, dp := range session.ExecScene.Datapools { if name == entity.DatapoolName { ret.Data = dp break @@ -142,7 +142,7 @@ func (entity *ProcessorData) GenerateLoopList(session *Session) (ret agentDomain } } else { - pth, _ := DownloadUploadedFile(entity.Url, session.ExecUuid) + pth, _ := DownloadUploadedFile(entity.Url, session) if err != nil { logUtils.Infof("Download file %s failed", pth) } diff --git a/internal/agent/exec/processor-group.go b/internal/agent/exec/processor-group.go index 08fcd64f1..59cbb2c6a 100644 --- a/internal/agent/exec/processor-group.go +++ b/internal/agent/exec/processor-group.go @@ -15,7 +15,7 @@ type ProcessorGroup struct { ProcessorEntityBase } -func (entity ProcessorGroup) Run(processor *Processor, session *Session) (err error) { +func (entity ProcessorGroup) Run(processor *Processor, session *ExecSession) (err error) { defer func() { if errX := recover(); errX != nil { processor.Error(session, errX) @@ -41,10 +41,10 @@ func (entity ProcessorGroup) Run(processor *Processor, session *Session) (err er processor.AddResultToParent() detail := map[string]interface{}{"name": entity.Name} processor.Result.Detail = commonUtils.JsonEncode(detail) - execUtils.SendExecMsg(*processor.Result, consts.Processor, session.WsMsg) + execUtils.SendExecMsg(*processor.Result, consts.Processor, session.ScenarioDebug.WsMsg) for _, child := range processor.Children { - if GetForceStopExec(session.ExecUuid) { + if IsExecCtxCancel(session.ExecUuid) { break } if child.Disable { diff --git a/internal/agent/exec/processor-interface.go b/internal/agent/exec/processor-interface.go index fb33d8fae..72577a90d 100644 --- a/internal/agent/exec/processor-interface.go +++ b/internal/agent/exec/processor-interface.go @@ -30,14 +30,14 @@ type ProcessorInterface struct { PostConditions []domain.InterfaceExecCondition `json:"postConditions"` } -func (entity ProcessorInterface) Run(processor *Processor, session *Session) (err error) { +func (entity ProcessorInterface) Run(processor *Processor, session *ExecSession) (err error) { defer func() { if errX := recover(); errX != nil { processor.Error(session, errX) } }() logUtils.Infof("interface entity") - SetCurrDebugInterfaceId(session.ExecUuid, processor.EntityId) + session.SetCurrDebugInterfaceId(processor.EntityId) execStartTime := time.Now() processor.Result = &agentDomain.ScenarioExecResult{ @@ -64,20 +64,20 @@ func (entity ProcessorInterface) Run(processor *Processor, session *Session) (er // init context //InitJsRuntime(processor.ProjectId, session.ExecUuid) - SetReqValueToGoja(&baseRequest) + SetReqValueToGoja(&baseRequest, session) // exec pre-condition entity.ExecPreConditions(processor, session) // dealwith variables - ReplaceVariables(&baseRequest, session.ExecUuid) - GetReqValueFromGoja(session.ExecUuid, session.TenantId, processor.ProjectId) + ReplaceVariables(&baseRequest, session) + GetReqValueFromGoja(session) // add cookies - DealwithCookies(&baseRequest, entity.ProcessorID, session.ExecUuid) + DealwithCookies(&baseRequest, entity.ProcessorID, session) // gen request url - GenRequestUrlWithBaseUrlAndPathParam(&baseRequest, processor.EntityId, entity.BaseUrl, session.ExecUuid) + GenRequestUrlWithBaseUrlAndPathParam(&baseRequest, processor.EntityId, entity.BaseUrl, session) // send request requestStartTime := time.Now() @@ -85,14 +85,14 @@ func (entity ProcessorInterface) Run(processor *Processor, session *Session) (er requestEndTime := time.Now() // exec post-condition - SetRespValueToGoja(&entity.Response) + SetRespValueToGoja(&entity.Response, session) processor.Result.ResultStatus, _ = entity.ExecPostConditions(processor, &detail, session) - GetRespValueFromGoja(session.ExecUuid, session.TenantId, processor.ProjectId) + GetRespValueFromGoja(session) processor.Result.Detail = commonUtils.JsonEncode(detail) // get the response data updated by script post-condition - if GetCurrResponse(session.ExecUuid).Data != nil { - entity.Response = GetCurrResponse(session.ExecUuid) + if session.GetCurrResponse().Data != nil { + entity.Response = session.GetCurrResponse() } // dealwith response @@ -102,50 +102,46 @@ func (entity ProcessorInterface) Run(processor *Processor, session *Session) (er } for _, c := range entity.Response.Cookies { - SetCookie(processor.ParentId, c.Name, c.Value, c.Domain, c.ExpireTime, session.ExecUuid) + SetCookie(processor.ParentId, c.Name, c.Value, c.Domain, c.ExpireTime, session) } - execUtils.SendExecMsg(*processor.Result, consts.Processor, session.WsMsg) + execUtils.SendExecMsg(*processor.Result, consts.Processor, session.ScenarioDebug.WsMsg) endTime := time.Now() processor.Result.EndTime = &endTime - stat := CountInterfaceStat(session.ExecUuid, processor.Result) - execUtils.SendStatMsg(stat, session.WsMsg) + stat := CountInterfaceStat(processor.Result, session.ExecUuid) + execUtils.SendStatMsg(stat, session.ScenarioDebug.WsMsg) processor.AddResultToParent() return } -func (entity *ProcessorInterface) ExecPreConditions(processor *Processor, session *Session) (err error) { +func (entity *ProcessorInterface) ExecPreConditions(processor *Processor, session *ExecSession) (err error) { for _, condition := range entity.PreConditions { if condition.Type == consts.ConditionTypeScript { - entity.DealwithScriptCondition(condition, nil, session.TenantId, processor.ProjectId, &processor.Result.PreConditions, - session.ExecUuid, false) + entity.DealwithScriptCondition(condition, nil, &processor.Result.PreConditions, false, session) } else if condition.Type == consts.ConditionTypeDatabase { - entity.DealwithDatabaseOptCondition(condition, processor.ID, processor.ParentId, &processor.Result.PreConditions, - session.ExecUuid) + entity.DealwithDatabaseOptCondition(condition, processor.ParentId, &processor.Result.PreConditions, session) } } return } -func (entity *ProcessorInterface) ExecPostConditions(processor *Processor, detail *map[string]interface{}, session *Session) ( +func (entity *ProcessorInterface) ExecPostConditions(processor *Processor, detail *map[string]interface{}, session *ExecSession) ( interfaceStatus consts.ResultStatus, err error) { interfaceStatus = processor.Result.ResultStatus for _, condition := range entity.PostConditions { if condition.Type == consts.ConditionTypeScript { - entity.DealwithScriptCondition(condition, &interfaceStatus, session.TenantId, processor.ProjectId, &processor.Result.PostConditions, - session.ExecUuid, true) + entity.DealwithScriptCondition(condition, &interfaceStatus, &processor.Result.PostConditions, true, session) } else if condition.Type == consts.ConditionTypeDatabase { - entity.DealwithDatabaseOptCondition(condition, processor.ID, processor.ParentId, &processor.Result.PostConditions, session.ExecUuid) + entity.DealwithDatabaseOptCondition(condition, processor.ParentId, &processor.Result.PostConditions, session) } else if condition.Type == consts.ConditionTypeExtractor { - entity.DealwithExtractorCondition(condition, - processor.ID, processor.ParentId, &processor.Result.PostConditions, session.ExecUuid) + entity.DealwithExtractorCondition(condition, processor.ParentId, &processor.Result.PostConditions, session) } else if condition.Type == consts.ConditionTypeResponseDefine { //entity.DealwithResponseDefineCondition(condition, &interfaceStatus, &processor.Result.PostConditions, detail, session.ExecUuid) @@ -156,7 +152,7 @@ func (entity *ProcessorInterface) ExecPostConditions(processor *Processor, detai for _, condition := range entity.PostConditions { if condition.Type == consts.ConditionTypeCheckpoint { entity.DealwithCheckpointCondition(condition, &interfaceStatus, &processor.Result.PostConditions, - detail, session.ExecUuid) + detail, session) } } @@ -164,20 +160,20 @@ func (entity *ProcessorInterface) ExecPostConditions(processor *Processor, detai } func (entity *ProcessorInterface) DealwithScriptCondition(condition domain.InterfaceExecCondition, - interfaceStatus *consts.ResultStatus, tenantId consts.TenantId, projectId uint, conditions *[]domain.InterfaceExecCondition, - execUuid string, isPostCondition bool) { + interfaceStatus *consts.ResultStatus, conditions *[]domain.InterfaceExecCondition, + isPostCondition bool, session *ExecSession) { var scriptBase domain.ScriptBase json.Unmarshal(condition.Raw, &scriptBase) if scriptBase.Disabled { return } - err := ExecScript(&scriptBase, tenantId, projectId, execUuid) // will set vari + err := ExecScript(&scriptBase, session) // will set vari if err != nil { } scriptHelper.GenResultMsg(&scriptBase) - scriptBase.VariableSettings = *GetGojaVariables(execUuid) + scriptBase.VariableSettings = *session.GetGojaVariables() interfaceExecCondition := domain.InterfaceExecCondition{ Type: condition.Type, @@ -186,7 +182,7 @@ func (entity *ProcessorInterface) DealwithScriptCondition(condition domain.Inter *conditions = append(*conditions, interfaceExecCondition) if isPostCondition { - for _, item := range *GetGojaLogs(execUuid) { + for _, item := range *session.GetGojaLogs() { createAssertFromScriptResult(item, conditions, interfaceStatus, scriptBase.ConditionId, scriptBase.ConditionEntityId) } @@ -194,7 +190,7 @@ func (entity *ProcessorInterface) DealwithScriptCondition(condition domain.Inter } func (entity *ProcessorInterface) DealwithDatabaseOptCondition(condition domain.InterfaceExecCondition, - processorId, parentId uint, conditions *[]domain.InterfaceExecCondition, execUuid string) { + parentId uint, conditions *[]domain.InterfaceExecCondition, session *ExecSession) { var databaseOptBase domain.DatabaseOptBase json.Unmarshal(condition.Raw, &databaseOptBase) @@ -202,7 +198,7 @@ func (entity *ProcessorInterface) DealwithDatabaseOptCondition(condition domain. return } - databaseOptBase.Sql = ReplaceVariableValue(databaseOptBase.Sql, execUuid) + databaseOptBase.Sql = ReplaceVariableValue(databaseOptBase.Sql, session) conditionStatus := true err := ExecDbOpt(&databaseOptBase) @@ -215,11 +211,11 @@ func (entity *ProcessorInterface) DealwithDatabaseOptCondition(condition domain. if databaseOptBase.JsonPath != "" && databaseOptBase.Variable != "" && conditionStatus { // will set vari scopeId := parentId if databaseOptBase.Scope == consts.Private { // put vari in its own scope if Private - scopeId = processorId + scopeId = session.GetCurrScenarioProcessorId() } SetVariable(scopeId, databaseOptBase.Variable, databaseOptBase.Result, databaseOptBase.ResultType, - consts.Public, execUuid) + consts.Public, session) } condition.Raw, _ = json.Marshal(databaseOptBase) @@ -227,7 +223,7 @@ func (entity *ProcessorInterface) DealwithDatabaseOptCondition(condition domain. } func (entity *ProcessorInterface) DealwithExtractorCondition(condition domain.InterfaceExecCondition, - processorId, parentId uint, conditions *[]domain.InterfaceExecCondition, execUuid string) { + parentId uint, conditions *[]domain.InterfaceExecCondition, session *ExecSession) { var extractorBase domain.ExtractorBase json.Unmarshal(condition.Raw, &extractorBase) @@ -248,11 +244,11 @@ func (entity *ProcessorInterface) DealwithExtractorCondition(condition domain.In if extractorBase.ResultStatus == consts.Pass { scopeId := parentId if extractorBase.Scope == consts.Private { // put vari in its own scope if Private - scopeId = processorId + scopeId = session.GetCurrScenarioProcessorId() } SetVariable(scopeId, extractorBase.Variable, extractorBase.Result, - extractorBase.ResultType, extractorBase.Scope, execUuid) + extractorBase.ResultType, extractorBase.Scope, session) } interfaceExecCondition := domain.InterfaceExecCondition{ @@ -264,7 +260,7 @@ func (entity *ProcessorInterface) DealwithExtractorCondition(condition domain.In func (entity *ProcessorInterface) DealwithCheckpointCondition(condition domain.InterfaceExecCondition, interfaceStatus *consts.ResultStatus, conditions *[]domain.InterfaceExecCondition, - detail *map[string]interface{}, execUuid string) { + detail *map[string]interface{}, session *ExecSession) { var checkpointBase domain.CheckpointBase json.Unmarshal(condition.Raw, &checkpointBase) @@ -273,7 +269,7 @@ func (entity *ProcessorInterface) DealwithCheckpointCondition(condition domain.I } resp := entity.Response - err := ExecCheckPoint(&checkpointBase, resp, 0, execUuid) + err := ExecCheckPoint(&checkpointBase, resp, 0, session) if err != nil || checkpointBase.ResultStatus == consts.Fail { *interfaceStatus = consts.Fail } @@ -328,7 +324,7 @@ func (entity *ProcessorInterface) DealwithResponseDefineCondition(condition doma func (entity *ProcessorInterface) GenResultFromResponse( processor *Processor, baseRequest domain.BaseRequest, requestEndTime, requestStartTime time.Time, - detail *map[string]interface{}, session *Session, err error) (ok bool) { + detail *map[string]interface{}, session *ExecSession, err error) (ok bool) { processor.Result.Cost = requestEndTime.UnixMilli() - requestStartTime.UnixMilli() reqContent, _ := json.Marshal(baseRequest) @@ -342,7 +338,7 @@ func (entity *ProcessorInterface) GenResultFromResponse( (*detail)["result"] = entity.Response.Content processor.Result.Detail = commonUtils.JsonEncode(*detail) - execUtils.SendErrorMsg(*processor.Result, consts.Processor, session.WsMsg) + execUtils.SendErrorMsg(*processor.Result, consts.Processor, session.ScenarioDebug.WsMsg) processor.AddResultToParent() return diff --git a/internal/agent/exec/processor-logic.go b/internal/agent/exec/processor-logic.go index d821e64d1..fada93851 100644 --- a/internal/agent/exec/processor-logic.go +++ b/internal/agent/exec/processor-logic.go @@ -17,7 +17,7 @@ type ProcessorLogic struct { Expression string `json:"expression" yaml:"expression"` } -func (entity ProcessorLogic) Run(processor *Processor, session *Session) (err error) { +func (entity ProcessorLogic) Run(processor *Processor, session *ExecSession) (err error) { defer func() { if errX := recover(); errX != nil { processor.Error(session, errX) @@ -44,17 +44,8 @@ func (entity ProcessorLogic) Run(processor *Processor, session *Session) (err er pass := false detail := map[string]interface{}{"name": entity.Name, "expression": entity.Expression} if typ == consts.ProcessorLogicIf { - var result interface{} - result, _, err = EvaluateGovaluateExpressionByProcessorScope(entity.Expression, entity.ProcessorID, session.ExecUuid) - if err != nil { - pass = false - } else { - var ok bool - pass, ok = result.(bool) - if !ok { - pass = false - } - } + result, _, _ := NewGojaSimple().ExecJsFuncSimple(entity.Expression, session, true) + pass, _ = result.(bool) } else if typ == consts.ProcessorLogicElse { brother, ok := getPreviousBrother(*processor) @@ -66,13 +57,13 @@ func (entity ProcessorLogic) Run(processor *Processor, session *Session) (err er processor.Result.ResultStatus, processor.Result.Summary = getResultStatus(pass) detail["result"] = pass processor.Result.Detail = commonUtils.JsonEncode(detail) - execUtils.SendExecMsg(*processor.Result, consts.Processor, session.WsMsg) + execUtils.SendExecMsg(*processor.Result, consts.Processor, session.ScenarioDebug.WsMsg) processor.AddResultToParent() executedProcessorIds := map[uint]bool{} if pass { for _, child := range processor.Children { - if GetForceStopExec(session.ExecUuid) { + if session.GetForceStop() { break } if child.Disable { @@ -88,8 +79,8 @@ func (entity ProcessorLogic) Run(processor *Processor, session *Session) (err er endTime := time.Now() processor.Result.EndTime = &endTime - stat := CountSkip(session.ExecUuid, executedProcessorIds, processor.Children) - execUtils.SendStatMsg(stat, session.WsMsg) + stat := CountSkip(executedProcessorIds, processor.Children, session) + execUtils.SendStatMsg(stat, session.ScenarioDebug.WsMsg) return } diff --git a/internal/agent/exec/processor-loop.go b/internal/agent/exec/processor-loop.go index af12cd9a3..5af6922ae 100644 --- a/internal/agent/exec/processor-loop.go +++ b/internal/agent/exec/processor-loop.go @@ -34,7 +34,7 @@ type ProcessorLoop struct { BreakIfExpression string `json:"breakIfExpression" yaml:"breakIfExpression"` } -func (entity ProcessorLoop) Run(processor *Processor, session *Session) (err error) { +func (entity ProcessorLoop) Run(processor *Processor, session *ExecSession) (err error) { defer func() { if errX := recover(); errX != nil { processor.Error(session, errX) @@ -58,14 +58,14 @@ func (entity ProcessorLoop) Run(processor *Processor, session *Session) (err err } processor.Result.Detail = commonUtils.JsonEncode(entity) - execUtils.SendExecMsg(*processor.Result, consts.Processor, session.WsMsg) + execUtils.SendExecMsg(*processor.Result, consts.Processor, session.ScenarioDebug.WsMsg) processor.Result.Iterator, processor.Result.Summary = entity.getIterator(session) if entity.ProcessorType == consts.ProcessorLoopUntil { - entity.runLoopUntil(session, processor, processor.Result.Iterator) + entity.runLoopUntil(processor, processor.Result.Iterator, session) } else { - entity.runLoopItems(session, processor, processor.Result.Iterator) + entity.runLoopItems(processor, processor.Result.Iterator, session) } processor.AddResultToParent() @@ -76,11 +76,11 @@ func (entity ProcessorLoop) Run(processor *Processor, session *Session) (err err return } -func (entity *ProcessorLoop) runLoopItems(session *Session, processor *Processor, iterator agentDomain.ExecIterator) (err error) { +func (entity *ProcessorLoop) runLoopItems(processor *Processor, iterator agentDomain.ExecIterator, session *ExecSession) (err error) { executedProcessorIds := map[uint]bool{} for index, item := range iterator.Items { - if GetForceStopExec(session.ExecUuid) { + if session.GetForceStop() { break } if DemoTestSite != "" && index > 2 { @@ -96,11 +96,11 @@ func (entity *ProcessorLoop) runLoopItems(session *Session, processor *Processor execUtils.SendExecMsg(msg, session.WsMsg) */ - SetVariable(entity.ProcessorID, iterator.VariableName, item, consts.ExtractorResultTypeString, consts.Public, session.ExecUuid) + SetVariable(entity.ProcessorID, iterator.VariableName, item, consts.ExtractorResultTypeString, consts.Public, session) round := "" for _, child := range processor.Children { - if GetForceStopExec(session.ExecUuid) { + if session.GetForceStop() { break } if child.Disable { @@ -126,26 +126,26 @@ func (entity *ProcessorLoop) runLoopItems(session *Session, processor *Processor // check break result := agentDomain.ScenarioExecResult{} - result.WillBreak, result.Summary, result.Detail = entity.getBeak(session.ExecUuid) + result.WillBreak, result.Summary, result.Detail = entity.getBeak(session) if result.WillBreak { - execUtils.SendExecMsg(result, consts.Processor, session.WsMsg) + execUtils.SendExecMsg(result, consts.Processor, session.ScenarioDebug.WsMsg) break } } - stat := CountSkip(session.ExecUuid, executedProcessorIds, processor.Children) - execUtils.SendStatMsg(stat, session.WsMsg) + stat := CountSkip(executedProcessorIds, processor.Children, session) + execUtils.SendStatMsg(stat, session.ScenarioDebug.WsMsg) return } -func (entity *ProcessorLoop) runLoopUntil(session *Session, processor *Processor, iterator agentDomain.ExecIterator) (err error) { +func (entity *ProcessorLoop) runLoopUntil(processor *Processor, iterator agentDomain.ExecIterator, session *ExecSession) (err error) { expression := iterator.UntilExpression executedProcessorIds := map[uint]bool{} index := 0 for { - if GetForceStopExec(session.ExecUuid) { + if session.GetForceStop() { break } if DemoTestSite != "" && index > 2 { @@ -153,21 +153,22 @@ func (entity *ProcessorLoop) runLoopUntil(session *Session, processor *Processor } index += 1 - result, _, err := EvaluateGovaluateExpressionByProcessorScope(expression, entity.ProcessorID, session.ExecUuid) + result, _, _ := NewGojaSimple().ExecJsFuncSimple(expression, session, true) pass, ok := result.(bool) - if err != nil || !ok || pass { + + if !ok || pass { result := agentDomain.ScenarioExecResult{ WillBreak: true, Summary: fmt.Sprintf("条件%s满足,跳出循环。", expression), } - execUtils.SendExecMsg(result, consts.Processor, session.WsMsg) + execUtils.SendExecMsg(result, consts.Processor, session.ScenarioDebug.WsMsg) break } round := "" for _, child := range processor.Children { - if GetForceStopExec(session.ExecUuid) { + if session.GetForceStop() { break } if child.Disable { @@ -197,27 +198,24 @@ func (entity *ProcessorLoop) runLoopUntil(session *Session, processor *Processor } LABEL: - stat := CountSkip(session.ExecUuid, executedProcessorIds, processor.Children) - execUtils.SendStatMsg(stat, session.WsMsg) + stat := CountSkip(executedProcessorIds, processor.Children, session) + execUtils.SendStatMsg(stat, session.ScenarioDebug.WsMsg) return } -func (entity *ProcessorLoop) getBeak(execUuid string) (ret bool, msg string, detailStr string) { +func (entity *ProcessorLoop) getBeak(session *ExecSession) (ret bool, msg string, detailStr string) { breakIfExpress := strings.TrimSpace(entity.BreakIfExpression) if breakIfExpress == "" { return } - expr := ReplaceDatapoolVariInGovaluateExpress(breakIfExpress, execUuid) - result, _, _ := EvaluateGovaluateExpressionByProcessorScope(expr, entity.ProcessorID, execUuid) + result, _, _ := NewGojaSimple().ExecJsFuncSimple(breakIfExpress, session, true) + pass, ok := result.(bool) - ret, ok := result.(bool) - pass := false - if ok && ret { + if ok && pass { msg = "真" - pass = true } else { msg = "假" } @@ -228,7 +226,7 @@ func (entity *ProcessorLoop) getBeak(execUuid string) (ret bool, msg string, det return } -func (entity *ProcessorLoop) getIterator(session *Session) (iterator agentDomain.ExecIterator, msg string) { +func (entity *ProcessorLoop) getIterator(session *ExecSession) (iterator agentDomain.ExecIterator, msg string) { if entity.ID == 0 { msg = "执行前请先配置处理器。" return @@ -240,7 +238,7 @@ func (entity *ProcessorLoop) getIterator(session *Session) (iterator agentDomain } else if entity.ProcessorType == consts.ProcessorLoopIn { if entity.InType == "variable" { - iterator, _ = entity.GenerateLoopVariable(session.ExecUuid) + iterator, _ = entity.GenerateLoopVariable(session) msg = fmt.Sprintf("\"%s\"。", entity.Variable) } else if entity.InType == "list" { @@ -282,8 +280,8 @@ func (entity *ProcessorLoop) GenerateLoopRange() (ret agentDomain.ExecIterator, return } -func (entity *ProcessorLoop) GenerateLoopVariable(execUuid string) (ret agentDomain.ExecIterator, err error) { - variableObj, err := GetVariable(entity.ProcessorID, entity.Variable, execUuid) +func (entity *ProcessorLoop) GenerateLoopVariable(session *ExecSession) (ret agentDomain.ExecIterator, err error) { + variableObj, err := GetVariable(entity.Variable, entity.ProcessorID, session) if err != nil { return } diff --git a/internal/agent/exec/processor-print.go b/internal/agent/exec/processor-print.go index 7a90f186a..51b86f089 100644 --- a/internal/agent/exec/processor-print.go +++ b/internal/agent/exec/processor-print.go @@ -19,7 +19,7 @@ type ProcessorPrint struct { RightValue string `json:"rightValue" yaml:"rightValue"` } -func (entity ProcessorPrint) Run(processor *Processor, session *Session) (err error) { +func (entity ProcessorPrint) Run(processor *Processor, session *ExecSession) (err error) { defer func() { if errX := recover(); errX != nil { processor.Error(session, errX) @@ -42,7 +42,7 @@ func (entity ProcessorPrint) Run(processor *Processor, session *Session) (err er Round: processor.Round, } - value := ReplaceVariableValue(entity.RightValue, session.ExecUuid) + value := ReplaceVariableValue(entity.RightValue, session) value = strings.TrimSpace(value) //processor.Result.Summary = strings.ReplaceAll(fmt.Sprintf("%s为\"%v\"。", entity.RightValue, value), "", "空") @@ -51,7 +51,7 @@ func (entity ProcessorPrint) Run(processor *Processor, session *Session) (err er processor.Result.Detail = commonUtils.JsonEncode(detail) processor.AddResultToParent() - execUtils.SendExecMsg(*processor.Result, consts.Processor, session.WsMsg) + execUtils.SendExecMsg(*processor.Result, consts.Processor, session.ScenarioDebug.WsMsg) endTime := time.Now() processor.Result.EndTime = &endTime diff --git a/internal/agent/exec/processor-root.go b/internal/agent/exec/processor-root.go index 762195aa2..06cffa308 100644 --- a/internal/agent/exec/processor-root.go +++ b/internal/agent/exec/processor-root.go @@ -14,7 +14,7 @@ type ProcessorRoot struct { ProcessorEntityBase } -func (entity ProcessorRoot) Run(processor *Processor, session *Session) (err error) { +func (entity ProcessorRoot) Run(processor *Processor, session *ExecSession) (err error) { logUtils.Infof("root entity") @@ -32,10 +32,10 @@ func (entity ProcessorRoot) Run(processor *Processor, session *Session) (err err //ParentLogId: , } - execUtils.SendExecMsg(*processor.Result, consts.Processor, session.WsMsg) + execUtils.SendExecMsg(*processor.Result, consts.Processor, session.ScenarioDebug.WsMsg) for _, child := range processor.Children { - if GetForceStopExec(session.ExecUuid) { + if session.GetForceStop() { break } if child.Disable { diff --git a/internal/agent/exec/processor-timer.go b/internal/agent/exec/processor-timer.go index 06d6d84ba..fbf5cd523 100644 --- a/internal/agent/exec/processor-timer.go +++ b/internal/agent/exec/processor-timer.go @@ -18,7 +18,7 @@ type ProcessorTimer struct { SleepTime int `json:"sleepTime" yaml:"sleepTime"` } -func (entity ProcessorTimer) Run(processor *Processor, session *Session) (err error) { +func (entity ProcessorTimer) Run(processor *Processor, session *ExecSession) (err error) { defer func() { if errX := recover(); errX != nil { processor.Error(session, errX) @@ -45,7 +45,7 @@ func (entity ProcessorTimer) Run(processor *Processor, session *Session) (err er processor.AddResultToParent() detail := map[string]interface{}{"name": entity.Name, "sleepTime": entity.SleepTime} processor.Result.Detail = commonUtils.JsonEncode(detail) - execUtils.SendExecMsg(*processor.Result, consts.Processor, session.WsMsg) + execUtils.SendExecMsg(*processor.Result, consts.Processor, session.ScenarioDebug.WsMsg) <-time.After(time.Duration(entity.SleepTime) * time.Second) diff --git a/internal/agent/exec/processor-variable.go b/internal/agent/exec/processor-variable.go index c2b4e9a4a..23642e60d 100644 --- a/internal/agent/exec/processor-variable.go +++ b/internal/agent/exec/processor-variable.go @@ -19,7 +19,7 @@ type ProcessorVariable struct { Expression string `json:"expression" yaml:"expression"` } -func (entity ProcessorVariable) Run(processor *Processor, session *Session) (err error) { +func (entity ProcessorVariable) Run(processor *Processor, session *ExecSession) (err error) { defer func() { if errX := recover(); errX != nil { processor.Error(session, errX) @@ -44,26 +44,22 @@ func (entity ProcessorVariable) Run(processor *Processor, session *Session) (err detail := map[string]interface{}{"name": entity.Name, "variableName": entity.VariableName} if entity.ProcessorType == consts.ProcessorVariableSet { var variableValue interface{} - variableValue, _, err = EvaluateGovaluateExpressionByProcessorScope(entity.Expression, processor.ID, session.ExecUuid) - - if err != nil { - panic(err) - //variableValue = err.Error() - } + variableValue = ReplaceVariableValue(entity.Expression, session) + //variableValue, _, _ = NewGojaSimple().ExecJsFuncSimple(entity.Expression, session, true) SetVariable(processor.ParentId, entity.VariableName, variableValue, consts.ExtractorResultTypeString, - consts.Public, session.ExecUuid) // set in parent scope + consts.Public, session) // set in parent scope processor.Result.Summary = fmt.Sprintf("\"%s\"为\"%v\"。", entity.VariableName, variableValue) detail["variableValue"] = variableValue } else if entity.ProcessorType == consts.ProcessorVariableClear { - ClearVariable(processor.ParentId, entity.VariableName, session.ExecUuid) + ClearVariable(processor.ParentId, entity.VariableName, session) processor.Result.Summary = fmt.Sprintf("\"%s\"成功。", entity.VariableName) } processor.AddResultToParent() processor.Result.Detail = commonUtils.JsonEncode(detail) - execUtils.SendExecMsg(*processor.Result, consts.Processor, session.WsMsg) + execUtils.SendExecMsg(*processor.Result, consts.Processor, session.ScenarioDebug.WsMsg) endTime := time.Now() processor.Result.EndTime = &endTime diff --git a/internal/agent/exec/processor.go b/internal/agent/exec/processor.go index f8cfa317f..b559f3a71 100644 --- a/internal/agent/exec/processor.go +++ b/internal/agent/exec/processor.go @@ -51,39 +51,35 @@ type ProcessorBase struct { ProcessorInterfaceSrc consts.ProcessorInterfaceSrc `json:"processorInterfaceSrc"` Round string `json:"round"` - - Session Session `json:"-"` } -func (p *Processor) Run(s *Session) (err error) { +func (p *Processor) Run(s *ExecSession) (err error) { _logUtils.Infof("%d - %s %s", p.ID, p.Name, p.EntityType) - SetCurrScenarioProcessorId(s.ExecUuid, p.ID) - SetCurrScenarioProcessor(s.ExecUuid, p) + s.SetCurrScenarioProcessorId(p.ID) + s.SetCurrScenarioProcessor(p) //每个执行器延迟0.1秒,防止发送ws消息过快,导致前端消息错误 time.Sleep(100 * time.Microsecond) - if !p.Disable && p.Entity != nil && !GetForceStopExec(s.ExecUuid) { + if !p.Disable && p.Entity != nil && !s.GetForceStop() { p.Entity.Run(p, s) } return } -func (p *Processor) Error(s *Session, err interface{}) { - +func (p *Processor) Error(s *ExecSession, err interface{}) { var detail map[string]interface{} commonUtils.JsonDecode(p.Result.Detail, &detail) if detail == nil { detail = map[string]interface{}{} } - detail["exception"] = fmt.Sprintf("错误:%v", err) + detail["exception"] = fmt.Sprintf("Processor %s exec error: %v", p.Name, err) p.Result.Detail = commonUtils.JsonEncode(detail) fmt.Printf("err=%v, stack=%s\n", err, string(debug.Stack())) p.AddResultToParent() - execUtils.SendExecMsg(p.Result, consts.Processor, s.WsMsg) - //execUtils.SendExceptionMsg(s.WsMsg) + execUtils.SendExecMsg(p.Result, consts.Exception, s.ScenarioDebug.WsMsg) } func (p *Processor) AddResultToParent() (err error) { diff --git a/internal/agent/exec/request.go b/internal/agent/exec/request.go index 61dc06abe..5b69914b7 100644 --- a/internal/agent/exec/request.go +++ b/internal/agent/exec/request.go @@ -108,33 +108,33 @@ func GetContentProps(req *domain.BaseRequest, resp *domain.DebugResponse) { return } -func ReplaceVariables(req *domain.BaseRequest, execUuid string) { +func ReplaceVariables(req *domain.BaseRequest, session *ExecSession) { // 每个接口的局部参数覆盖全局参数 mergeParams(req) - replaceUrl(req, execUuid) + replaceUrl(req, session) - replaceQueryParams(req, execUuid) - replacePathParams(req, execUuid) - replaceHeaders(req, execUuid) - replaceCookies(req, execUuid) - replaceFormBodies(req, execUuid) + replaceQueryParams(req, session) + replacePathParams(req, session) + replaceHeaders(req, session) + replaceCookies(req, session) + replaceFormBodies(req, session) - replaceBody(req, execUuid) - replaceAuthor(req, execUuid) + replaceBody(req, session) + replaceAuthor(req, session) } -func DealwithCookies(req *domain.BaseRequest, processorId uint, execUuid string) { +func DealwithCookies(req *domain.BaseRequest, processorId uint, session *ExecSession) { if req.Cookies != nil { - *req.Cookies = ListScopeCookie(processorId, execUuid) + *req.Cookies = ListScopeCookie(processorId, session) } } -func replaceUrl(req *domain.BaseRequest, execUuid string) { +func replaceUrl(req *domain.BaseRequest, session *ExecSession) { // project's global params already be added - req.Url = ReplaceVariableValue(req.Url, execUuid) + req.Url = ReplaceVariableValue(req.Url, session) } -func replaceQueryParams(req *domain.BaseRequest, execUuid string) { +func replaceQueryParams(req *domain.BaseRequest, session *ExecSession) { if req.GlobalParams != nil { for _, p := range *req.GlobalParams { if !p.Disabled && p.In == consts.ParamInQuery { @@ -157,7 +157,7 @@ func replaceQueryParams(req *domain.BaseRequest, execUuid string) { if param.Disabled { continue } - (*req.QueryParams)[idx].Value = ReplaceVariableValue(param.Value, execUuid) + (*req.QueryParams)[idx].Value = ReplaceVariableValue(param.Value, session) queryParams = append(queryParams, (*req.QueryParams)[idx]) } req.QueryParams = &queryParams @@ -165,7 +165,7 @@ func replaceQueryParams(req *domain.BaseRequest, execUuid string) { } -func replacePathParams(req *domain.BaseRequest, execUuid string) { +func replacePathParams(req *domain.BaseRequest, session *ExecSession) { var pathParams []domain.Param if req.PathParams != nil { @@ -173,7 +173,7 @@ func replacePathParams(req *domain.BaseRequest, execUuid string) { if param.Disabled || param.Name == "" { continue } - (*req.PathParams)[idx].Value = ReplaceVariableValue(param.Value, execUuid) + (*req.PathParams)[idx].Value = ReplaceVariableValue(param.Value, session) pathParams = append(pathParams, (*req.PathParams)[idx]) } req.PathParams = &pathParams @@ -182,7 +182,7 @@ func replacePathParams(req *domain.BaseRequest, execUuid string) { return } -func replaceHeaders(req *domain.BaseRequest, execUuid string) { +func replaceHeaders(req *domain.BaseRequest, session *ExecSession) { if req.GlobalParams != nil { for _, p := range *req.GlobalParams { if p.In == consts.ParamInHeader && !p.Disabled { @@ -203,14 +203,14 @@ func replaceHeaders(req *domain.BaseRequest, execUuid string) { if header.Disabled { continue } - (*req.Headers)[idx].Value = ReplaceVariableValue(header.Value, execUuid) + (*req.Headers)[idx].Value = ReplaceVariableValue(header.Value, session) headers = append(headers, (*req.Headers)[idx]) } req.Headers = &headers } } -func replaceCookies(req *domain.BaseRequest, execUuid string) { +func replaceCookies(req *domain.BaseRequest, session *ExecSession) { if req.GlobalParams != nil { for _, p := range *req.GlobalParams { if p.In == consts.ParamInCookie && !p.Disabled { @@ -232,14 +232,14 @@ func replaceCookies(req *domain.BaseRequest, execUuid string) { if cookie.Disabled { continue } - (*req.Cookies)[idx].Value = ReplaceVariableValue(_stringUtils.InterfToStr(cookie.Value), execUuid) + (*req.Cookies)[idx].Value = ReplaceVariableValue(_stringUtils.InterfToStr(cookie.Value), session) cookies = append(cookies, (*req.Cookies)[idx]) } *req.Cookies = cookies } } -func replaceFormBodies(req *domain.BaseRequest, execUuid string) { +func replaceFormBodies(req *domain.BaseRequest, session *ExecSession) { if req.GlobalParams != nil { for _, v := range *req.GlobalParams { if v.In == consts.ParamInBody && !v.Disabled { @@ -264,38 +264,38 @@ func replaceFormBodies(req *domain.BaseRequest, execUuid string) { } if req.BodyFormData != nil { for idx, item := range *req.BodyFormData { - (*req.BodyFormData)[idx].Value = ReplaceVariableValue(_stringUtils.InterfToStr(item.Value), execUuid) + (*req.BodyFormData)[idx].Value = ReplaceVariableValue(_stringUtils.InterfToStr(item.Value), session) } } if req.BodyFormUrlencoded != nil { for idx, item := range *req.BodyFormUrlencoded { - (*req.BodyFormUrlencoded)[idx].Value = ReplaceVariableValue(_stringUtils.InterfToStr(item.Value), execUuid) + (*req.BodyFormUrlencoded)[idx].Value = ReplaceVariableValue(_stringUtils.InterfToStr(item.Value), session) } } } -func replaceBody(req *domain.BaseRequest, execUuid string) { - req.Body = ReplaceVariableValueInBody(req.Body, execUuid) +func replaceBody(req *domain.BaseRequest, session *ExecSession) { + req.Body = ReplaceVariableValueInBody(req.Body, session) } -func replaceAuthor(req *domain.BaseRequest, execUuid string) { +func replaceAuthor(req *domain.BaseRequest, session *ExecSession) { if req.AuthorizationType == consts.BasicAuth { - req.BasicAuth.Username = ReplaceVariableValue(req.BasicAuth.Username, execUuid) - req.BasicAuth.Password = ReplaceVariableValue(req.BasicAuth.Password, execUuid) + req.BasicAuth.Username = ReplaceVariableValue(req.BasicAuth.Username, session) + req.BasicAuth.Password = ReplaceVariableValue(req.BasicAuth.Password, session) } else if req.AuthorizationType == consts.BearerToken { - req.BearerToken.Token = ReplaceVariableValue(req.BearerToken.Token, execUuid) + req.BearerToken.Token = ReplaceVariableValue(req.BearerToken.Token, session) } else if req.AuthorizationType == consts.OAuth2 { - req.OAuth20.Name = ReplaceVariableValue(req.OAuth20.Name, execUuid) - req.OAuth20.CallbackUrl = ReplaceVariableValue(req.OAuth20.CallbackUrl, execUuid) - req.OAuth20.AuthURL = ReplaceVariableValue(req.OAuth20.AuthURL, execUuid) - req.OAuth20.AccessTokenURL = ReplaceVariableValue(req.OAuth20.AccessTokenURL, execUuid) - req.OAuth20.ClientID = ReplaceVariableValue(req.OAuth20.ClientID, execUuid) - req.OAuth20.Scope = ReplaceVariableValue(req.OAuth20.Scope, execUuid) + req.OAuth20.Name = ReplaceVariableValue(req.OAuth20.Name, session) + req.OAuth20.CallbackUrl = ReplaceVariableValue(req.OAuth20.CallbackUrl, session) + req.OAuth20.AuthURL = ReplaceVariableValue(req.OAuth20.AuthURL, session) + req.OAuth20.AccessTokenURL = ReplaceVariableValue(req.OAuth20.AccessTokenURL, session) + req.OAuth20.ClientID = ReplaceVariableValue(req.OAuth20.ClientID, session) + req.OAuth20.Scope = ReplaceVariableValue(req.OAuth20.Scope, session) } else if req.AuthorizationType == consts.ApiKey { - req.ApiKey.Key = ReplaceVariableValue(req.ApiKey.Key, execUuid) - req.ApiKey.Value = ReplaceVariableValue(req.ApiKey.Value, execUuid) - req.ApiKey.TransferMode = ReplaceVariableValue(req.ApiKey.TransferMode, execUuid) + req.ApiKey.Key = ReplaceVariableValue(req.ApiKey.Key, session) + req.ApiKey.Value = ReplaceVariableValue(req.ApiKey.Value, session) + req.ApiKey.TransferMode = ReplaceVariableValue(req.ApiKey.TransferMode, session) } } diff --git a/internal/agent/exec/session-store-goja.go b/internal/agent/exec/session-store-goja.go new file mode 100644 index 000000000..f7b27a48e --- /dev/null +++ b/internal/agent/exec/session-store-goja.go @@ -0,0 +1,39 @@ +package agentExec + +import ( + "github.com/aaronchen2k/deeptest/internal/pkg/domain" +) + +func (s *ExecSession) GetGojaVariables() (ret *[]domain.ExecVariable) { + ret = s._gojaVariables + return +} +func (s *ExecSession) SetGojaVariables(val *[]domain.ExecVariable) { + s._gojaVariables = val + return +} +func (s *ExecSession) ResetGojaVariables() { + s._gojaVariables = &[]domain.ExecVariable{} + return +} +func (s *ExecSession) AppendGojaVariables(val domain.ExecVariable) { + *s._gojaVariables = append(*s._gojaVariables, val) + return +} + +func (s *ExecSession) GetGojaLogs() (ret *[]string) { + ret = s._gojaLogs + return +} +func (s *ExecSession) SetGojaLogs(val *[]string) { + s._gojaLogs = val + return +} +func (s *ExecSession) ResetGojaLogs() { + s._gojaLogs = &[]string{} + return +} +func (s *ExecSession) AppendGojaLog(item string) { + *s._gojaLogs = append(*s._gojaLogs, item) + return +} diff --git a/internal/agent/exec/session-store.go b/internal/agent/exec/session-store.go new file mode 100644 index 000000000..f007b6429 --- /dev/null +++ b/internal/agent/exec/session-store.go @@ -0,0 +1,69 @@ +package agentExec + +import "github.com/aaronchen2k/deeptest/internal/pkg/domain" + +func (s *ExecSession) SetIsRunning(val bool) { + s._isRunning = val +} +func (s *ExecSession) GetIsRunning() (ret bool) { + ret = s._isRunning + return +} +func (s *ExecSession) SetForceStop(val bool) { + s._forceStop = val +} +func (s *ExecSession) GetForceStop() (ret bool) { + ret = s._forceStop + return +} + +func (s *ExecSession) SetCurrRequest(val domain.BaseRequest) { + s.InterfaceDebug._currRequest = val +} +func (s *ExecSession) GetCurrRequest() (ret domain.BaseRequest) { + if s.InterfaceDebug != nil { + ret = s.InterfaceDebug._currRequest + } + return +} + +func (s *ExecSession) SetCurrResponse(val domain.DebugResponse) { + s.InterfaceDebug._currResponse = val +} +func (s *ExecSession) GetCurrResponse() (ret domain.DebugResponse) { + if s.InterfaceDebug != nil { + ret = s.InterfaceDebug._currResponse + } + + return +} + +func (s *ExecSession) SetCurrScenarioProcessor(val *Processor) { + s.ScenarioDebug._currProcessor = val +} +func (s *ExecSession) GetCurrScenarioProcessor() (ret *Processor) { + if s.ScenarioDebug != nil { + ret = s.ScenarioDebug._currProcessor + } + return +} + +func (s *ExecSession) SetCurrScenarioProcessorId(val uint) { + s.ScenarioDebug._currProcessorId = val +} +func (s *ExecSession) GetCurrScenarioProcessorId() (ret uint) { + if s.ScenarioDebug != nil { + ret = s.ScenarioDebug._currProcessorId + } + return +} + +func (s *ExecSession) SetCurrDebugInterfaceId(val uint) { + s.InterfaceDebug._debugInterfaceId = val +} +func (s *ExecSession) GetCurrDebugInterfaceId() (ret uint) { + if s.InterfaceDebug != nil { + ret = s.InterfaceDebug._debugInterfaceId + } + return +} diff --git a/internal/agent/exec/session.go b/internal/agent/exec/session.go index 4324430ca..782350fde 100644 --- a/internal/agent/exec/session.go +++ b/internal/agent/exec/session.go @@ -2,7 +2,11 @@ package agentExec import ( "crypto/tls" + agentExecDomain "github.com/aaronchen2k/deeptest/internal/agent/exec/domain" "github.com/aaronchen2k/deeptest/internal/pkg/consts" + "github.com/aaronchen2k/deeptest/internal/pkg/domain" + "github.com/dop251/goja" + "github.com/dop251/goja_nodejs/require" "github.com/kataras/iris/v12/websocket" "golang.org/x/net/http2" "net/http" @@ -10,35 +14,143 @@ import ( "time" ) -type Session struct { - ExecUuid string +type ExecSession struct { + Name string + VuNo int + ExecUuid string + EnvironmentId uint + ProjectId uint + TenantId consts.TenantId + + // exec status + ExecScene domain.ExecScene + _isRunning bool + _forceStop bool + + InterfaceStat agentExecDomain.InterfaceStat + + // communication + ServerUrl string + ServerToken string + + // goja engine + GojaRuntime *goja.Runtime + GojaRequire *require.RequireModule + GojaSetValueFunc func(name string, value interface{}) // call this as call js _setData method in goja + + _gojaLogs *[]string + _gojaVariables *[]domain.ExecVariable + + InterfaceDebug *InterfaceDebugSession + ScenarioDebug *ScenarioDebugSession +} + +type InterfaceDebugSession struct { + _debugInterfaceId uint + + AllVariables map[uint][]domain.ExecVariable + + // used to exchange request and response data between goja and go + _currRequest domain.BaseRequest + _currResponse domain.DebugResponse +} + +type ScenarioDebugSession struct { ScenarioId uint - Name string - TenantId consts.TenantId `json:"tenantId"` + + ScopedVariables map[uint][]domain.ExecVariable + ScopedCookies map[uint][]domain.ExecCookie + ScopeHierarchy map[uint]*[]uint // processId -> ancestorProcessIds + DatapoolCursor map[string]int + + _currProcessorId uint // for interface, pass an empty param to variable opt methods + _currProcessor *Processor + RootProcessor *Processor + Report *Report + + WsMsg *websocket.Message HttpClient *http.Client Http2Client *http.Client - Failfast bool +} - RootProcessor *Processor - Report *Report +func NewInterfaceExecSession(call domain.InterfaceCall) (session *ExecSession) { + session = &ExecSession{ + Name: call.Data.Name, + VuNo: 0, + ExecUuid: call.ExecUuid, - WsMsg *websocket.Message + EnvironmentId: call.Data.EnvironmentId, + ProjectId: call.Data.ProjectId, + TenantId: call.TenantId, + + ExecScene: call.ExecScene, + InterfaceStat: agentExecDomain.InterfaceStat{}, + + ServerUrl: call.ServerUrl, + ServerToken: call.Token, + + InterfaceDebug: &InterfaceDebugSession{ + AllVariables: map[uint][]domain.ExecVariable{}, + + _debugInterfaceId: call.Data.DebugInterfaceId, + _currRequest: domain.BaseRequest{}, + _currResponse: domain.DebugResponse{}, + }, + ScenarioDebug: &ScenarioDebugSession{ // just put an empty + ScopedVariables: map[uint][]domain.ExecVariable{}, + DatapoolCursor: map[string]int{}, + }, + } + session.ResetGojaVariables() + session.ResetGojaLogs() + + InitGojaRuntimeWithSession(session, session.VuNo, session.TenantId) + + return } -func NewSession(req *ScenarioExecObj, failfast bool, wsMsg *websocket.Message) (ret *Session) { - root := req.RootProcessor +func NewScenarioExecSession(vuNo int, req *ScenarioExecObj, environmentId uint, wsMsg *websocket.Message) (session *ExecSession) { + session = &ExecSession{ + Name: req.Name, + VuNo: vuNo, + ExecUuid: req.ExecUuid, + + EnvironmentId: environmentId, + ProjectId: req.RootProcessor.ProjectId, + TenantId: req.TenantId, + + ExecScene: req.ExecScene, + InterfaceStat: agentExecDomain.InterfaceStat{}, - session := Session{ - ScenarioId: root.ScenarioId, - Name: req.Name, - RootProcessor: root, - Failfast: failfast, - WsMsg: wsMsg, + ServerUrl: req.ServerUrl, + ServerToken: req.Token, + + GojaSetValueFunc: func(name string, value interface{}) {}, + + ScenarioDebug: &ScenarioDebugSession{ + ScenarioId: req.ScenarioId, + + RootProcessor: req.RootProcessor, + + DatapoolCursor: map[string]int{}, + + ScopedVariables: map[uint][]domain.ExecVariable{}, + ScopedCookies: map[uint][]domain.ExecCookie{}, + ScopeHierarchy: map[uint]*[]uint{}, + + WsMsg: wsMsg, + }, + InterfaceDebug: &InterfaceDebugSession{}, // just put an empty } + session.ResetGojaVariables() + session.ResetGojaLogs() + + InitGojaRuntimeWithSession(session, session.VuNo, session.TenantId) + ComputerScopeHierarchy(req.RootProcessor, &session.ScenarioDebug.ScopeHierarchy) jar, _ := cookiejar.New(nil) - session.HttpClient = &http.Client{ + session.ScenarioDebug.HttpClient = &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }, @@ -46,18 +158,16 @@ func NewSession(req *ScenarioExecObj, failfast bool, wsMsg *websocket.Message) ( Timeout: 120 * time.Second, } - session.Http2Client = &http.Client{ + session.ScenarioDebug.Http2Client = &http.Client{ Transport: &http2.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }, Timeout: 120 * time.Second, } - ret = &session - return } -func (s *Session) Run() { - s.RootProcessor.Run(s) +func (s *ExecSession) Run() { + s.ScenarioDebug.RootProcessor.Run(s) } diff --git a/internal/agent/exec/stat.go b/internal/agent/exec/stat.go index 55a77c616..2aeee7630 100644 --- a/internal/agent/exec/stat.go +++ b/internal/agent/exec/stat.go @@ -9,11 +9,11 @@ import ( "strings" ) -func ResetStat(execUuid string) { - SetInterfaceStat(execUuid, &agentDomain.InterfaceStat{}) +func ResetStat(session ExecSession) { + session.InterfaceStat = agentDomain.InterfaceStat{} } -func CountInterfaceStat(execUuid string, result *agentDomain.ScenarioExecResult) agentDomain.InterfaceStat { +func CountInterfaceStat(result *agentDomain.ScenarioExecResult, execUuid string) agentDomain.InterfaceStat { stat := GetInterfaceStat(execUuid) stat.InterfaceCount += 1 @@ -61,7 +61,7 @@ func CountInterfaceStat(execUuid string, result *agentDomain.ScenarioExecResult) return *stat } -func CountScriptAssertionStat(execUuid string, output string, result *agentDomain.ScenarioExecResult) agentDomain.InterfaceStat { +func CountScriptAssertionStat(output string, result *agentDomain.ScenarioExecResult, execUuid string) agentDomain.InterfaceStat { stat := GetInterfaceStat(execUuid) arr := []string{} @@ -84,15 +84,15 @@ func CountScriptAssertionStat(execUuid string, output string, result *agentDomai return *stat } -func CountSkip(execUuid string, executedProcessorIds map[uint]bool, skippedChildren []*Processor) agentDomain.InterfaceStat { +func CountSkip(executedProcessorIds map[uint]bool, skippedChildren []*Processor, session *ExecSession) agentDomain.InterfaceStat { countedProcessorIds := map[uint]bool{} - countSkipInterface(execUuid, executedProcessorIds, skippedChildren, &countedProcessorIds) + countSkipInterface(executedProcessorIds, skippedChildren, &countedProcessorIds, session) - return *GetInterfaceStat(execUuid) + return session.InterfaceStat } -func countSkipInterface(execUuid string, executedProcessorIds map[uint]bool, skippedChildren []*Processor, countedProcessorIds *map[uint]bool) agentDomain.InterfaceStat { - stat := GetInterfaceStat(execUuid) +func countSkipInterface(executedProcessorIds map[uint]bool, skippedChildren []*Processor, countedProcessorIds *map[uint]bool, session *ExecSession) agentDomain.InterfaceStat { + stat := session.InterfaceStat for _, child := range skippedChildren { if child.Disable { @@ -107,11 +107,12 @@ func countSkipInterface(execUuid string, executedProcessorIds map[uint]bool, ski } if len(child.Children) > 0 { - countSkipInterface(execUuid, map[uint]bool{}, child.Children, countedProcessorIds) + countSkipInterface(map[uint]bool{}, child.Children, countedProcessorIds, session) } } - return *stat + session.InterfaceStat = stat + return session.InterfaceStat } func ParseChaiAssertion(output string) (status, name, checkpoint string) { diff --git a/internal/agent/exec/sysfunc.go b/internal/agent/exec/sysfunc.go new file mode 100644 index 000000000..4d7ae1240 --- /dev/null +++ b/internal/agent/exec/sysfunc.go @@ -0,0 +1,133 @@ +package agentExec + +import ( + "crypto/md5" + "encoding/base64" + "encoding/hex" + "github.com/dop251/goja" + uuid "github.com/satori/go.uuid" + "io" + "math/rand" + "strings" + "time" +) + +type SysFunc struct { + Label string + Name string + Func interface{} + Value string + Desc string +} + +var SysFuncList []SysFunc + +func init() { + SysFuncList = []SysFunc{ + {"__uuid()", "__uuid", __uuid, "__uuid()", "唯一id"}, + {"__random(min,max)", "__random", __random, "__random(1,100)", "随机数"}, + {"__timestamp()", "__timestamp", __timestamp, "__timestamp()", "时间戳"}, + {"__base64encode(str)", "__base64encode", __base64encode, "__base64encode('')", "base64编码"}, + {"__base64decode(str)", "__base64decode", __base64decode, "__base64decode('')", "base64解码"}, + {"__md5(str)", "__md5", __md5, "__md5('')", "md5加密"}, + {"__strreplace(s,old,new)", "__strreplace", __strreplace, "__strreplace('abc','b','d')", "字符串替换"}, + {"__strlen(str)", "__strlen", __strlen, "__strlen('')", "字符串长度"}, + {"__strtolower(str)", "__strtolower", __strtolower, "__strtolower('')", "字符串转小写"}, + {"__strtoupper(str)", "__strtoupper", __strtoupper, "__strtoupper('')", "字符串转大写"}, + {"__substr(start,length)", "__substr", __substr, "__substr('abc',1,2)", "字符串截取"}, + } +} + +func defineJsSysFunc(runtime *goja.Runtime) { + for _, sysFunc := range SysFuncList { + runtime.Set(sysFunc.Name, sysFunc.Func) + } +} + +func __uuid() string { + uuid := uuid.NewV4() + return uuid.String() +} + +func __random(min, max int) int { + rand.Seed(time.Now().UnixNano()) + randomInt := rand.Intn(max - min) + return min + randomInt +} + +func __timestamp() int64 { + timestamp := time.Now().UnixMilli() + return timestamp +} + +func __base64encode(str string) string { + originalData := []byte(str) + encodedData := base64.StdEncoding.EncodeToString(originalData) + return encodedData +} + +func __base64decode(str string) string { + decodedData, err := base64.StdEncoding.DecodeString(str) + if err != nil { + return "" + } + return string(decodedData) +} + +func __md5(str string) string { + // 创建一个新的MD5哈希生成器 + hasher := md5.New() + // 写入要生成哈希的数据 + io.WriteString(hasher, str) + + // 计算最终的哈希值,返回的是一个16字节的数组 + hashBytes := hasher.Sum(nil) + + // 将字节数组转换为16进制的字符串表示 + hashString := hex.EncodeToString(hashBytes) + return hashString + +} + +func __strreplace(s, old, new string) string { + return strings.ReplaceAll(s, old, new) +} + +func __substr(s string, args ...int) string { + runeS := []rune(s) + lenS := len(runeS) + if len(args) == 0 { + return s + } + start := args[0] + if start < 0 { + start = lenS + start + } + if start < 0 { + start = 0 + } + + if len(args) == 1 { + return string(runeS[start:]) + } + + length := args[1] + + end := start + length + if end > lenS { + end = lenS + } + return string(runeS[start:end]) +} + +func __strlen(str string) int { + return len(str) +} + +func __strtolower(str string) string { + return strings.ToLower(str) +} + +func __strtoupper(str string) string { + return strings.ToUpper(str) +} diff --git a/internal/agent/exec/to-plan.go b/internal/agent/exec/to-plan.go index 6026e7f52..94bd84226 100644 --- a/internal/agent/exec/to-plan.go +++ b/internal/agent/exec/to-plan.go @@ -6,8 +6,8 @@ type PlanExecReq struct { ExecUuid string `json:"execUuid"` ServerUrl string `json:"serverUrl"` Token string `json:"token"` - PlanId int `json:"planId"` - EnvironmentId int `json:"environmentId"` + PlanId uint `json:"planId"` + EnvironmentId uint `json:"environmentId"` TenantId consts.TenantId `json:"tenantId"` } diff --git a/internal/agent/exec/to-scenario.go b/internal/agent/exec/to-scenario.go index 4fec5bb2a..2e2d3c1f6 100644 --- a/internal/agent/exec/to-scenario.go +++ b/internal/agent/exec/to-scenario.go @@ -10,9 +10,9 @@ type ScenarioExecReq struct { ServerUrl string `json:"serverUrl"` Token string `json:"token"` TenantId consts.TenantId `json:"tenantId"` - ScenarioId int `json:"scenarioId"` + ScenarioId uint `json:"scenarioId"` - EnvironmentId int `json:"environmentId"` + EnvironmentId uint `json:"environmentId"` } type ScenarioExecObj struct { @@ -38,3 +38,15 @@ type ScenarioExecObjBase struct { Token string `json:"token"` TenantId consts.TenantId `json:"tenantId"` } + +type WebsocketExecReq struct { + ServerUrl string `json:"serverUrl"` + Token string `json:"token"` + + Room string `json:"room"` + Data domain.WebsocketDebugData `json:"data"` + + WebsocketInterfaceId int `json:"websocketInterfaceId"` + EnvironmentId int `json:"environmentId"` + TenantId consts.TenantId `json:"tenantId"` +} diff --git a/internal/agent/exec/to-ws.go b/internal/agent/exec/to-ws.go new file mode 100644 index 000000000..058962240 --- /dev/null +++ b/internal/agent/exec/to-ws.go @@ -0,0 +1,20 @@ +package agentExec + +import ( + "github.com/aaronchen2k/deeptest/internal/pkg/consts" + "github.com/kataras/iris/v12" +) + +type WsReq struct { + Act consts.ExecType `json:"act"` + + ScenarioExecReq ScenarioExecReq `json:"scenarioExecReq"` + PlanExecReq PlanExecReq `json:"planExecReq"` + CasesExecReq CasesExecReq `json:"casesExecReq"` + + WebsocketExecReq WebsocketExecReq `json:"websocketExecReq"` + + MessageReq MessageExecReq `json:"messageReq"` + LocalVarsCache iris.Map `json:"localVarsCache"` + TenantId consts.TenantId `json:"tenantId"` +} diff --git a/internal/agent/exec/utils/exec/msg.go b/internal/agent/exec/utils/exec/msg.go index 7bbd41f00..59e1e9b77 100644 --- a/internal/agent/exec/utils/exec/msg.go +++ b/internal/agent/exec/utils/exec/msg.go @@ -62,9 +62,9 @@ func SendErrorMsg(log interface{}, category consts.WsMsgCategory, wsMsg *websock return } -func SendAlreadyRunningMsg(processor consts.WsMsgCategory, wsMsg *websocket.Message) (err error) { +func SendAlreadyRunningMsg(wsMsg *websocket.Message) (err error) { msg := _i118Utils.Sprintf("pls_stop_previous") - websocketHelper.SendExecMsg(msg, agentDomain.ScenarioExecResult{ProgressStatus: consts.InProgress}, processor, wsMsg) + websocketHelper.SendExecMsg(msg, agentDomain.ScenarioExecResult{ProgressStatus: consts.InProgress}, consts.Processor, wsMsg) _logUtils.Infof(msg) return diff --git a/internal/agent/exec/variable-find.go b/internal/agent/exec/variable-find.go index df5460a50..33b4c08cc 100644 --- a/internal/agent/exec/variable-find.go +++ b/internal/agent/exec/variable-find.go @@ -7,12 +7,12 @@ import ( "github.com/aaronchen2k/deeptest/internal/pkg/domain" ) -func getDynamicVariableFromScope(processorId uint, propExpression string, execUuid string) (ret domain.ExecVariable, err error) { - allValidIds := GetValidScopeIds(processorId, execUuid) +func getDynamicVariableFromScope(propExpression string, processorId uint, session *ExecSession) (ret domain.ExecVariable, err error) { + allValidIds := GetValidScopeIds(processorId, session) if allValidIds != nil { for _, id := range *allValidIds { - for _, item := range GetScopedVariables(execUuid)[id] { + for _, item := range session.ScenarioDebug.ScopedVariables[id] { if !(item.Scope == consts.Public || (item.Scope == consts.Private && id == processorId)) { continue } @@ -35,20 +35,20 @@ LABEL: return } -func getVariableFromShareVar(name string, execUuid string) (ret domain.ExecVariable, err error) { - execScene := GetExecScene(execUuid) +func getVariableFromShareVar(name string, session *ExecSession) (ret domain.ExecVariable, err error) { + execScene := session.ExecScene ret, err = GetVariableFromList(name, execScene.ShareVars) return } -func getVariableFromEnvVar(name string, execUuid string) (ret domain.ExecVariable, err error) { - execScene := GetExecScene(execUuid) +func getVariableFromEnvVar(name string, session *ExecSession) (ret domain.ExecVariable, err error) { + execScene := session.ExecScene - envId := uint(GetCurrEnvironmentId(execUuid)) + envId := session.EnvironmentId if envId == 0 { - envId = execScene.DebugInterfaceToEnvMap[GetCurrDebugInterfaceId(execUuid)] + envId = execScene.DebugInterfaceToEnvMap[session.GetCurrDebugInterfaceId()] } vars := execScene.EnvToVariables[envId] @@ -57,8 +57,8 @@ func getVariableFromEnvVar(name string, execUuid string) (ret domain.ExecVariabl return } -func getVariableFromGlobalVar(name, execUuid string) (ret domain.ExecVariable, err error) { - execScene := GetExecScene(execUuid) +func getVariableFromGlobalVar(name string, session *ExecSession) (ret domain.ExecVariable, err error) { + execScene := session.ExecScene ret, err = GetVariableFromList(name, execScene.GlobalVars) diff --git a/internal/agent/exec/variable-opt.go b/internal/agent/exec/variable-opt.go index dd039144b..e7584528c 100644 --- a/internal/agent/exec/variable-opt.go +++ b/internal/agent/exec/variable-opt.go @@ -6,9 +6,10 @@ import ( ) // GetVariable -// @Param processorId int true "Processor Id" -// @Param nameOrExpr string true "Variable name(email) or prop expression(address.city)" -func GetVariable(processorId uint, nameOrExpr string, execUuid string) (ret domain.ExecVariable, err error) { +// @Param processorId int true "Processor Id" +// @Param nameWithProp string true "Variable name(email) or name with prop(address.city)" +// @Param session ExecSession true "Execution Session" +func GetVariable(nameWithProp string, processorId uint, session *ExecSession) (ret domain.ExecVariable, err error) { /** for interface debug: Dynamic Vars: generated by current interface's earlier pre/post conditions (agent side) Shared Vars: generated by other interface in same serve (server side) @@ -20,7 +21,7 @@ func GetVariable(processorId uint, nameOrExpr string, execUuid string) (ret doma **/ // priority 1: Dynamic Vars, generated by execution (extractor, condition script, processor) - ret, err = getDynamicVariableFromScope(processorId, nameOrExpr, execUuid) + ret, err = getDynamicVariableFromScope(nameWithProp, processorId, session) if ret.Name != "" { return } @@ -28,19 +29,19 @@ func GetVariable(processorId uint, nameOrExpr string, execUuid string) (ret doma // priority 2: Shared Vars, for interface debug from server side // A. shared vars generated by Interface Debug in same serve // B. shared vars generated by Extractor and Processor in scenario - ret, err = getVariableFromShareVar(nameOrExpr, execUuid) + ret, err = getVariableFromShareVar(nameWithProp, session) if ret.Name != "" { return } // priority 3: Environment Vars, in project's serve settings - ret, err = getVariableFromEnvVar(nameOrExpr, execUuid) + ret, err = getVariableFromEnvVar(nameWithProp, session) if ret.Name != "" { return } // priority 4: Global Vars, on project level - ret, err = getVariableFromGlobalVar(nameOrExpr, execUuid) + ret, err = getVariableFromGlobalVar(nameWithProp, session) if ret.Name != "" { return } @@ -51,16 +52,14 @@ func GetVariable(processorId uint, nameOrExpr string, execUuid string) (ret doma // SetVariable // @Param processorId int true "Processor Id, 0 if NOT executed by a scenario processor" func SetVariable(processorId uint, variableName string, variableValue interface{}, - variableType consts.ExtractorResultType, scope consts.ExtractorScope, execUuid string) ( + variableType consts.ExtractorResultType, scope consts.ExtractorScope, session *ExecSession) ( newVariable domain.ExecVariable, err error) { - scopeHierarchy := GetScopeHierarchy(execUuid) - scopedVariables := GetScopedVariables(execUuid) + scopeHierarchy := session.ScenarioDebug.ScopeHierarchy + scopedVariables := session.ScenarioDebug.ScopedVariables found := false - //value, valueType := commUtils.GetValueInfo(variableValue) - newVariable = domain.ExecVariable{ Name: variableName, Value: variableValue, @@ -93,18 +92,18 @@ func SetVariable(processorId uint, variableName string, variableValue interface{ return } -func ClearVariable(processorId uint, variableName string, execUuid string) (err error) { - scopeHierarchy := GetScopeHierarchy(execUuid) - scopedVariables := GetScopedVariables(execUuid) +func ClearVariable(scopeId uint, variableName string, session *ExecSession) (err error) { + scopeHierarchy := session.ScenarioDebug.ScopeHierarchy + scopedVariables := session.ScenarioDebug.ScopedVariables deleteIndex := -1 targetScopeId := uint(0) - allValidIds := scopeHierarchy[processorId] + allValidIds := scopeHierarchy[scopeId] if allValidIds != nil { - if scopeHierarchy[processorId] != nil { - for _, id := range *scopeHierarchy[processorId] { + if scopeHierarchy[scopeId] != nil { + for _, id := range *scopeHierarchy[scopeId] { for index, item := range scopedVariables[id] { if item.Name == variableName { deleteIndex = index diff --git a/internal/agent/exec/variable-placeholder.go b/internal/agent/exec/variable-placeholder.go deleted file mode 100644 index a2cbab980..000000000 --- a/internal/agent/exec/variable-placeholder.go +++ /dev/null @@ -1,72 +0,0 @@ -package agentExec - -import ( - "fmt" - "github.com/aaronchen2k/deeptest/internal/pkg/consts" - commUtils "github.com/aaronchen2k/deeptest/internal/pkg/utils" - "strings" -) - -func ReplaceVariableValueInBody(value string, execUuid string) (ret string) { - // add a plus to set field vale as a number - // {"id": "${+dev_env_var1}"} => {"id": 2} - - variablePlaceholders := commUtils.GetVariablesInExpressionPlaceholder(value) - ret = value - - for _, placeholder := range variablePlaceholders { - oldVal := fmt.Sprintf("${%s}", placeholder) - if strings.Index(placeholder, "+") == 0 { // replace it with a number, if has prefix + - oldVal = "\"" + oldVal + "\"" - } - - placeholderWithoutPlus := strings.TrimLeft(placeholder, "+") - newVal := getPlaceholderVariableValue(placeholderWithoutPlus, execUuid) - - ret = strings.ReplaceAll(ret, oldVal, newVal) - } - - return -} - -func ReplaceVariableValue(value string, execUuid string) (ret string) { - ret = value - variablePlaceholders := commUtils.GetVariablesInExpressionPlaceholder(value) - - for _, placeholder := range variablePlaceholders { - oldVal := fmt.Sprintf("${%s}", placeholder) - - placeholderWithoutPlus := strings.TrimLeft(placeholder, "+") - newVal := getPlaceholderVariableValue(placeholderWithoutPlus, execUuid) - - ret = strings.ReplaceAll(ret, oldVal, newVal) - } - - return -} - -func getPlaceholderVariableValue(name string, execUuid string) (ret string) { - typ := getPlaceholderType(name) - - if typ == consts.PlaceholderTypeVariable { - variable, _ := GetVariable(GetCurrScenarioProcessorId(execUuid), name, execUuid) - ret, _ = commUtils.ConvertValueForPersistence(variable.Value) - - } else if typ == consts.PlaceholderTypeDatapool { - ret = getDatapoolValue(name, execUuid) - } - //else if typ == consts.PlaceholderTypeFunction { - //} - - return -} - -func getPlaceholderType(placeholder string) (ret consts.PlaceholderType) { - if strings.HasPrefix(placeholder, consts.PlaceholderPrefixDatapool.String()) { - return consts.PlaceholderTypeDatapool - } else if strings.HasPrefix(placeholder, consts.PlaceholderPrefixFunction.String()) { - return consts.PlaceholderTypeFunction - } - - return consts.PlaceholderTypeVariable -} diff --git a/internal/agent/exec/variable-replace.go b/internal/agent/exec/variable-replace.go new file mode 100644 index 000000000..52190d231 --- /dev/null +++ b/internal/agent/exec/variable-replace.go @@ -0,0 +1,241 @@ +package agentExec + +import ( + "github.com/aaronchen2k/deeptest/internal/pkg/consts" + mockData "github.com/aaronchen2k/deeptest/internal/pkg/helper/openapi-mock/openapi/generator/data" + commUtils "github.com/aaronchen2k/deeptest/internal/pkg/utils" + _commUtils "github.com/aaronchen2k/deeptest/pkg/lib/comm" + _stringUtils "github.com/aaronchen2k/deeptest/pkg/lib/string" + "regexp" + "strings" +) + +func ReplaceVariableValue(value string, session *ExecSession) (ret string) { + ret = value + placeholders := parseStatement(value) + + for _, placeholder := range placeholders { + // mock + if placeholder.Type == PlaceholderTypeMock { + result, err := (&mockData.MockjsGenerator{}).GenerateByMockJsExpression(placeholder.Content, "string") + if err == nil { + newVal := _stringUtils.InterfToStr(result) + + oldVal := placeholder.Whole + ret = strings.Replace(ret, oldVal, newVal, -1) + } + + continue + } + + // expression + if placeholder.Type == PlaceholderTypeExpression || placeholder.Type == PlaceholderTypeVariable { + result, _, _ := NewGojaSimple().ExecJsFuncSimple(placeholder.Content, session, true) + + newVal := _stringUtils.InterfToStr(result) + + oldVal := placeholder.Whole + ret = strings.Replace(ret, oldVal, newVal, -1) + + continue + } + } + return +} + +func ReplaceVariableValueInBody(value string, session *ExecSession) (ret string) { + // add a plus to set field vale as a number + // {"id": "${+dev_env_var1}"} => {"id": 2} + //var x interface{} + var data interface{} + _commUtils.JsonDecode(value, &data) + data = execJsFuncSimple(data, session) + ret = _commUtils.JsonEncode(data) + return + /* + variablePlaceholders := commUtils.GetVariablesInExpressionPlaceholder(value) + ret = value + + for _, placeholder := range variablePlaceholders { + oldVal := fmt.Sprintf("${%s}", placeholder) + if strings.Index(placeholder, "+") == 0 { // replace it with a number, if has prefix + + oldVal = "\"" + oldVal + "\"" + } + + placeholderWithoutPlus := strings.TrimLeft(placeholder, "+") + newVal := getPlaceholderVariableValue(placeholderWithoutPlus, session) + + ret = strings.ReplaceAll(ret, oldVal, newVal) + } + */ + + return +} + +func execJsFuncSimple(data interface{}, session *ExecSession) interface{} { + if v, ok := data.(string); ok { + return ReplaceVariableValue(v, session) + /* + if strings.HasPrefix(v, "${") && strings.HasSuffix(v, "}") { + expression := v[2 : len(v)-1] + result, _, _ := NewGojaSimple().ExecJsFuncSimple(expression, session, true) + return _stringUtils.InterfToStr(result) + } + */ + } + if v, ok := data.(map[string]interface{}); ok { + for key, item := range v { + v[key] = execJsFuncSimple(item, session) + } + } + + if v, ok := data.([]interface{}); ok { + for i, item := range v { + v[i] = execJsFuncSimple(item, session) + } + + } + + return data +} + +func parseStatement(statement string) (ret []Placeholder) { + // 前缀 ${任意长度的JS表达式} 后缀,可包含闭包函数function(){return 1} + //reg := regexp.MustCompile(`(?U)\$\{(.+)}`) + //arr := reg.FindAllStringSubmatch(statement, -1) + + //str := "xxxx${_mock('@string(pool, 1, 10)')}+==1222{_mock('@string(pool, 1, 10)')}-------" + + // 编译正则表达式 + // 注意:这里的正则表达式使用了非贪婪匹配(*?)来确保只匹配到最近的'}' + re := regexp.MustCompile(`\$\{(.*?)\}`) + + // 查找所有匹配的子串 + matches := re.FindAllStringSubmatch(statement, -1) + + var arrOrArr [][]string + // 打印所有匹配的子串(每个子串的第一个元素是完整匹配,第二个元素是括号内的匹配内容) + for _, match := range matches { + arrOrArr = append(arrOrArr, match) + } + + /* + placeholderStart := -1 + countLeftBrace := 0 + countRightBrace := 0 + arrOrArr := [][]string{} + + for index, char := range statement { + if char == '{' { + countLeftBrace++ + + if index > 0 && statement[index-1] == '$' { // found ${ + placeholderStart = index + arrOrArr = append(arrOrArr, []string{"${", ""}) + + continue + } else { + arrOrArr[len(arrOrArr)-1][0] += string(char) + arrOrArr[len(arrOrArr)-1][1] += string(char) + } + + continue + } + + if char == '}' { + arrOrArr[len(arrOrArr)-1][0] += string(char) + countRightBrace++ + + if countLeftBrace > 0 && countLeftBrace == countRightBrace { // expression finish + placeholderStart = -1 + countLeftBrace = 0 + countRightBrace = 0 + } else { + arrOrArr[len(arrOrArr)-1][1] += string(char) + } + + continue + } + + if placeholderStart > 0 { + arrOrArr[len(arrOrArr)-1][0] += string(char) + arrOrArr[len(arrOrArr)-1][1] += string(char) + } + } + */ + + for _, arr := range arrOrArr { + placeholder := Placeholder{ + Whole: arr[0], + Content: arr[1], + } + + if isVariable(placeholder.Content) { + placeholder.Type = PlaceholderTypeVariable + ret = append(ret, placeholder) + + continue + } + + reg1 := regexp.MustCompile(`_mock\("(.+)"\)`) + arr1 := reg1.FindAllStringSubmatch(placeholder.Content, -1) + + if len(arr1) > 0 { // is mock + placeholder.Type = PlaceholderTypeMock + placeholder.Content = arr1[0][1] + } else { + placeholder.Type = PlaceholderTypeExpression + } + + ret = append(ret, placeholder) + } + + return +} + +func getPlaceholderVariableValue(name string, session *ExecSession) (ret string) { + typ := getPlaceholderType(name) + + if typ == consts.PlaceholderTypeVariable { + variable, _ := GetVariable(name, session.GetCurrScenarioProcessorId(), session) + ret, _ = commUtils.ConvertValueForPersistence(variable.Value) + + } else if typ == consts.PlaceholderTypeDatapool { + ret = getDatapoolValue(name, session) + } + + return +} + +func getPlaceholderType(placeholder string) (ret consts.PlaceholderType) { + if strings.HasPrefix(placeholder, consts.PlaceholderPrefixDatapool.String()) { + return consts.PlaceholderTypeDatapool + } + + return consts.PlaceholderTypeVariable +} + +func isVariable(str string) (ret bool) { + reg := regexp.MustCompile(`^\+?[_A-Za-z][_A-Za-z0-9]*$`) + arr := reg.FindAllStringSubmatch(str, -1) + + if len(arr) > 0 { + ret = true + } + + return +} + +type Placeholder struct { + Whole string + Content string + Type PlaceholderType +} + +type PlaceholderType string + +const ( + PlaceholderTypeExpression PlaceholderType = "expression" + PlaceholderTypeMock PlaceholderType = "mock" + PlaceholderTypeVariable PlaceholderType = "variable" +) diff --git a/internal/agent/exec/variable.go b/internal/agent/exec/variable.go new file mode 100644 index 000000000..ee48eaa3e --- /dev/null +++ b/internal/agent/exec/variable.go @@ -0,0 +1,79 @@ +package agentExec + +import ( + "github.com/aaronchen2k/deeptest/internal/pkg/consts" + "github.com/aaronchen2k/deeptest/internal/pkg/domain" +) + +func GetAllValidVariables(session *ExecSession) (ret map[string]domain.ExecVariable) { + ret = map[string]domain.ExecVariable{} + + // global variables + globalVars := session.ExecScene.GlobalVars + popValidGlobalVariables(globalVars, &ret) + + // env variables + execScene := session.ExecScene + envId := session.EnvironmentId + if envId == 0 { + envId = execScene.DebugInterfaceToEnvMap[session.GetCurrDebugInterfaceId()] + } + envVars := execScene.EnvToVariables[envId] + popValidGlobalVariables(envVars, &ret) + + // share variables + shareVars := session.ExecScene.ShareVars + popValidGlobalVariables(shareVars, &ret) + + // share variables + allValidIds := GetValidScopeIds(session.GetCurrScenarioProcessorId(), session) + if allValidIds != nil { + for _, id := range *allValidIds { + for _, item := range session.ScenarioDebug.ScopedVariables[id] { + if !(item.Scope == consts.Public || + (item.Scope == consts.Private && id == session.GetCurrScenarioProcessorId())) { + continue + } + + addValidExecVariables(item, &ret) + } + } + } + + return +} + +func popValidGlobalVariables(srcVars []domain.GlobalVar, ret *map[string]domain.ExecVariable) { + for _, v := range srcVars { + if v.Name == "" { + continue + } + + val := "" + if v.LocalValue != "" { + val = v.LocalValue + } else if v.RemoteValue != "" { + val = v.RemoteValue + } + + (*ret)[v.Name] = domain.ExecVariable{ + Name: v.Name, + Value: val, + } + } + + return +} + +func addValidExecVariables(srcVar domain.ExecVariable, ret *map[string]domain.ExecVariable) { + if srcVar.Name == "" { + return + } + + (*ret)[srcVar.Name] = domain.ExecVariable{ + Name: srcVar.Name, + Value: srcVar.Value, + } + + return +} diff --git a/internal/agent/service/exec-by-websocket.go b/internal/agent/service/exec-by-websocket.go new file mode 100644 index 000000000..243dc8474 --- /dev/null +++ b/internal/agent/service/exec-by-websocket.go @@ -0,0 +1,107 @@ +package service + +import ( + "fmt" + agentExec "github.com/aaronchen2k/deeptest/internal/agent/exec" + execUtils "github.com/aaronchen2k/deeptest/internal/agent/exec/utils/exec" + "github.com/aaronchen2k/deeptest/internal/pkg/consts" + logUtils "github.com/aaronchen2k/deeptest/pkg/lib/log" + stringUtils "github.com/aaronchen2k/deeptest/pkg/lib/string" + "github.com/kataras/iris/v12/websocket" + "github.com/savsgio/gotils/strings" + "runtime/debug" +) + +func StartExec(req agentExec.WsReq, wsMsg *websocket.Message) (err error) { + act := req.Act + execUuid := getExecUuid(req) + if execUuid == "" { + logUtils.Info("****** execUuid is empty") + logUtils.Infof("%v", req) + return + } + + // stop exec + if act == consts.ExecStop { + StopExec(execUuid, wsMsg) + return + } + + // is running + ctx, _ := agentExec.GetExecCtx(execUuid) + if ctx != nil && (strings.Include([]string{consts.ExecScenario.String(), consts.ExecPlan.String(), consts.ExecCase.String()}, act.String())) { + execUtils.SendAlreadyRunningMsg(wsMsg) + return + } + + // exec task + go func() { + defer errDefer(wsMsg) + + agentExec.InitUserExecContext(execUuid) + + if act == consts.ExecScenario { + req.ScenarioExecReq.TenantId = req.TenantId + RunScenario(&req.ScenarioExecReq, req.LocalVarsCache, wsMsg) + + } else if act == consts.ExecPlan { + req.PlanExecReq.TenantId = req.TenantId + RunPlan(&req.PlanExecReq, req.LocalVarsCache, wsMsg) + + } else if act == consts.ExecCase { + req.CasesExecReq.TenantId = req.TenantId + RunCases(&req.CasesExecReq, req.LocalVarsCache, wsMsg) + + } else if act == consts.ExecMessage { + req.MessageReq.TenantId = req.TenantId + RunMessage(&req.MessageReq, wsMsg) + + } else if stringUtils.FindInArr(act.String(), consts.WebscoketAtions) { + req.WebsocketExecReq.TenantId = req.TenantId + RunWebsocket(act, &req.WebsocketExecReq, req.LocalVarsCache, wsMsg) + + } + + if !stringUtils.FindInArr(act.String(), consts.WebscoketAtions) { // keep context for websocket testing + agentExec.CancelExecCtx(execUuid) + } + }() + + return +} + +func getExecUuid(req agentExec.WsReq) (ret string) { + if req.ScenarioExecReq.ExecUuid != "" { + ret = req.ScenarioExecReq.ExecUuid + + } else if req.PlanExecReq.ExecUuid != "" { + ret = req.PlanExecReq.ExecUuid + + } else if req.CasesExecReq.ExecUuid != "" { + ret = req.CasesExecReq.ExecUuid + + } else if req.WebsocketExecReq.Room != "" { + ret = req.WebsocketExecReq.Room + + } + + return +} + +func StopExec(execUuid string, wsMsg *websocket.Message) (err error) { + agentExec.CancelExecCtx(execUuid) + execUtils.SendCancelMsg(wsMsg) + + return +} + +func errDefer(wsMsg *websocket.Message) { + err := recover() + + if err != nil { + s := string(debug.Stack()) + fmt.Printf("err=%v, stack=%s\n", err, s) + + execUtils.SendErrorMsg(err, consts.Processor, wsMsg) + } +} diff --git a/internal/agent/service/exec-cases.go b/internal/agent/service/exec-cases.go index 94559b66a..e16b0b0e5 100644 --- a/internal/agent/service/exec-cases.go +++ b/internal/agent/service/exec-cases.go @@ -14,10 +14,6 @@ import ( func RunCases(req *agentExec.CasesExecReq, localVarsCache iris.Map, wsMsg *websocket.Message) (err error) { logUtils.Infof("run cases %s on env %d", req.ExecUuid, req.EnvironmentId) - // reset exec - agentExec.ResetStat(req.ExecUuid) - agentExec.SetForceStopExec(req.ExecUuid, false) - // start msg execUtils.SendStartMsg(wsMsg) @@ -31,14 +27,17 @@ func RunCases(req *agentExec.CasesExecReq, localVarsCache iris.Map, wsMsg *webso } func doExecCases(req *agentExec.CasesExecReq, localVarsCache iris.Map, wsMsg *websocket.Message, parentUuid string) (err error) { + userCtx := agentExec.GetUserExecContext(parentUuid) casesExecObj := GetCasesToExec(req) for _, cs := range casesExecObj.Children { doExecCase(cs, localVarsCache, wsMsg, req.TenantId, req.ExecUuid, parentUuid, req.ProjectId) - // stop if needed - if agentExec.GetForceStopExec(parentUuid) { + select { + case <-userCtx.ExecCtx.Done(): break + + default: } } @@ -66,49 +65,41 @@ func doExecCase(cs *agentExec.CaseExecProcessor, localVarsCache iris.Map, wsMsg return } - caseInterfaceExecObj := cs.Data - updateLocalValues(&caseInterfaceExecObj.ExecScene, localVarsCache) - // variables etc. - agentExec.SetExecScene(execUuid, caseInterfaceExecObj.ExecScene) - - // execution - agentExec.SetCurrDebugInterfaceId(execUuid, caseInterfaceExecObj.DebugData.DebugInterfaceId) // must use execUuid not parentUuid - agentExec.SetCurrScenarioProcessorId(parentUuid, 0) // not in a scenario - - agentExec.SetCurrRequest(parentUuid, domain.BaseRequest{}) - agentExec.SetCurrResponse(parentUuid, domain.DebugResponse{}) - agentExec.SetExecScene(parentUuid, caseInterfaceExecObj.ExecScene) - // init context - agentExec.InitDebugExecContext(execUuid) - agentExec.InitJsRuntime(tenantId, projectId, execUuid) + call := domain.InterfaceCall{ + ExecUuid: cs.ExecUUid, + Data: cs.Data.DebugData, + ExecScene: cs.Data.ExecScene, + } + updateLocalValues(&call.ExecScene, localVarsCache) + session := agentExec.NewInterfaceExecSession(call) // not in a scenario - agentExec.ExecPreConditions(caseInterfaceExecObj, execUuid) // must before PreRequest, since it will update the vari in script - originalReqUri, _ := PreRequest(&caseInterfaceExecObj.DebugData, execUuid) + agentExec.ExecPreConditions(cs.Data, session) // must before PreRequest, since it will update the vari in script + originalReqUri, _ := PreRequest(&cs.Data.DebugData, session) - agentExec.SetReqValueToGoja(&caseInterfaceExecObj.DebugData.BaseRequest) - agentExec.GetReqValueFromGoja(execUuid, tenantId, projectId) + agentExec.SetReqValueToGoja(&cs.Data.DebugData.BaseRequest, session) + agentExec.GetReqValueFromGoja(session) // a new interface may not has a pre-script, which will not update agentExec.CurrRequest, need to skip - if agentExec.GetCurrRequest(execUuid).Url != "" { - caseInterfaceExecObj.DebugData.BaseRequest = agentExec.GetCurrRequest(execUuid) // update to the value changed in goja + if session.GetCurrRequest().Url != "" { + cs.Data.DebugData.BaseRequest = session.GetCurrRequest() // update to the value changed in goja } - resultResp, err1 := RequestInterface(&caseInterfaceExecObj.DebugData) + resultResp, err1 := RequestInterface(&cs.Data.DebugData) if err1 != nil { execUtils.SendResult(err1, wsMsg) return err1 } - agentExec.SetRespValueToGoja(&resultResp) - assertResultStatus, _ := agentExec.ExecPostConditions(caseInterfaceExecObj, resultResp, execUuid) - agentExec.GetRespValueFromGoja(execUuid, tenantId, projectId) - PostRequest(originalReqUri, &caseInterfaceExecObj.DebugData) + agentExec.SetRespValueToGoja(&resultResp, session) + assertResultStatus, _ := agentExec.ExecPostConditions(cs.Data, resultResp, session) + agentExec.GetRespValueFromGoja(session) + PostRequest(originalReqUri, &cs.Data.DebugData) // get the response data updated by script post-condition - if agentExec.GetCurrResponse(execUuid).Data != nil { - resultResp = agentExec.GetCurrResponse(execUuid) - resultResp.ConsoleLogs = GenConditionLogsForCase(caseInterfaceExecObj) // only for cases + if session.GetCurrResponse().Data != nil { + resultResp = session.GetCurrResponse() + resultResp.ConsoleLogs = GenConditionLogsForCase(cs.Data) // only for cases } status := consts.Pass @@ -121,7 +112,7 @@ func doExecCase(cs *agentExec.CaseExecProcessor, localVarsCache iris.Map, wsMsg "execUuid": execUuid, "caseUuid": cs.Key, - "request": caseInterfaceExecObj, + "request": cs.Data, "response": resultResp, "status": status, diff --git a/internal/agent/service/exec-interface.go b/internal/agent/service/exec-interface.go index bff095dcb..a38720b8f 100644 --- a/internal/agent/service/exec-interface.go +++ b/internal/agent/service/exec-interface.go @@ -1,7 +1,6 @@ package service import ( - agentDomain "github.com/aaronchen2k/deeptest/cmd/agent/v1/domain" agentExec "github.com/aaronchen2k/deeptest/internal/agent/exec" execUtils "github.com/aaronchen2k/deeptest/internal/agent/exec/utils/exec" "github.com/aaronchen2k/deeptest/internal/pkg/consts" @@ -10,46 +9,36 @@ import ( logUtils "github.com/aaronchen2k/deeptest/pkg/lib/log" ) -func RunInterface(call agentDomain.InterfaceCall) (resultReq domain.DebugData, resultResp domain.DebugResponse, err error) { - agentExec.SetServerUrl(call.ExecUuid, call.ServerUrl) - agentExec.SetServerToken(call.ExecUuid, call.Token) +func RunInterface(call domain.InterfaceCall) (resultReq domain.DebugData, resultResp domain.DebugResponse, err error) { req := GetInterfaceToExec(call) + call.ExecScene = req.ExecScene updateLocalValues(&req.ExecScene, call.LocalVarsCache) - agentExec.SetCurrDebugInterfaceId(call.ExecUuid, req.DebugData.DebugInterfaceId) - agentExec.SetCurrScenarioProcessorId(call.ExecUuid, 0) // not in a scenario + session := agentExec.NewInterfaceExecSession(call) - agentExec.SetCurrRequest(call.ExecUuid, domain.BaseRequest{}) - agentExec.SetCurrResponse(call.ExecUuid, domain.DebugResponse{}) - agentExec.SetExecScene(call.ExecUuid, req.ExecScene) + agentExec.SetReqValueToGoja(&req.DebugData.BaseRequest, session) - // init context - agentExec.InitDebugExecContext(call.ExecUuid) - agentExec.InitJsRuntime(call.TenantId, call.Data.ProjectId, call.ExecUuid) + agentExec.ExecPreConditions(&req, session) // must before PreRequest, since it will update the vari in script + originalReqUri, _ := PreRequest(&req.DebugData, session) - agentExec.SetReqValueToGoja(&req.DebugData.BaseRequest) - - agentExec.ExecPreConditions(&req, call.ExecUuid) // must before PreRequest, since it will update the vari in script - originalReqUri, _ := PreRequest(&req.DebugData, call.ExecUuid) - - agentExec.GetReqValueFromGoja(call.ExecUuid, call.TenantId, call.Data.ProjectId) + agentExec.GetReqValueFromGoja(session) // TODO: a new interface may not has a pre-script, which will not update agentExec.CurrRequest, need to skip - if agentExec.GetCurrRequest(call.ExecUuid).Url != "" { - req.DebugData.BaseRequest = agentExec.GetCurrRequest(call.ExecUuid) // update to the value changed in goja + if session.GetCurrRequest().Url != "" { + req.DebugData.BaseRequest = session.GetCurrRequest() // update to the value changed in goja } resultResp, err = RequestInterface(&req.DebugData) - agentExec.SetRespValueToGoja(&resultResp) - assertResultStatusPost, _ := agentExec.ExecPostConditions(&req, resultResp, call.ExecUuid) + agentExec.SetRespValueToGoja(&resultResp, session) + assertResultStatusPost, _ := agentExec.ExecPostConditions(&req, resultResp, session) - agentExec.GetRespValueFromGoja(call.ExecUuid, call.TenantId, call.Data.ProjectId) + agentExec.GetRespValueFromGoja(session) PostRequest(originalReqUri, &req.DebugData) // get the response data updated by script post-condition - if agentExec.GetCurrResponse(call.ExecUuid).Data != nil { - resultResp = agentExec.GetCurrResponse(call.ExecUuid) + if session.GetCurrResponse().Data != nil { + resultResp = session.GetCurrResponse() } // submit result @@ -60,9 +49,9 @@ func RunInterface(call agentDomain.InterfaceCall) (resultReq domain.DebugData, r return } -func PreRequest(req *domain.DebugData, execUuid string) (originalReqUri string, err error) { +func PreRequest(req *domain.DebugData, session *agentExec.ExecSession) (originalReqUri string, err error) { // replace variables - agentExec.ReplaceVariables(&req.BaseRequest, execUuid) + agentExec.ReplaceVariables(&req.BaseRequest, session) // gen url req.BaseRequest.Url, originalReqUri = UpdateUrl(*req) @@ -73,7 +62,7 @@ func PreRequest(req *domain.DebugData, execUuid string) (originalReqUri string, if req.BodyFormData != nil { for index, item := range *req.BodyFormData { if item.Type == consts.FormDataTypeFile { - (*req.BodyFormData)[index].Value, err = agentExec.DownloadUploadedFile(item.Value, execUuid) + (*req.BodyFormData)[index].Value, err = agentExec.DownloadUploadedFile(item.Value, session) } } } diff --git a/internal/agent/service/exec-plan.go b/internal/agent/service/exec-plan.go index f42dffe14..868531281 100644 --- a/internal/agent/service/exec-plan.go +++ b/internal/agent/service/exec-plan.go @@ -11,12 +11,6 @@ import ( func RunPlan(req *agentExec.PlanExecReq, localVarsCache iris.Map, wsMsg *websocket.Message) (err error) { execUuid := req.ExecUuid - agentExec.ResetStat(req.ExecUuid) - agentExec.SetForceStopExec(execUuid, false) - - agentExec.SetServerUrl(execUuid, req.ServerUrl) - agentExec.SetServerToken(execUuid, req.Token) - planExecObj := GetPlanToExec(req) if planExecObj == nil || len(planExecObj.Scenarios) == 0 { execUtils.SendEndMsg(wsMsg) @@ -39,26 +33,29 @@ func RunPlan(req *agentExec.PlanExecReq, localVarsCache iris.Map, wsMsg *websock ID: req.PlanId, } - for _, scenario := range planExecObj.Scenarios { - updateLocalValues(&scenario.ExecScene, localVarsCache) + for _, scenarioExecObj := range planExecObj.Scenarios { + scenarioExecObj.ExecUuid = execUuid + scenarioExecObj.ServerUrl = req.ServerUrl + scenarioExecObj.Token = req.Token + updateLocalValues(&scenarioExecObj.ExecScene, localVarsCache) + + session := agentExec.NewScenarioExecSession(0, &scenarioExecObj, req.EnvironmentId, wsMsg) + err = ExecScenario(session) - scenario.ExecUuid = execUuid - session, _ := ExecScenario(&scenario, req.EnvironmentId, wsMsg) + scenarioReport, _ := SubmitScenarioResult(*session.ScenarioDebug.RootProcessor.Result, session.ScenarioDebug.RootProcessor.Result.ScenarioId, + session.ServerUrl, session.ServerToken, session.TenantId) + session.ScenarioDebug.RootProcessor.Result.EnvironmentId = req.EnvironmentId - scenarioReport, _ := SubmitScenarioResult(*session.RootProcessor.Result, session.RootProcessor.Result.ScenarioId, - agentExec.GetServerUrl(execUuid), agentExec.GetServerToken(execUuid), req.TenantId) - session.RootProcessor.Result.EnvironmentId = req.EnvironmentId + session.ScenarioDebug.RootProcessor.Result.ScenarioReportId = uint(scenarioReport.ID) + result.Scenarios = append(result.Scenarios, session.ScenarioDebug.RootProcessor.Result) - session.RootProcessor.Result.ScenarioReportId = uint(scenarioReport.ID) - result.Scenarios = append(result.Scenarios, session.RootProcessor.Result) - execUtils.SendResultMsg(scenarioReport, session.WsMsg) + execUtils.SendResultMsg(scenarioReport, session.ScenarioDebug.WsMsg) } // submit result result.Stat = *agentExec.GetInterfaceStat(execUuid) report, _ := SubmitPlanResult(result, req.PlanId, req.ServerUrl, req.Token, req.TenantId) execUtils.SendResultMsg(report, wsMsg) - //sendPlanSubmitResult(req.PlanId, wsMsg) // end msg execUtils.SendEndMsg(wsMsg) diff --git a/internal/agent/service/exec-scenario.go b/internal/agent/service/exec-scenario.go index de052dabf..544e07fcf 100644 --- a/internal/agent/service/exec-scenario.go +++ b/internal/agent/service/exec-scenario.go @@ -6,19 +6,12 @@ import ( logUtils "github.com/aaronchen2k/deeptest/pkg/lib/log" "github.com/kataras/iris/v12" "github.com/kataras/iris/v12/websocket" - "go.uber.org/zap" ) func RunScenario(req *agentExec.ScenarioExecReq, localVarsCache iris.Map, wsMsg *websocket.Message) (err error) { - logUtils.Infof("run scenario", zap.Int("ScenarioId", req.ScenarioId), zap.Int("environmentId", req.EnvironmentId)) + logUtils.Infof("run scenario %d on environment %d", req.ScenarioId, req.EnvironmentId) - agentExec.ResetStat(req.ExecUuid) - agentExec.SetForceStopExec(req.ExecUuid, false) - - agentExec.SetServerUrl(req.ExecUuid, req.ServerUrl) - agentExec.SetServerToken(req.ExecUuid, req.Token) - - // start msg + // send start msg execUtils.SendStartMsg(wsMsg) //场景执行初始信息 @@ -34,17 +27,18 @@ func RunScenario(req *agentExec.ScenarioExecReq, localVarsCache iris.Map, wsMsg scenarioExecObj.ExecUuid = req.ExecUuid - session, err := ExecScenario(scenarioExecObj, req.EnvironmentId, wsMsg) - session.RootProcessor.Result.Stat = *agentExec.GetInterfaceStat(req.ExecUuid) - session.RootProcessor.Result.EnvironmentId = req.EnvironmentId - session.RootProcessor.Result.ScenarioId = uint(req.ScenarioId) + session := agentExec.NewScenarioExecSession(0, scenarioExecObj, req.EnvironmentId, wsMsg) + err = ExecScenario(session) + + session.ScenarioDebug.RootProcessor.Result.Stat = *agentExec.GetInterfaceStat(session.ExecUuid) + session.ScenarioDebug.RootProcessor.Result.EnvironmentId = req.EnvironmentId + session.ScenarioDebug.RootProcessor.Result.ScenarioId = req.ScenarioId // submit result - report, _ := SubmitScenarioResult(*session.RootProcessor.Result, scenarioExecObj.RootProcessor.ScenarioId, - agentExec.GetServerUrl(req.ExecUuid), agentExec.GetServerToken(req.ExecUuid), req.TenantId) + report, _ := SubmitScenarioResult(*session.ScenarioDebug.RootProcessor.Result, scenarioExecObj.RootProcessor.ScenarioId, + session.ServerUrl, session.ServerToken, req.TenantId) - execUtils.SendResultMsg(report, session.WsMsg) - //sendScenarioSubmitResult(session.RootProcessor.ID, session.WsMsg) + execUtils.SendResultMsg(report, session.ScenarioDebug.WsMsg) // end msg execUtils.SendEndMsg(wsMsg) @@ -52,28 +46,13 @@ func RunScenario(req *agentExec.ScenarioExecReq, localVarsCache iris.Map, wsMsg return } -func ExecScenario(execObj *agentExec.ScenarioExecObj, selectedEnvironmentId int, wsMsg *websocket.Message) ( - session *agentExec.Session, err error) { - // variables etc. - agentExec.SetCurrEnvironmentId(execObj.ExecUuid, selectedEnvironmentId) - agentExec.SetExecScene(execObj.ExecUuid, execObj.ExecScene) - - RestoreEntityFromRawAndSetParent(execObj.RootProcessor) - - agentExec.InitScenarioExecContext(execObj) - agentExec.InitJsRuntime(execObj.TenantId, execObj.RootProcessor.ProjectId, execObj.ExecUuid) - - // start msg - //execUtils.SendStartMsg(wsMsg) - - // execution - session = agentExec.NewSession(execObj, false, wsMsg) - session.ExecUuid = execObj.ExecUuid +func ExecScenario(session *agentExec.ExecSession) (err error) { + RestoreEntityFromRawAndSetParent(session.ScenarioDebug.RootProcessor) session.Run() - if session.RootProcessor.Result != nil { - session.RootProcessor.Result.ScenarioId = execObj.ScenarioId + if session.ScenarioDebug.RootProcessor.Result != nil { + session.ScenarioDebug.RootProcessor.Result.ScenarioId = session.ScenarioDebug.ScenarioId } return diff --git a/internal/agent/service/exec-websocket.go b/internal/agent/service/exec-websocket.go index 30357857f..2c79a09ec 100644 --- a/internal/agent/service/exec-websocket.go +++ b/internal/agent/service/exec-websocket.go @@ -1,102 +1,309 @@ package service import ( + "context" "fmt" - agentDomain "github.com/aaronchen2k/deeptest/cmd/agent/v1/domain" - agentExec "github.com/aaronchen2k/deeptest/internal/agent/exec" + "github.com/aaronchen2k/deeptest/internal/agent/exec" execUtils "github.com/aaronchen2k/deeptest/internal/agent/exec/utils/exec" "github.com/aaronchen2k/deeptest/internal/pkg/consts" + "github.com/aaronchen2k/deeptest/internal/pkg/domain" logUtils "github.com/aaronchen2k/deeptest/pkg/lib/log" + ws "github.com/gorilla/websocket" + "github.com/kataras/iris/v12" "github.com/kataras/iris/v12/websocket" - "github.com/savsgio/gotils/strings" - "runtime/debug" + "github.com/kataras/neffos" + "github.com/kataras/neffos/gorilla" + "log" + "net/http" + "net/url" + "sync" + "time" ) -func StartExec(req agentDomain.WsReq, wsMsg *websocket.Message) (err error) { - act := req.Act - execUuid := getExecUuid(req) - if execUuid == "" { - logUtils.Info("****** execUuid is empty") - logUtils.Infof("%v", req) +var ( + WebsocketContextStore sync.Map +) + +func RunWebsocket(act consts.ExecType, req *agentExec.WebsocketExecReq, localVarsCache iris.Map, wsMsg *websocket.Message) (err error) { + logUtils.Infof("run websocket test") + + dpRoom := req.Room + execData := req.Data + + if req.Data.ExtMode { + if act == consts.ExecWebsocketConnect { + err = connectToWebsocketExt(dpRoom, execData, wsMsg) + } else if act == consts.ExecWebsocketDisconnect { + err = disconnectToWebsocketExt(dpRoom, execData, wsMsg) + } else if act == consts.ExecWebsocketSendMsg { + err = sendMessageExt(dpRoom, execData, wsMsg) + } + } else { + if act == consts.ExecWebsocketConnect { + err = connectToWebsocketSimple(dpRoom, execData, wsMsg) + } else if act == consts.ExecWebsocketDisconnect { + err = disconnectToWebsocketSimple(dpRoom, execData, wsMsg) + } else if act == consts.ExecWebsocketSendMsg { + err = sendMessageSimple(dpRoom, execData, wsMsg) + } + } + + return +} + +// Simple Websocket mode +func connectToWebsocketSimple(dpRoom string, execData domain.WebsocketDebugData, wsMsg *websocket.Message) (err error) { + address := genUrl(execData.Address, execData.Params) + header := genHeaders(execData.Headers) + + wsConn, _, err := ws.DefaultDialer.Dial(address, header) + if err != nil { + msg := fmt.Sprintf("dial websocket error: %s", err.Error()) + logUtils.Infof(msg) + + execUtils.SendExecMsg(msg, consts.Exception, wsMsg) return } - isRunning := agentExec.GetIsRunning(execUuid) + setWsConn(dpRoom, wsConn) + + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) + setExecCtx(dpRoom, &ctx) + + // listen websocket messages + go func() { + defer cancel() + + for { + message := "" + + _, msg, err := wsConn.ReadMessage() + if err != nil { + //message = "ERR: " + err.Error() + //execUtils.SendExecMsg(message, consts.ProgressResult, wsMsg) + goto Label_END_WEBSOCKET_TEST + } + + message = string(msg) + execUtils.SendExecMsg(message, consts.ProgressResult, wsMsg) + + select { + case <-ctx.Done(): + logUtils.Debug("<<<<<<< stop websocket test") + goto Label_END_WEBSOCKET_TEST + + default: + } + } + + Label_END_WEBSOCKET_TEST: + }() + + return +} +func disconnectToWebsocketSimple(dpRoom string, execData domain.WebsocketDebugData, wsMsg *websocket.Message) (err error) { + ctx := getExecCtx(dpRoom) + if ctx != nil { + (*ctx).Done() + } + + setExecCtx(dpRoom, nil) - // stop exec - if act == consts.ExecStop { - StopExec(execUuid, wsMsg) + wsConn := getWsConn(dpRoom) + if wsConn == nil { + return + } + + err = wsConn.Close() + if err != nil { + logUtils.Infof("disconnect ws websocket error: %s", err.Error()) + return + } + return +} +func sendMessageSimple(dpRoom string, execData domain.WebsocketDebugData, wsMsg *websocket.Message) (err error) { + wsConn := getWsConn(dpRoom) + if wsConn == nil { return } - // already running - if isRunning && (strings.Include([]string{ - consts.ExecScenario.String(), - consts.ExecPlan.String(), - consts.ExecCase.String(), - }, act.String())) { - execUtils.SendAlreadyRunningMsg(consts.Processor, wsMsg) + err = wsConn.WriteMessage(ws.TextMessage, []byte(execData.Message)) + if err != nil { + log.Println("write ws websocket msg error: %s", err.Error()) return } - // exec task + return +} + +// Extend Websocket mode +func connectToWebsocketExt(dpRoom string, execData domain.WebsocketDebugData, wsMsg *websocket.Message) (err error) { + address := genUrl(execData.Address, execData.Params) + //header := genHeaders(execData.Headers) + + events := neffos.WithTimeout{ + ReadTimeout: 0, + WriteTimeout: 0, + Namespaces: neffos.Namespaces{ + execData.Namespace: neffos.Events{ + neffos.OnAnyEvent: func(c *neffos.NSConn, msg neffos.Message) error { + m := iris.Map{ + "namespace": msg.Namespace, + "room": msg.Room, + "body": string(msg.Body), + } + execUtils.SendExecMsg(m, consts.ProgressResult, wsMsg) + + return nil + }, + }, + }, + } + + client, err := neffos.Dial(context.TODO(), gorilla.DefaultDialer, address, events) + nsConn, err := client.Connect(context.TODO(), execData.Namespace) + nsRoom, err := nsConn.JoinRoom(context.TODO(), execData.Room) + + setNsRoom(dpRoom, nsRoom) + + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) + setExecCtx(dpRoom, &ctx) + + // just wait, message events caught in events go func() { - defer errDefer(wsMsg) - if act == consts.ExecScenario { - req.ScenarioExecReq.TenantId = req.TenantId - RunScenario(&req.ScenarioExecReq, req.LocalVarsCache, wsMsg) - - } else if act == consts.ExecPlan { - req.PlanExecReq.TenantId = req.TenantId - RunPlan(&req.PlanExecReq, req.LocalVarsCache, wsMsg) - - } else if act == consts.ExecCase { - req.CasesExecReq.TenantId = req.TenantId - RunCases(&req.CasesExecReq, req.LocalVarsCache, wsMsg) - - } else if act == consts.ExecMessage { - req.MessageReq.TenantId = req.TenantId - RunMessage(&req.MessageReq, wsMsg) + defer cancel() + + for { + select { + case <-ctx.Done(): + logUtils.Debug("<<<<<<< stop websocket test") + goto Label_END_WEBSOCKET_TEST + + default: + } } - agentExec.ClearExecContext(execUuid) + Label_END_WEBSOCKET_TEST: }() return } +func disconnectToWebsocketExt(dpRoom string, execData domain.WebsocketDebugData, wsMsg *websocket.Message) (err error) { + ctx := getExecCtx(dpRoom) + if ctx != nil { + (*ctx).Done() + } + + setExecCtx(dpRoom, nil) -func getExecUuid(req agentDomain.WsReq) (ret string) { - if req.ScenarioExecReq.ExecUuid != "" { - ret = req.ScenarioExecReq.ExecUuid + nsRoom := getNsRoom(dpRoom) + if nsRoom == nil { + return + } - } else if req.PlanExecReq.ExecUuid != "" { - ret = req.PlanExecReq.ExecUuid + err = nsRoom.NSConn.LeaveAll(context.Background()) + if err != nil { + logUtils.Infof("disconnect ns websocket error: %s", err.Error()) + return + } - } else if req.CasesExecReq.ExecUuid != "" { - ret = req.CasesExecReq.ExecUuid + return +} +func sendMessageExt(dpRoom string, execData domain.WebsocketDebugData, wsMsg *websocket.Message) (err error) { + nsRoom := getNsRoom(dpRoom) + if nsRoom == nil { + return + } + ok := nsRoom.Emit(execData.Event, []byte(execData.Message)) + if !ok { + log.Println("write ns websocket msg failed") + return } return } -func StopExec(execUuid string, wsMsg *websocket.Message) (err error) { - agentExec.SetForceStopExec(execUuid, true) +// helper methods +func setWsConn(dpRoom string, conn *ws.Conn) { + WebsocketContextStore.Store("ws_conn_"+dpRoom, conn) +} +func getWsConn(dpRoom string) (ret *ws.Conn) { + obj, ok := WebsocketContextStore.Load("ws_conn_" + dpRoom) - agentExec.SetIsRunning(execUuid, false) - execUtils.SendCancelMsg(wsMsg) + if ok { + ret = obj.(*ws.Conn) + } return } -func errDefer(wsMsg *websocket.Message) { - err := recover() +func setNsRoom(dpRoom string, obj *neffos.Room) { + WebsocketContextStore.Store("ns_room_"+dpRoom, obj) +} +func getNsRoom(dpRoom string) (ret *neffos.Room) { + obj, ok := WebsocketContextStore.Load("ns_room_" + dpRoom) + + if ok { + ret = obj.(*neffos.Room) + } - if err != nil { - s := string(debug.Stack()) - fmt.Printf("err=%v, stack=%s\n", err, s) + return +} + +func setExecCtx(room string, ctx *context.Context) { + WebsocketContextStore.Store("ctx_"+room, ctx) +} +func getExecCtx(room string) (ret *context.Context) { + obj, ok := WebsocketContextStore.Load("ctx_" + room) + + if ok { + ret = obj.(*context.Context) + } + + return +} + +func genUrl(address string, params *[]domain.Param) (ret string) { + parsedUrl, _ := url.Parse(address) + + baseUrl := &url.URL{ + Scheme: parsedUrl.Scheme, + Host: parsedUrl.Host, + Path: parsedUrl.Path, + } + + queryParams := parsedUrl.Query() + for _, p := range *params { + if p.Name == "" || p.Value == "" { + continue + } + + queryParams.Add(p.Name, p.Value) + } + + baseUrl.RawQuery = queryParams.Encode() + + ret = baseUrl.String() + + return +} - execUtils.SendErrorMsg(err, consts.Processor, wsMsg) +func genHeaders(headers *[]domain.Header) (ret http.Header) { + ret = http.Header{} + + for _, h := range *headers { + if h.Name == "" || h.Value == "" { + continue + } + + if value, ok := ret[h.Name]; !ok || value == nil { + ret[h.Name] = []string{h.Value} + } else { + ret[h.Name] = append(ret[h.Name], h.Value) + } } + + return } diff --git a/internal/agent/service/remote.go b/internal/agent/service/remote.go index 812a9a766..168964604 100644 --- a/internal/agent/service/remote.go +++ b/internal/agent/service/remote.go @@ -3,7 +3,6 @@ package service import ( "encoding/json" "fmt" - v1 "github.com/aaronchen2k/deeptest/cmd/agent/v1/domain" agentExec "github.com/aaronchen2k/deeptest/internal/agent/exec" agentDomain "github.com/aaronchen2k/deeptest/internal/agent/exec/domain" "github.com/aaronchen2k/deeptest/internal/pkg/consts" @@ -16,7 +15,7 @@ import ( ) // for interface invocation in both endpoint and scenario -func GetInterfaceToExec(req v1.InterfaceCall) (ret agentExec.InterfaceExecObj) { +func GetInterfaceToExec(req domain.InterfaceCall) (ret agentExec.InterfaceExecObj) { url := fmt.Sprintf("debugs/interface/loadForExec?currProjectId=%d", req.Data.ProjectId) body, err := json.Marshal(req.Data) if err != nil { @@ -139,7 +138,7 @@ func GetScenarioToExec(req *agentExec.ScenarioExecReq) (ret *agentExec.ScenarioE }, { Name: "environmentId", - Value: _stringUtils.IntToStr(req.EnvironmentId), + Value: _stringUtils.IntToStr(int(req.EnvironmentId)), }, }, Headers: &[]domain.Header{{Name: "tenantId", Value: string(req.TenantId)}}, @@ -206,7 +205,7 @@ func GetScenarioNormalData(req *agentExec.ScenarioExecReq) (ret agentDomain.Repo }, { Name: "environmentId", - Value: _stringUtils.IntToStr(req.EnvironmentId), + Value: _stringUtils.IntToStr(int(req.EnvironmentId)), }, }, Headers: &[]domain.Header{{Name: "tenantId", Value: string(req.TenantId)}}, @@ -300,7 +299,7 @@ func GetPlanToExec(req *agentExec.PlanExecReq) (ret *agentExec.PlanExecObj) { }, { Name: "environmentId", - Value: _stringUtils.IntToStr(req.EnvironmentId), + Value: _stringUtils.IntToStr(int(req.EnvironmentId)), }, }, Headers: &[]domain.Header{{Name: "tenantId", Value: string(req.TenantId)}}, @@ -354,7 +353,7 @@ func GetPlanNormalData(req *agentExec.PlanExecReq) (ret agentDomain.Report, err }, { Name: "environmentId", - Value: _stringUtils.IntToStr(req.EnvironmentId), + Value: _stringUtils.IntToStr(int(req.EnvironmentId)), }, }, Headers: &[]domain.Header{{Name: "tenantId", Value: string(req.TenantId)}}, @@ -389,7 +388,7 @@ func GetPlanNormalData(req *agentExec.PlanExecReq) (ret agentDomain.Report, err return } -func SubmitPlanResult(result agentDomain.PlanExecResult, planId int, serverUrl, token string, tenantId consts.TenantId) ( +func SubmitPlanResult(result agentDomain.PlanExecResult, planId uint, serverUrl, token string, tenantId consts.TenantId) ( report agentDomain.Report, err error) { bodyBytes, _ := json.Marshal(result) req := domain.BaseRequest{ diff --git a/internal/pkg/consts/enum.go b/internal/pkg/consts/enum.go index 32161ef31..1553f695f 100644 --- a/internal/pkg/consts/enum.go +++ b/internal/pkg/consts/enum.go @@ -595,12 +595,24 @@ const ( ExecPlan ExecType = "execPlan" ExecCase ExecType = "execCases" ExecMessage ExecType = "execMessage" + + ExecWebsocketConnect ExecType = "execWebsocketConnect" + ExecWebsocketDisconnect ExecType = "execWebsocketDisconnect" + ExecWebsocketSendMsg ExecType = "execWebsocketSendMsg" + + StartExecGrpc ExecType = "startExecGrpc" + StopExecGrpc ExecType = "stopExecGrpc" ) func (e ExecType) String() string { return string(e) } +var WebscoketAtions = []string{ + ExecWebsocketConnect.String(), + ExecWebsocketDisconnect.String(), + ExecWebsocketSendMsg.String()} + type DataType string const ( @@ -656,7 +668,6 @@ type PlaceholderPrefix string const ( PlaceholderPrefixDatapool PlaceholderPrefix = "_dp" - PlaceholderPrefixFunction PlaceholderPrefix = "_func" ) func (e PlaceholderPrefix) String() string { diff --git a/internal/pkg/domain/condition.go b/internal/pkg/domain/condition.go index 7007f05bb..3e80ea729 100644 --- a/internal/pkg/domain/condition.go +++ b/internal/pkg/domain/condition.go @@ -50,6 +50,7 @@ type CheckpointBase struct { Operator consts.ComparisonOperator `json:"operator"` Value string `json:"value"` + ExpectResult string `json:"expectResult"` ActualResult string `json:"actualResult" gorm:"type:text"` ResultStatus consts.ResultStatus `json:"resultStatus"` diff --git a/internal/pkg/domain/debug.go b/internal/pkg/domain/debug.go index ba2c3e96d..15c9ef369 100644 --- a/internal/pkg/domain/debug.go +++ b/internal/pkg/domain/debug.go @@ -3,8 +3,22 @@ package domain import ( "encoding/json" "github.com/aaronchen2k/deeptest/internal/pkg/consts" + serverConsts "github.com/aaronchen2k/deeptest/internal/server/consts" + "github.com/kataras/iris/v12" ) +type InterfaceCall struct { + ExecUuid string `json:"execUuid"` + ServerUrl string `json:"serverUrl"` + Token string `json:"token"` + TenantId consts.TenantId `json:"tenantId"` + + LocalVarsCache iris.Map `json:"localVarsCache"` + + Data DebugData `json:"data"` + ExecScene ExecScene `json:"execScene"` +} + type DebugInfo struct { DebugInterfaceId uint `json:"debugInterfaceId"` EndpointInterfaceId uint `json:"endpointInterfaceId"` // EndpointInterface without DebugInterface init @@ -82,3 +96,57 @@ type InterfaceExecCondition struct { Desc string `json:"desc"` Raw json.RawMessage `json:"raw"` } + +type WebsocketDebugData struct { + ID uint `json:"id"` + Name string `json:"name"` + ExtMode bool `json:"extMode"` + Namespace string `json:"namespace"` + Room string `json:"room"` + Event string `json:"event"` + Message string `json:"message"` + + Address string `json:"address"` + Service string `json:"service"` + Method string `json:"method"` + + Params *[]Param ` json:"params"` + Headers *[]Header ` json:"headers"` + + UsedBy consts.UsedBy `json:"usedBy"` + Type serverConsts.DiagnoseInterfaceType `json:"type"` + + DiagnoseInterfaceId uint `json:"diagnoseInterfaceId"` + EnvironmentId uint `json:"environmentId"` + ServeId uint `json:"serveId"` + ServerId uint `json:"serverId"` + ProjectId uint `json:"projectId"` +} + +type GrpcDebugData struct { + ID uint `json:"id"` + Address string `json:"address"` + UseTls *bool `json:"useTls"` + RestartConn *bool `json:"restartConn"` + RequestMetadata string `json:"requestMetadata"` + + ProtoSrc string `json:"protoSrc"` + Service string `json:"service"` + Method string `json:"method"` + + Template string `json:"template"` + Message string `json:"message"` + + ProtoName string `json:"protoName"` + ProtoPath string `json:"protoPath"` + // ProtoContent string `json:"protoContent"` + + UsedBy consts.UsedBy `json:"usedBy"` + Type serverConsts.DiagnoseInterfaceType `json:"type"` + + DiagnoseInterfaceId uint `json:"diagnoseInterfaceId"` + EnvironmentId uint `json:"environmentId"` + ServeId uint `json:"serveId"` + ServerId uint `json:"serverId"` + ProjectId uint `json:"projectId"` +} diff --git a/internal/pkg/helper/checkpoint/checkpoint.go b/internal/pkg/helper/checkpoint/checkpoint.go index f6d38bd28..21a728e71 100644 --- a/internal/pkg/helper/checkpoint/checkpoint.go +++ b/internal/pkg/helper/checkpoint/checkpoint.go @@ -8,7 +8,7 @@ import ( _i118Utils "github.com/aaronchen2k/deeptest/pkg/lib/i118" ) -func GenDesc(typ consts.CheckpointType, operator consts.ComparisonOperator, value, expression, +func GenDesc(typ consts.CheckpointType, operator consts.ComparisonOperator, actualResult, expression, extractorVariable string, extractorType consts.ExtractorType, extractorExpression string) (ret string) { nameDesc := "" @@ -16,16 +16,16 @@ func GenDesc(typ consts.CheckpointType, operator consts.ComparisonOperator, valu optName := _i118Utils.Sprintf(opt) if typ == consts.ResponseStatus { nameDesc = _i118Utils.Sprintf("usage") - nameDesc = fmt.Sprintf("状态码%s\"%s\"", optName, value) + nameDesc = fmt.Sprintf("状态码%s\"%s\"", optName, actualResult) } else if typ == consts.ResponseHeader { - nameDesc = fmt.Sprintf("响应头%s%s\"%s\"", expression, optName, value) + nameDesc = fmt.Sprintf("响应头%s%s\"%s\"", expression, optName, actualResult) } else if typ == consts.ResponseBody { - nameDesc = fmt.Sprintf("响应体%s\"%s\"", optName, value) + nameDesc = fmt.Sprintf("响应体%s\"%s\"", optName, actualResult) } else if typ == consts.ExtractorVari { - nameDesc = fmt.Sprintf("提取变量%s%s\"%s\"", extractorVariable, optName, value) + nameDesc = fmt.Sprintf("提取变量%s%s\"%s\"", extractorVariable, optName, actualResult) } else if typ == consts.Extractor { extractorDesc := extractorHelper.GenDescForCheckpoint(extractorType, extractorExpression) - nameDesc = fmt.Sprintf("提取%s%s\"%s\"", extractorDesc, optName, value) + nameDesc = fmt.Sprintf("提取%s%s\"%s\"", extractorDesc, optName, actualResult) } else if typ == consts.Judgement { nameDesc = fmt.Sprintf("表达式\"%s\"", expression) } @@ -36,12 +36,16 @@ func GenDesc(typ consts.CheckpointType, operator consts.ComparisonOperator, valu } func GenResultMsg(po *domain.CheckpointBase) { - desc := GenDesc(po.Type, po.Operator, po.Value, po.Expression, po.ExtractorVariable, po.ExtractorType, po.ExtractorExpression) + result := po.ActualResult + if po.ResultStatus != consts.Pass || po.Operator == consts.NotEqual { + result = po.ExpectResult + } + desc := GenDesc(po.Type, po.Operator, result, po.Expression, po.ExtractorVariable, po.ExtractorType, po.ExtractorExpression) po.ResultMsg = fmt.Sprintf("%s", desc) if po.ResultStatus != consts.Pass { - po.ResultMsg += fmt.Sprintf(",实际结果\"%s\"", po.ActualResult) + po.ResultMsg += fmt.Sprintf(",实际结果\"%s\"。", po.ActualResult) } return diff --git a/internal/pkg/helper/grpc/core/connection.go b/internal/pkg/helper/grpc/core/connection.go new file mode 100644 index 000000000..db93fe322 --- /dev/null +++ b/internal/pkg/helper/grpc/core/connection.go @@ -0,0 +1,145 @@ +package grpcCore + +import ( + "log" + "sync" + "time" +) + +// ConnStore - connection store instance +type ConnStore struct { + sync.RWMutex + + conn map[string]*connection + + // flag for auto garbage collector(close and cleanup expired connection) + activeGC bool + // controls gc intervals + gcTicker *time.Ticker + // gc stop signal + done chan struct{} +} + +type connection struct { + // hold connection object + resource *Resource + // keep connect + keepAlive bool + // will automatically close connection + expired time.Time +} + +// NewConnectionStore - constructor connection store +func NewConnectionStore() *ConnStore { + return &ConnStore{ + conn: make(map[string]*connection), + } +} + +// StartGC - start gc ticker +func (c *ConnStore) StartGC(interval time.Duration) { + if interval <= 0 { + return + } + + c.activeGC = true + + ticker := time.NewTicker(interval) + done := make(chan struct{}) + + c.Lock() + c.gcTicker = ticker + c.done = done + c.Unlock() + + go func() { + for { + select { + case <-ticker.C: + c.Lock() + for key := range c.conn { + if c.isExpired(key) { + c.delete(key) + log.Printf("Connection %s expired and closed\n", key) + } + } + c.Unlock() + case <-done: + return + } + } + }() +} + +// StopGC stops sweeping goroutine. +func (c *ConnStore) StopGC() { + if c.activeGC { + c.Lock() + c.gcTicker.Stop() + c.gcTicker = nil + close(c.done) + c.done = nil + c.Unlock() + } +} + +func (c *ConnStore) extend(key string, ttl time.Duration) { + conn := c.conn[key] + if conn != nil { + conn.extendConnection(ttl) + } +} + +func (c *ConnStore) isExpired(key string) bool { + conn := c.conn[key] + if conn == nil { + return false + } + return !conn.keepAlive && conn.expired.Before(time.Now()) +} + +func (c *ConnStore) getAllConn() map[string]*connection { + return c.conn +} + +func (c *ConnStore) delete(key string) { + conn := c.conn[key] + if conn != nil { + conn.close() + delete(c.conn, key) + } +} + +func (c *ConnStore) addConnection(host string, res *Resource, ttl ...time.Duration) { + conn := &connection{ + resource: res, + keepAlive: true, + } + + if len(ttl) > 0 { + conn.keepAlive = false + conn.expired = time.Now().Add(ttl[0]) + } + + c.Lock() + c.conn[host] = conn + c.Unlock() +} + +func (c *ConnStore) getConnection(host string) (res *Resource, found bool) { + conn, ok := c.conn[host] + if ok && conn.resource != nil { + found = true + res = conn.resource + } + return +} + +func (conn *connection) extendConnection(ttl time.Duration) { + conn.keepAlive = false + conn.expired = time.Now().Add(ttl) +} + +func (conn *connection) close() { + conn.resource.Close() +} diff --git a/internal/pkg/helper/grpc/core/grpcox.go b/internal/pkg/helper/grpc/core/grpcox.go new file mode 100644 index 000000000..6122f10f5 --- /dev/null +++ b/internal/pkg/helper/grpc/core/grpcox.go @@ -0,0 +1,179 @@ +package grpcCore + +import ( + "context" + "reflect" + "time" + + "github.com/fullstorydev/grpcurl" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/keepalive" +) + +// GrpCox - main object +type GrpCox struct { + KeepAlive float64 + + activeConn *ConnStore + maxLifeConn time.Duration + + // TODO : utilize below args + headers []string + reflectHeaders []string + authority string + insecure bool + cacert string + cert string + key string + serverName string + isUnixSocket func() bool +} + +// Proto define protofile uploaded from client +// will be used to be persisted to disk and indicator +// whether connections should reflect from server or local proto +type Proto struct { + Name string + Content []byte +} + +// InitGrpCox constructor +func InitGrpCox() *GrpCox { + maxLife, tick := 10, 3 + + connStore := NewConnectionStore() + inst := &GrpCox{ + activeConn: connStore, + } + + if maxLife > 0 && tick > 0 { + inst.maxLifeConn = time.Duration(maxLife) * time.Minute + connStore.StartGC(time.Duration(tick) * time.Second) + } + + return inst +} + +// GetResource - open resource to targeted grpc server +func (g *GrpCox) GetResource(ctx context.Context, target string, plainText, isRestartConn bool) (*Resource, error) { + conn, ok := g.activeConn.getConnection(target) + if ok { + if !isRestartConn && conn.isValid() { + return conn, nil + } + g.CloseActiveConns(target) + } + + var err error + r := new(Resource) + h := append(g.headers, g.reflectHeaders...) + r.md = grpcurl.MetadataFromHeaders(h) + r.clientConn, err = g.dial(ctx, target, plainText) + if err != nil { + return nil, err + } + + // what is r.Headers used for? + r.headers = h + + g.activeConn.addConnection(target, r, g.maxLifeConn) + return r, nil +} + +// GetResourceWithProto - open resource to targeted grpc server using given protofile +func (g *GrpCox) GetResourceWithProto(ctx context.Context, target string, plainText, isRestartConn bool, protos []Proto) (*Resource, error) { + r, err := g.GetResource(ctx, target, plainText, isRestartConn) + if err != nil { + return nil, err + } + + // if given protofile is equal to current, skip adding protos as it's already persisted on disk + if reflect.DeepEqual(r.protos, protos) { + return r, nil + } + + // add protos property to resource and persist it on disk + err = r.AddProtos(protos) + return r, err +} + +// GetActiveConns - get all saved active connection +func (g *GrpCox) ListActiveConn(host string) (result []string, err error) { + active := g.activeConn.getAllConn() + result = make([]string, len(active)) + + i := 0 + for k := range active { + result[i] = k + i++ + } + + return +} + +// CloseActiveConns - close conn by host or all +func (g *GrpCox) CloseActiveConns(host string) error { + if host == "all" { + for k := range g.activeConn.getAllConn() { + g.activeConn.delete(k) + } + return nil + } + + g.activeConn.delete(host) + return nil +} + +// Extend extend connection based on setting max life +func (g *GrpCox) Extend(host string) { + g.activeConn.extend(host, g.maxLifeConn) +} + +// SetReflectHeaders sets grpcox reflection headers +func (g *GrpCox) SetReflectHeaders(headers ...string) { + g.reflectHeaders = headers +} + +func (g *GrpCox) dial(ctx context.Context, target string, plainText bool) (*grpc.ClientConn, error) { + dialTime := 10 * time.Second + ctx, cancel := context.WithTimeout(ctx, dialTime) + defer cancel() + var opts []grpc.DialOption + + // keep alive + if g.KeepAlive > 0 { + timeout := time.Duration(g.KeepAlive * float64(time.Second)) + opts = append(opts, grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: timeout, + Timeout: timeout, + })) + } + + if g.authority != "" { + opts = append(opts, grpc.WithAuthority(g.authority)) + } + + var creds credentials.TransportCredentials + if !plainText { + var err error + creds, err = grpcurl.ClientTransportCredentials(g.insecure, g.cacert, g.cert, g.key) + if err != nil { + return nil, err + } + if g.serverName != "" { + if err := creds.OverrideServerName(g.serverName); err != nil { + return nil, err + } + } + } + network := "tcp" + if g.isUnixSocket != nil && g.isUnixSocket() { + network = "unix" + } + cc, err := grpcurl.BlockingDial(ctx, network, target, creds, opts...) + if err != nil { + return nil, err + } + return cc, nil +} diff --git a/internal/pkg/helper/grpc/core/resource.go b/internal/pkg/helper/grpc/core/resource.go new file mode 100644 index 000000000..10c07cd99 --- /dev/null +++ b/internal/pkg/helper/grpc/core/resource.go @@ -0,0 +1,368 @@ +package grpcCore + +import ( + "bytes" + "context" + "fmt" + "github.com/aaronchen2k/deeptest/internal/pkg/consts" + "io" + "log" + "os" + "path/filepath" + "regexp" + "strings" + "sync" + "time" + + "github.com/fullstorydev/grpcurl" + "github.com/jhump/protoreflect/desc" + "github.com/jhump/protoreflect/grpcreflect" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + reflectpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha" +) + +var ( + BasePath = "" +) + +// Resource - hold 3 main function (List, Describe, and Invoke) +type Resource struct { + clientConn *grpc.ClientConn + descSource grpcurl.DescriptorSource + refClient *grpcreflect.Client + protos []Proto + protosets []Proto + + headers []string + md metadata.MD +} + +// openDescriptor - use it to reflect server descriptor +func (r *Resource) openDescriptor() error { + ctx := context.Background() + refCtx := metadata.NewOutgoingContext(ctx, r.md) + r.refClient = grpcreflect.NewClient(refCtx, reflectpb.NewServerReflectionClient(r.clientConn)) + + // if no protos available use server reflection + if r.protos == nil && r.protosets == nil { + r.descSource = grpcurl.DescriptorSourceFromServer(ctx, r.refClient) + return nil + } + + protoDir := filepath.Join(consts.WorkDir, r.clientConn.Target()) + + var err error + if len(r.protosets) > 0 { + // make list of protos name to be used as descriptor + protos := make([]string, 0, len(r.protosets)) + for _, proto := range r.protosets { + protos = append(protos, filepath.Join(protoDir, proto.Name)) + } + + r.descSource, err = grpcurl.DescriptorSourceFromProtoSets(protos...) + } else { + // make list of protos name to be used as descriptor + protos := make([]string, 0, len(r.protos)) + for _, proto := range r.protos { + protos = append(protos, proto.Name) + } + + r.descSource, err = grpcurl.DescriptorSourceFromProtoFiles([]string{protoDir}, protos...) + } + return err +} + +// closeDescriptor - please ensure to always close after open in the same flow +func (r *Resource) closeDescriptor() { + done := make(chan int) + go func() { + if r.refClient != nil { + r.refClient.Reset() + } + done <- 1 + }() + + select { + case <-done: + return + case <-time.After(3 * time.Second): + log.Printf("Reflection %s failed to close\n", r.clientConn.Target()) + return + } +} + +// List - To list all services exposed by a server +// symbol can be "" to list all available services +// symbol also can be service name to list all available method +func (r *Resource) List(symbol string) ([]string, error) { + err := r.openDescriptor() + if err != nil { + return nil, err + } + defer r.closeDescriptor() + + var result []string + if symbol == "" { + svcs, err := grpcurl.ListServices(r.descSource) + if err != nil { + return result, err + } + if len(svcs) == 0 { + return result, fmt.Errorf("No Services") + } + + for _, svc := range svcs { + result = append(result, fmt.Sprintf("%s\n", svc)) + } + } else { + methods, err := grpcurl.ListMethods(r.descSource, symbol) + if err != nil { + return result, err + } + if len(methods) == 0 { + return result, fmt.Errorf("No Function") // probably unlikely + } + + for _, m := range methods { + result = append(result, fmt.Sprintf("%s\n", m)) + } + } + + return result, nil +} + +// Describe - The "describe" verb will print the type of any symbol that the server knows about +// or that is found in a given protoset file. +// It also prints a description of that symbol, in the form of snippets of proto source. +// It won't necessarily be the original source that defined the element, but it will be equivalent. +func (r *Resource) Describe(symbol string, typ string) (string, string, bool, bool, error) { + err := r.openDescriptor() + if err != nil { + return "", "", false, false, err + } + defer r.closeDescriptor() + + var result, template string + + var symbols []string + if symbol != "" { + symbols = []string{symbol} + } else { + // if no symbol given, describe all exposed services + svcs, err := r.descSource.ListServices() + if err != nil { + return "", "", false, false, err + } + if len(svcs) == 0 { + log.Println("Server returned an empty list of exposed services") + } + symbols = svcs + } + + isClientStreaming := false + isServerStreaming := false + + for _, s := range symbols { + if s[0] == '.' { + s = s[1:] + } + + if typ == "param" { + s = strings.TrimPrefix(s, "stream .") + } + + dsc, err := r.descSource.FindSymbol(s) + if err != nil { + return "", "", false, false, err + } + + txt, err := grpcurl.GetDescriptorText(dsc, r.descSource) + if err != nil { + return "", "", false, false, err + } + result = txt + + if dsc, ok := dsc.(*desc.MethodDescriptor); ok { + isClientStreaming = dsc.IsClientStreaming() + isServerStreaming = dsc.IsServerStreaming() + log.Println(isClientStreaming, isServerStreaming) + } + + if dsc, ok := dsc.(*desc.MessageDescriptor); ok { + // for messages, also show a template in JSON, to make it easier to + // create a request to invoke an RPC + tmpl := grpcurl.MakeTemplate(dsc) + _, formatter, err := grpcurl.RequestParserAndFormatterFor(grpcurl.Format("json"), r.descSource, true, false, nil) + if err != nil { + return "", "", false, false, err + } + str, err := formatter(tmpl) + if err != nil { + return "", "", false, false, err + } + template = str + } + } + + return result, template, isClientStreaming, isServerStreaming, nil +} + +// Invoke - invoking gRPC function +func (r *Resource) Invoke(ctx context.Context, metadata []string, symbol string, in io.Reader, + isClientStreaming, isServerStreaming bool) (string, time.Duration, error) { + + err := r.openDescriptor() + if err != nil { + return "", 0, err + } + defer r.closeDescriptor() + + var resultBuffer bytes.Buffer + rf, formatter, err := grpcurl.RequestParserAndFormatterFor( + "json", r.descSource, false, true, in) + if err != nil { + return "", 0, err + } + + handler := grpcurl.NewDefaultEventHandler(&resultBuffer, r.descSource, formatter, false) + + var headers []string + if len(metadata) != 0 { + headers = metadata + } + + start := time.Now() + + err = grpcurl.InvokeRPC(ctx, r.descSource, r.clientConn, symbol, headers, handler, rf.Next) + + end := time.Now().Sub(start) / time.Millisecond + if err != nil { + return "", end, err + } + + if handler.Status.Code() != codes.OK { + return "", end, fmt.Errorf(handler.Status.Message()) + } + + return resultBuffer.String(), end, nil +} + +// Close - to close all resources that was opened before +func (r *Resource) Close() { + var wg sync.WaitGroup + + target := r.clientConn.Target() + + wg.Add(1) + go func() { + defer wg.Done() + if r.clientConn != nil { + r.clientConn.Close() + r.clientConn = nil + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + protoDir := filepath.Join(consts.WorkDir, target) + err := os.RemoveAll(protoDir) + if err != nil { + log.Printf("error removing proto dir from tmp: %s", err.Error()) + } + }() + + c := make(chan struct{}) + go func() { + defer close(c) + wg.Wait() + }() + + select { + case <-c: + return + case <-time.After(3 * time.Second): + log.Printf("Connection %s failed to close\n", r.clientConn.Target()) + return + } +} + +func (r *Resource) isValid() bool { + return r.refClient != nil && r.clientConn != nil +} + +func (r *Resource) exit(code int) { + // to force reset before os exit + r.Close() + os.Exit(code) +} + +// AddProtos to resource properties and harddisk +// added protos will be persisted in `basepath + connection target` +// i.e. connection target == 127.0.0.1:8888 +// proto files will be persisted in ~/127.0.0.1:8888 +// if the directory is already there, remove it first +func (r *Resource) AddProtos(protos []Proto) error { + protoDir := filepath.Join(consts.WorkDir, r.clientConn.Target()) + err := os.MkdirAll(protoDir, 0777) + if os.IsExist(err) { + err = os.RemoveAll(protoDir) + if err != nil { + return err + } + err = os.MkdirAll(protoDir, 0777) + if err != nil { + return err + } + } else if err != nil { + return err + } + + var protoSlice, protosetSlice []Proto + for _, proto := range protos { + var err error + if strings.HasSuffix(proto.Name, ".protoset") { + protosetSlice = append(protosetSlice, proto) + err = os.WriteFile(filepath.Join(protoDir, proto.Name), + proto.Content, + 0777) + } else { + protoSlice = append(protoSlice, proto) + err = os.WriteFile(filepath.Join(protoDir, proto.Name), + prepareImport(proto.Content), + 0777) + } + if err != nil { + return err + } + } + + r.protos = protoSlice + r.protosets = protosetSlice + return nil +} + +// prepareImport transforming proto import into local path +// with exception to google proto import as it won't cause any problem +func prepareImport(proto []byte) []byte { + const pattern = `import ".+` + result := string(proto) + + re := regexp.MustCompile(pattern) + matchs := re.FindAllString(result, -1) + for _, match := range matchs { + if strings.Contains(match, "\"google/") { + continue + } + name := strings.Split(match, "/") + if len(name) < 2 { + continue + } + importString := `import "` + name[len(name)-1] + result = strings.Replace(result, match, importString, -1) + } + + return []byte(result) +} diff --git a/internal/pkg/helper/grpc/domain/domain.go b/internal/pkg/helper/grpc/domain/domain.go new file mode 100644 index 000000000..0dc47e230 --- /dev/null +++ b/internal/pkg/helper/grpc/domain/domain.go @@ -0,0 +1,13 @@ +package grpcDomain + +type Desc struct { + Schema string `json:"schema"` + Template string `json:"template"` + IsClientStreaming bool `json:"isClientStreaming"` + IsServerStreaming bool `json:"isServerStreaming"` +} + +type InvRes struct { + Time string `json:"timer"` + Result string `json:"result"` +} diff --git a/internal/pkg/helper/grpc/index.go b/internal/pkg/helper/grpc/index.go new file mode 100644 index 000000000..93615ad62 --- /dev/null +++ b/internal/pkg/helper/grpc/index.go @@ -0,0 +1,231 @@ +package grpcHelper + +import ( + "context" + "errors" + "fmt" + serverDomain "github.com/aaronchen2k/deeptest/cmd/server/v1/domain" + grpcCore "github.com/aaronchen2k/deeptest/internal/pkg/helper/grpc/core" + grpcDomain "github.com/aaronchen2k/deeptest/internal/pkg/helper/grpc/domain" + logUtils "github.com/aaronchen2k/deeptest/pkg/lib/log" + "io" + "os" + "regexp" + "strings" + "time" +) + +var ( + reGetFuncArg = regexp.MustCompile("\\( (.*) \\) returns") +) + +// Handler hold all handler methods +type Handler struct { + G *grpcCore.GrpCox +} + +// InitHandler Constructor +func InitHandler() *Handler { + return &Handler{ + G: grpcCore.InitGrpCox(), + } +} + +func (h *Handler) List(req serverDomain.GrpcReq) (services, methods []string, err error) { + address := req.Address + service := req.Service + method := req.Method + useTls := req.UseTls + isRestartConn := req.RestartConn + + metaData := []string{} + for _, item := range req.MetaData { + metaData = append(metaData, fmt.Sprintf("%s:%s", item.Key, item.Value)) + } + + h.G.SetReflectHeaders(metaData...) + + res, err := h.G.GetResource(context.Background(), address, !useTls, isRestartConn) + if err != nil { + return + } + + if service == "" { // parse, load services + services, err = res.List("") + if err != nil { + logUtils.Infof("grpc List error: %s", err.Error()) + return + } + + } else if service != "" && method == "" { // select service, load methods + methods, err = res.List(service) + if err != nil { + logUtils.Infof("grpc List error: %s", err.Error()) + return + } + + } else if service != "" && method != "" { // reload, load both services and methods + services, err = res.List("") + if err != nil { + logUtils.Infof("grpc List error: %s", err.Error()) + return + } + + methods, err = res.List(service) + if err != nil { + logUtils.Infof("grpc List error: %s", err.Error()) + return + } + } + + h.G.Extend(address) + + return +} + +// ListWithProto handling client request for service list with proto +func (h *Handler) ListWithProto(req serverDomain.GrpcReq) (services, methods []string, err error) { + address := req.Address + service := req.Service + method := req.Method + useTls := req.UseTls + isRestartConn := req.RestartConn + protoName := req.ProtoName + protoPath := req.ProtoPath + + // convert uploaded file to list of Proto struct + protos := []grpcCore.Proto{} + + fileData, err := os.Open(protoPath) + if err != nil { + return + } + defer fileData.Close() + + content, err1 := io.ReadAll(fileData) + if err1 != nil { + err = err1 + return + } + + protos = append(protos, grpcCore.Proto{ + Name: protoName, + Content: content, + }) + + res, err := h.G.GetResourceWithProto(context.Background(), address, !useTls, isRestartConn, protos) + if err != nil { + return + } + + if service == "" { // parse, load services + services, err = res.List("") + if err != nil { + logUtils.Infof("grpc List error: %s", err.Error()) + return + } + + } else if service != "" && method == "" { // select service, load methods + methods, err = res.List(service) + if err != nil { + logUtils.Infof("grpc List error: %s", err.Error()) + return + } + + } else if service != "" && method != "" { // reload, load both services and methods + services, err = res.List("") + if err != nil { + logUtils.Infof("grpc List error: %s", err.Error()) + return + } + + methods, err = res.List(service) + if err != nil { + logUtils.Infof("grpc List error: %s", err.Error()) + return + } + } + + h.G.Extend(address) + + return +} + +func (h *Handler) DescribeFunc(req serverDomain.GrpcReq) (ret grpcDomain.Desc, err error) { + address := req.Address + funcName := req.Method + useTls := req.UseTls + isRestartConn := req.RestartConn + + res, err := h.G.GetResource(context.Background(), address, !useTls, isRestartConn) + if err != nil { + return + } + + // get param + result, _, isClientStreaming, isServerStreaming, err := res.Describe(funcName, "func") + if err != nil { + return + } + matchedParams := reGetFuncArg.FindStringSubmatch(result) + if len(matchedParams) < 2 { + errors.New("Invalid Func Type") + return + } + + // describe func + result, template, _, _, err := res.Describe(matchedParams[1], "param") + if err != nil { + return + } + + h.G.Extend(address) + + ret = grpcDomain.Desc{ + Schema: result, + Template: template, + IsClientStreaming: isClientStreaming, + IsServerStreaming: isServerStreaming, + } + + return +} + +func (h *Handler) InvokeFunc(req serverDomain.GrpcReq) (ret grpcDomain.InvRes, err error) { + address := req.Address + funcName := req.Method + useTls := req.UseTls + isRestartConn := req.RestartConn + message := req.Message + isClientStreaming := req.IsClientStreaming + isServerStreaming := req.IsServerStreaming + + res, err := h.G.GetResource(context.Background(), address, !useTls, isRestartConn) + if err != nil { + return + } + + metaData := []string{} + for _, item := range req.MetaData { + metaData = append(metaData, fmt.Sprintf("%s:%s", item.Key, item.Value)) + } + + // get param + ctx, _ := context.WithTimeout(context.Background(), time.Duration(600)*time.Second) + body := strings.NewReader(message) + + isClientStreaming = true + result, timer, err := res.Invoke(ctx, metaData, funcName, body, isClientStreaming, isServerStreaming) + if err != nil { + return + } + + h.G.Extend(address) + + ret = grpcDomain.InvRes{ + Time: timer.String(), + Result: result, + } + + return +} diff --git a/internal/pkg/helper/jslib/agent-lib.go b/internal/pkg/helper/jslib/agent-lib.go index 99cb4ea32..89406762f 100644 --- a/internal/pkg/helper/jslib/agent-lib.go +++ b/internal/pkg/helper/jslib/agent-lib.go @@ -53,16 +53,15 @@ func LoadChaiJslibs(runtime *goja.Runtime) { runtime.Set("expect", chaiInst.Exports().Named["expect"]) } -func RefreshRemoteAgentJslibs(runtime *goja.Runtime, require *require.RequireModule, tenantId consts.TenantId, projectId uint, serverUrl, token string) { +func RefreshRemoteAgentJslibs(runtime *goja.Runtime, require *require.RequireModule, vuNo int, tenantId consts.TenantId, projectId uint, serverUrl, token string) { libs := getJslibsFromServer(tenantId, projectId, serverUrl, token) for _, lib := range libs { - id := lib.Id if tenantId == "" { tenantId = "NA" } - tmpFile := fmt.Sprintf("%d-%s-%d.js", id, tenantId, lib.UpdatedAt.Unix()) + tmpFile := fmt.Sprintf("%d-%d-%s-%d.js", lib.Id, vuNo, tenantId, lib.UpdatedAt.Unix()) tmpPath := fmt.Sprintf("%s/%s", consts.TmpDirRelativeAgent, tmpFile) tmpContent := lib.Script fileUtils.WriteFileIfNotExist(tmpPath, tmpContent) @@ -76,29 +75,44 @@ func RefreshRemoteAgentJslibs(runtime *goja.Runtime, require *require.RequireMod if err != nil { logUtils.Errorf(err.Error()) } + /* + x := module.Export() + fmt.Println(x) - //SetAgentCache(projectId, id, lib.UpdatedAt) - logUtils.Infof("更新第三方库,projectId:%v,id:%v,lib.Name:%v", projectId, id, lib.Name) - //} - } -} + for key, item := range x.(map[string]interface{}) { + a := item.(func(goja.FunctionCall) goja.Value) + println(a) -func GetAgentCache(tenantId consts.TenantId, projectId uint, id uint) (val time.Time, ok bool) { - mp := GetAgentLoadedLibs(tenantId, projectId) - val, ok = (*mp)[id] + fun := runtime.Get(key).Export() + t := reflect.TypeOf(fun) - return -} + // 输出函数参数的数量和它们的类型 + for i := 0; i < t.NumIn(); i++ { + fmt.Println("Input", i, ":", t.In(i)) + } + + // 输出函数返回值的数量和它们的类型 + for i := 0; i < t.NumOut(); i++ { + fmt.Println("Output", i, ":", t.Out(i)) + } + } -func SetAgentCache(tenantId consts.TenantId, projectId uint, id uint, val time.Time) { - mp := GetAgentLoadedLibs(tenantId, projectId) - (*mp)[id] = val + + x := runtime.Get("exports").Export() + + obj := runtime.ToValue(x) + y := obj.Export() + fmt.Println(y) + */ + + logUtils.Infof("更新第三方库,projectId:%v,id:%v,lib.Name:%v", projectId, lib.Id, lib.Name) + } } func getJslibsFromServer(tenantId consts.TenantId, projectId uint, serverUrl, token string) (libs []Jslib) { url := fmt.Sprintf("snippets/getJslibsForAgent?projectId=%d", projectId) - loadedLibs := &map[uint]time.Time{} // GetAgentLoadedLibs(projectId) + loadedLibs := &map[uint]time.Time{} // get all if loaded libs is empty body, err := json.Marshal(loadedLibs) diff --git a/internal/pkg/helper/jslib/caches.go b/internal/pkg/helper/jslib/caches.go index 90cd50c71..1cccdaa4f 100644 --- a/internal/pkg/helper/jslib/caches.go +++ b/internal/pkg/helper/jslib/caches.go @@ -5,7 +5,6 @@ import ( "github.com/aaronchen2k/deeptest/internal/pkg/consts" "github.com/aaronchen2k/deeptest/internal/server/core/dao" fileUtils "github.com/aaronchen2k/deeptest/pkg/lib/file" - "github.com/snowlyg/helper/dir" "path/filepath" "sync" ) @@ -54,10 +53,12 @@ func InitJslibCache(tenantId consts.TenantId) (err error) { Find(&pos).Error for _, po := range pos { - pth := filepath.Join(dir.GetCurrentAbPath(), po.ScriptFile) + pth := filepath.Join(consts.WorkDir, po.ScriptFile) content := fileUtils.ReadFile(pth) to := Jslib{ + Id: po.ID, + ProjectId: po.ProjectId, Name: po.Name, Script: content, UpdatedAt: *po.UpdatedAt, diff --git a/internal/pkg/helper/jslib/context-project.go b/internal/pkg/helper/jslib/context-project.go index 812457747..d0a387912 100644 --- a/internal/pkg/helper/jslib/context-project.go +++ b/internal/pkg/helper/jslib/context-project.go @@ -1,94 +1,86 @@ package jslibHelper -import ( - "fmt" - "github.com/aaronchen2k/deeptest/internal/pkg/consts" - "github.com/dop251/goja" - "github.com/dop251/goja_nodejs/require" - "sync" - "time" -) - -var ( - ProjectContextStore sync.Map -) - -func InitProjectContext(tenantId consts.TenantId, projectId uint) { - val := ProjectContext{ - GojaRuntime: nil, - GojaRequire: nil, - AgentLoadedLibs: nil, - } - key := getKey(tenantId, projectId) - ProjectContextStore.Store(key, &val) -} - -func ClearProjectContext(tenantId consts.TenantId, projectId uint) { - key := getKey(tenantId, projectId) - ProjectContextStore.Store(key, nil) -} - -func GetProjectContext(tenantId consts.TenantId, projectId uint) (val *ProjectContext) { - key := getKey(tenantId, projectId) - inf, ok := ProjectContextStore.Load(key) - if !ok { - InitProjectContext(tenantId, projectId) - } - - inf, _ = ProjectContextStore.Load(key) - val = inf.(*ProjectContext) - - return -} - -func InitGojaRuntime(tenantId consts.TenantId, projectId uint) (execRuntime *goja.Runtime, execRequire *require.RequireModule) { - projectContext := GetProjectContext(tenantId, projectId) - - projectContext.GojaRuntime = goja.New() - projectContext.GojaRuntime.SetFieldNameMapper(goja.TagFieldNameMapper("json", true)) - registry := new(require.Registry) // registry 能夠被多个goja.Runtime共用 - execRequire = registry.Enable(projectContext.GojaRuntime) - - projectContext.GojaRequire = execRequire - - execRuntime = projectContext.GojaRuntime - - return -} - -func GetGojaRuntime(tenantId consts.TenantId, projectId uint) (execRuntime *goja.Runtime, execRequire *require.RequireModule) { - projectContext := GetProjectContext(tenantId, projectId) - execRuntime = projectContext.GojaRuntime - execRequire = projectContext.GojaRequire - - return -} - -func GetAgentLoadedLibs(tenantId consts.TenantId, projectId uint) (agentLoadedLibs *map[uint]time.Time) { - projectContext := GetProjectContext(tenantId, projectId) - agentLoadedLibs = projectContext.AgentLoadedLibs - - if agentLoadedLibs == nil { - agentLoadedLibs = &map[uint]time.Time{} - SetAgentLoadedLibs(tenantId, projectId, agentLoadedLibs) - } - - return -} -func SetAgentLoadedLibs(tenantId consts.TenantId, projectId uint, agentLoadedLibs *map[uint]time.Time) { - projectContext := GetProjectContext(tenantId, projectId) - projectContext.AgentLoadedLibs = agentLoadedLibs - - return -} - -type ProjectContext struct { - // for goja js engine - GojaRuntime *goja.Runtime - GojaRequire *require.RequireModule - AgentLoadedLibs *map[uint]time.Time -} - -func getKey(tenantId consts.TenantId, projectId uint) string { - return fmt.Sprintf("%v-%d", tenantId, projectId) -} +// +//var ( +// ProjectContextStore sync.Map +//) +// +//func InitProjectContext(tenantId consts.TenantId, projectId uint) { +// val := ProjectContext{ +// GojaRuntime: nil, +// GojaRequire: nil, +// AgentLoadedLibs: nil, +// } +// key := getKey(tenantId, projectId) +// ProjectContextStore.Store(key, &val) +//} +// +//func ClearProjectContext(tenantId consts.TenantId, projectId uint) { +// key := getKey(tenantId, projectId) +// ProjectContextStore.Store(key, nil) +//} +// +//func GetProjectContext(tenantId consts.TenantId, projectId uint) (val *ProjectContext) { +// key := getKey(tenantId, projectId) +// inf, ok := ProjectContextStore.Load(key) +// if !ok { +// InitProjectContext(tenantId, projectId) +// } +// +// inf, _ = ProjectContextStore.Load(key) +// val = inf.(*ProjectContext) +// +// return +//} +// +//func InitProjectGojaRuntime(tenantId consts.TenantId, projectId uint) (execRuntime *goja.Runtime, execRequire *require.RequireModule) { +// projectContext := GetProjectContext(tenantId, projectId) +// +// projectContext.GojaRuntime = goja.New() +// projectContext.GojaRuntime.SetFieldNameMapper(goja.TagFieldNameMapper("json", true)) +// registry := new(require.Registry) // registry 能夠被多个goja.Runtime共用 +// execRequire = registry.Enable(projectContext.GojaRuntime) +// +// projectContext.GojaRequire = execRequire +// +// execRuntime = projectContext.GojaRuntime +// +// return +//} +// +//func GetProjectGojaRuntime(tenantId consts.TenantId, projectId uint) (execRuntime *goja.Runtime, execRequire *require.RequireModule) { +// projectContext := GetProjectContext(tenantId, projectId) +// execRuntime = projectContext.GojaRuntime +// execRequire = projectContext.GojaRequire +// +// return +//} +// +//func GetAgentLoadedLibs(tenantId consts.TenantId, projectId uint) (agentLoadedLibs *map[uint]time.Time) { +// projectContext := GetProjectContext(tenantId, projectId) +// agentLoadedLibs = projectContext.AgentLoadedLibs +// +// if agentLoadedLibs == nil { +// agentLoadedLibs = &map[uint]time.Time{} +// SetAgentLoadedLibs(tenantId, projectId, agentLoadedLibs) +// } +// +// return +//} +//func SetAgentLoadedLibs(tenantId consts.TenantId, projectId uint, agentLoadedLibs *map[uint]time.Time) { +// projectContext := GetProjectContext(tenantId, projectId) +// projectContext.AgentLoadedLibs = agentLoadedLibs +// +// return +//} +// +//type ProjectContext struct { +// // for goja js engine +// GojaRuntime *goja.Runtime +// GojaRequire *require.RequireModule +// AgentLoadedLibs *map[uint]time.Time +//} +// +//func getKey(tenantId consts.TenantId, projectId uint) string { +// return fmt.Sprintf("%v-%d", tenantId, projectId) +//} diff --git a/internal/pkg/helper/jslib/domain.go b/internal/pkg/helper/jslib/domain.go index d34299f17..71bc91c61 100644 --- a/internal/pkg/helper/jslib/domain.go +++ b/internal/pkg/helper/jslib/domain.go @@ -2,14 +2,20 @@ package jslibHelper import "time" +type JsFunc struct { + Name string `json:"name"` + Args string `json:"args"` +} + type Jslib struct { Id uint `json:"id"` - Name string `json:"name"` - Desc string `json:"desc"` - Script string `json:"script" gorm:"type:text"` - + Name string `json:"name"` + Desc string `json:"desc"` + Script string `json:"script" gorm:"type:text"` + ProjectId uint `json:"projectId"` UpdatedAt time.Time `json:"updatedAt"` + Functions []JsFunc } type SysJslib struct { @@ -26,6 +32,8 @@ type SysJslib struct { CreateUser string `json:"createUser"` UpdateUser string `json:"updateUser"` + + ProjectId uint `json:"projectId"` } func (SysJslib) TableName() string { diff --git a/internal/pkg/helper/jslib/server-lib.go b/internal/pkg/helper/jslib/server-lib.go index 976057022..b37f80e18 100644 --- a/internal/pkg/helper/jslib/server-lib.go +++ b/internal/pkg/helper/jslib/server-lib.go @@ -9,21 +9,21 @@ import ( "github.com/dop251/goja_nodejs/require" ) -func LoadServerJslibs(tenantId consts.TenantId, runtime *goja.Runtime, require *require.RequireModule) { +func LoadServerJslibs(tenantId consts.TenantId, runtime *goja.Runtime, require *require.RequireModule, projectId uint) { LoadCacheIfNeeded(tenantId) JslibCache.Range(func(key, value interface{}) bool { - id := key.(uint) - if tenantId == "" { - tenantId = "NA" - } lib, ok := value.(Jslib) if !ok { return true } - tmpFile := fmt.Sprintf("%d-%s-%d.js", id, tenantId, lib.UpdatedAt.Unix()) + if lib.ProjectId != projectId { + return true + } + + tmpFile := fmt.Sprintf("%s-%d.js", key, lib.UpdatedAt.Unix()) tmpPath := fmt.Sprintf("%s/%s.js", consts.TmpDirRelativeServer, tmpFile) tmpContent := lib.Script fileUtils.WriteFileIfNotExist(tmpPath, tmpContent) @@ -31,10 +31,49 @@ func LoadServerJslibs(tenantId consts.TenantId, runtime *goja.Runtime, require * module, err := require.Require("./" + tmpPath) if err != nil { logUtils.Infof("goja require failed, path: %s, err: %s.", tmpPath, err.Error()) + return true } runtime.Set(lib.Name, module) + res := module.Export() + functions, ok := res.(map[string]interface{}) + if ok { + lib.Functions = make([]JsFunc, 0) + for funcName, _ := range functions { + var args string + args, err = GetJsFunsParams(runtime, fmt.Sprintf("%s.%s", lib.Name, funcName)) + if err == nil { + lib.Functions = append(lib.Functions, JsFunc{Name: funcName, Args: args}) + } + + } + SetJslibCache(tenantId, lib.Id, lib) + } + return true }) } + +func GetJsFunsParams(runtime *goja.Runtime, funName string) (res string, err error) { + script := `function getFunctionParameters(func) { + const str = func.toString(); + const start = str.indexOf('(') + 1; + const end = str.indexOf(')'); + + if (start < 0 || end < 0) { + return []; // 如果函数没有参数或格式不正确,返回空数组 + } + + const result = str.slice(start, end).match(/\b\w+\b/g); // 使用正则表达式匹配参数名称 + if (result === null) { + return []; // 如果没有匹配到参数,返回空数组 + } + + return result; +} +getFunctionParameters(` + funName + `)` + value, err := runtime.RunString(script) + return value.String(), err + +} diff --git a/internal/pkg/helper/mock/mock.go b/internal/pkg/helper/mock/mock.go index 74494af15..dc7da3650 100644 --- a/internal/pkg/helper/mock/mock.go +++ b/internal/pkg/helper/mock/mock.go @@ -100,9 +100,9 @@ func SetValueToGoja(name string, value interface{}) { _setValueFunc(name, value) } -func InitJsRuntime(tenantId consts.TenantId) { +func InitJsRuntime(tenantId consts.TenantId, projectId uint) { if mockVm.JsRuntime != nil { - jslibHelper.LoadServerJslibs(tenantId, mockVm.JsRuntime, mockRequire) + jslibHelper.LoadServerJslibs(tenantId, mockVm.JsRuntime, mockRequire, projectId) return } @@ -130,5 +130,5 @@ func InitJsRuntime(tenantId consts.TenantId) { mockVm.JsRuntime.Set("dt", dt) // import other custom libs - jslibHelper.LoadServerJslibs(tenantId, mockVm.JsRuntime, mockRequire) + jslibHelper.LoadServerJslibs(tenantId, mockVm.JsRuntime, mockRequire, projectId) } diff --git a/internal/pkg/helper/openapi-mock/openapi/generator/data/mockjsGenerator.go b/internal/pkg/helper/openapi-mock/openapi/generator/data/mockjsGenerator.go index 6ef959818..c0850c1b3 100644 --- a/internal/pkg/helper/openapi-mock/openapi/generator/data/mockjsGenerator.go +++ b/internal/pkg/helper/openapi-mock/openapi/generator/data/mockjsGenerator.go @@ -14,13 +14,20 @@ type MockjsGenerator struct { func (g *MockjsGenerator) GenerateDataBySchema(ctx context.Context, schema *openapi3.Schema) (value Data, err error) { expr := mockjsHelper.GetMockJsSchemaExpression(schema) + + value, _ = g.GenerateByMockJsExpression(expr, schema.Type) + + return +} + +func (g *MockjsGenerator) GenerateByMockJsExpression(expr string, schemaType string) (ret interface{}, err error) { req := serverDomain.MockJsExpression{ Expression: expr, } - ret, err := mockjsHelper.EvaluateExpression(req) + result, err := mockjsHelper.EvaluateExpression(req) - value = mockjsHelper.ConvertData(ret.Result, schema.Type) + ret = mockjsHelper.ConvertData(result.Result, schemaType) return } diff --git a/internal/pkg/helper/openapi-mock/openapi/generator/data/numberGenerator.go b/internal/pkg/helper/openapi-mock/openapi/generator/data/numberGenerator.go index b153011cf..366a1fa77 100644 --- a/internal/pkg/helper/openapi-mock/openapi/generator/data/numberGenerator.go +++ b/internal/pkg/helper/openapi-mock/openapi/generator/data/numberGenerator.go @@ -53,7 +53,8 @@ func (generator *numberGenerator) getMinMax(schema *openapi3.Schema) (float64, f func (generator *numberGenerator) generateUniformRandomValue(schema *openapi3.Schema) float64 { minimum := 0 - maximum := math.MaxInt64 + maximum := math.MaxInt32 + if schema.ExclusiveMin { minimum++ } @@ -62,8 +63,8 @@ func (generator *numberGenerator) generateUniformRandomValue(schema *openapi3.Sc } delta := maximum - minimum - value1 := float64(generator.random.Intn(delta)+minimum) / float64(math.MaxInt64) - value2 := float64(generator.random.Intn(delta)+minimum) / float64(math.MaxInt64) + value1 := float64(generator.random.Intn(delta)+minimum) / float64(maximum) + value2 := float64(generator.random.Intn(delta)+minimum) / float64(maximum) return value1 * value2 } diff --git a/internal/pkg/helper/script/script.go b/internal/pkg/helper/script/script.go index 71da6a1e7..ae5473034 100644 --- a/internal/pkg/helper/script/script.go +++ b/internal/pkg/helper/script/script.go @@ -17,6 +17,13 @@ func GetScript(name ScriptType) string { } return DeepTestScript + } else if name == ScriptDeepTestSimple { + if DeepTestScriptSimple == "" { + bytes, _ := deeptest.ReadResData(path.Join("res", "goja", "export", "deeptest-simple.js")) + DeepTestScriptSimple = string(bytes) + } + return DeepTestScriptSimple + } else if name == DeclareDeepTest { if DeepTestDeclare == "" { bytes, _ := deeptest.ReadResData(path.Join("res", "goja", "export", "deeptest.d.ts")) @@ -87,13 +94,20 @@ func GetScript(name ScriptType) string { } return VariablesClear + } else if name == ScriptCustom { + if CustomScript == "" { + bytes, _ := deeptest.ReadResData(path.Join("res", "goja", "export", "custom.js")) + CustomScript = string(bytes) + } + return CustomScript + } return "" } -func GetModule(name string) (ret string) { - bytes, _ := deeptest.ReadResData(path.Join("res", "goja", "module", name)) +func GetModule(name ScriptType) (ret string) { + bytes, _ := deeptest.ReadResData(path.Join("res", "goja", "module", name.String())) ret = string(bytes) return @@ -123,6 +137,7 @@ func GenResultMsg(po *domain.ScriptBase) { var ( DeepTestScript = "" + DeepTestScriptSimple = "" DeepTestDeclare = "" DeepTestDeclarePost = "" DeepTestScenarioCustomCode = "" @@ -137,28 +152,32 @@ var ( VariablesGet = "" VariablesSet = "" VariablesClear = "" + CustomScript = "" ) type ScriptType string const ( - ScriptDeepTest = "deeptest" - DeclareDeepTest = "deeptest.d" - DeclareDeepTestPost = "deeptest-post.d" - DeclareDeepTestScenarioCustomCode = "deeptest-scenario-custom-code.d" - DeclareChai = "chai.d" + ScriptDeepTest ScriptType = "deeptest" + ScriptDeepTestSimple ScriptType = "deeptest-simple" + DeclareDeepTest ScriptType = "deeptest.d" + DeclareDeepTestPost ScriptType = "deeptest-post.d" + DeclareDeepTestScenarioCustomCode ScriptType = "deeptest-scenario-custom-code.d" + DeclareChai ScriptType = "chai.d" + + ModuleMockJs ScriptType = "mockjs.js" - ModuleMockJs = "mockjs.js" + ScriptMock ScriptType = "mock" + DeclareMock ScriptType = "mock.d" - ScriptMock = "mock" - DeclareMock = "mock.d" + DeclareJslibs ScriptType = "jslibs" - DeclareJslibs = "jslibs" + ScriptCustom ScriptType = "custom" - SnippetDatapoolGet = "datapool_get" - SnippetVariablesGet = "variables_get" - SnippetVariablesSet = "variables_set" - SnippetVariablesClear = "variables_clear" + SnippetDatapoolGet ScriptType = "datapool_get" + SnippetVariablesGet ScriptType = "variables_get" + SnippetVariablesSet ScriptType = "variables_set" + SnippetVariablesClear ScriptType = "variables_clear" ) func (e ScriptType) String() string { diff --git a/internal/pkg/log/log.go b/internal/pkg/log/log.go index cd16a609c..da3d12d15 100644 --- a/internal/pkg/log/log.go +++ b/internal/pkg/log/log.go @@ -1,32 +1,43 @@ package zapLog import ( + "fmt" "github.com/aaronchen2k/deeptest/internal/pkg/config" "github.com/aaronchen2k/deeptest/internal/pkg/consts" - myZap "github.com/aaronchen2k/deeptest/pkg/core/zap" logUtils "github.com/aaronchen2k/deeptest/pkg/lib/log" "github.com/snowlyg/helper/dir" "go.uber.org/zap" "go.uber.org/zap/zapcore" + "os" + "path/filepath" + "runtime" ) -// level 日志级别 -var level zapcore.Level - -// Init 初始化日志服务 func Init() { - var logger *zap.Logger + logPath := getLogPath() - logDir := "log" - if consts.RunFrom == consts.FromServer { - logDir = config.CONFIG.Zap.Director - } + logUtils.Logger = getLogger(logPath) +} +func getLogger(logPath string) (logger *zap.Logger) { + logDir := filepath.Dir(logPath) if !dir.IsExist(logDir) { dir.InsureDir(logDir) } - switch config.CONFIG.Zap.Level { // 初始化配置文件的Level + level := getLogLevel() + + logger, err := CreateLogger(logPath, level) + + if err != nil { + panic(err) + } + + return +} + +func getLogLevel() (level zapcore.Level) { + switch config.CONFIG.Zap.Level { case "debug": level = zap.DebugLevel case "info": @@ -45,14 +56,47 @@ func Init() { level = zap.InfoLevel } - if level == zap.DebugLevel || level == zap.ErrorLevel { - logger = zap.New(myZap.GetEncoderCore(level), zap.AddStacktrace(level)) + return +} + +func getLogPath() (ret string) { + if runtime.GOOS == "windows" { + ret = filepath.Join("log", fmt.Sprintf("%s.log", consts.RunFrom)) } else { - logger = zap.New(myZap.GetEncoderCore(level)) + ret = filepath.Join(consts.WorkDir, "log", fmt.Sprintf("%s.log", consts.RunFrom)) } - if config.CONFIG.Zap.ShowLine { - logger = logger.WithOptions(zap.AddCaller()) + + return +} + +func CreateLogger(logPath string, level zapcore.Level) (ret *zap.Logger, err error) { + prodEncoder := zap.NewProductionEncoderConfig() + prodEncoder.EncodeTime = zapcore.ISO8601TimeEncoder + + infoPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool { + + return lev >= level + }) + writeSyncer, lowClose, err := zap.Open(logPath) + if err != nil { + if lowClose != nil { + lowClose() + } + return } - logUtils.Logger = logger + swSugar := zapcore.NewMultiWriteSyncer( + writeSyncer, + zapcore.AddSync(os.Stdout), + ) + infoCore := zapcore.NewCore(zapcore.NewJSONEncoder(prodEncoder), swSugar, infoPriority) + + ret = zap.New(zapcore.NewTee(infoCore)) + + ret = ret.WithOptions( + zap.AddCaller(), + zap.AddCallerSkip(1), + zap.AddStacktrace(zap.ErrorLevel)) + + return } diff --git a/internal/server/consts/enum.go b/internal/server/consts/enum.go index 57e36f56b..2137f6116 100644 --- a/internal/server/consts/enum.go +++ b/internal/server/consts/enum.go @@ -52,6 +52,9 @@ type DiagnoseInterfaceType string const ( DiagnoseInterfaceTypeDir DiagnoseInterfaceType = "dir" DiagnoseInterfaceTypeInterface DiagnoseInterfaceType = "interface" + + DiagnoseInterfaceTypeWebsocketInterface DiagnoseInterfaceType = "websocket_interface" + DiagnoseInterfaceTypeGrpcInterface DiagnoseInterfaceType = "grpc_interface" ) func (e DiagnoseInterfaceType) String() string { diff --git a/internal/server/modules/model/interface-grpc.go b/internal/server/modules/model/interface-grpc.go new file mode 100644 index 000000000..a5f3268e6 --- /dev/null +++ b/internal/server/modules/model/interface-grpc.go @@ -0,0 +1,61 @@ +package model + +import ( + "github.com/aaronchen2k/deeptest/internal/pkg/consts" + serverConsts "github.com/aaronchen2k/deeptest/internal/server/consts" +) + +type GrpcInterface struct { + BaseModel + + CreatedBy uint `json:"createdBy"` + UpdatedBy uint `json:"updatedBy"` + + Name string `json:"name"` + Address string `json:"address"` + UseTls *bool `json:"useTls"` + RestartConn *bool `json:"restartConn"` + RequestMetadata string `json:"requestMetadata"` + + Service string `gorm:"default:''" json:"service"` + Method string `gorm:"default:''" json:"method"` + + Schema string `json:"schema"` + Template string `json:"template"` + Message string `json:"message"` + + ProtoSrc string `gorm:"default:'remote'" json:"protoSrc"` + ProtoName string `json:"protoName"` + ProtoPath string `json:"protoPath"` + // ProtoContent string `json:"protoContent"` + + UsedBy consts.UsedBy `json:"usedBy"` + Type serverConsts.DiagnoseInterfaceType `json:"type"` + + DiagnoseInterfaceId uint `json:"diagnoseInterfaceId"` + EnvironmentId uint `json:"environmentId"` + ServeId uint `json:"serveId"` + ServerId uint `json:"serverId"` + ProjectId uint `json:"projectId"` +} + +type GrpcInterfaceRequest struct { + BaseModel + + CreatedBy uint `json:"createdBy"` + UpdatedBy uint `json:"updatedBy"` + + Service string `json:"service"` + Method string `json:"method"` + RequestContent string `json:"requestContent"` + ResponseContent string `json:"responseContent"` + + InterfaceId uint `json:"interfaceId"` +} + +func (GrpcInterface) TableName() string { + return "biz_grpc_interface" +} +func (GrpcInterfaceRequest) TableName() string { + return "biz_grpc_interface_request" +} diff --git a/internal/server/modules/model/interface-websocket.go b/internal/server/modules/model/interface-websocket.go new file mode 100644 index 000000000..613fb4752 --- /dev/null +++ b/internal/server/modules/model/interface-websocket.go @@ -0,0 +1,81 @@ +package model + +type WebsocketInterface struct { + BaseModel + + Name string `json:"name"` + Message string `json:"message"` + + Address string `json:"address"` + ExtMode *bool `json:"extMode"` + Namespace string `json:"Namespace"` + Room string `json:"room"` + Event string `json:"event"` + ListenEvents string `json:"listenEvents"` + + CreatedBy uint `json:"createdBy"` + UpdatedBy uint `json:"updatedBy"` + + DiagnoseInterfaceId uint `json:"diagnoseInterfaceId"` + ProjectId uint `json:"projectId"` + UseID uint `json:"useId"` +} + +func (WebsocketInterface) TableName() string { + return "biz_websocket_interface" +} + +type WebsocketInterfaceParam struct { + BaseModel + InterfaceParamBase +} + +func (WebsocketInterfaceParam) TableName() string { + return "biz_websocket_interface_param" +} + +type WebsocketInterfaceHeader struct { + BaseModel + InterfaceHeaderBase +} + +func (WebsocketInterfaceHeader) TableName() string { + return "biz_websocket_interface_header" +} + +type WebsocketInterfaceRequest struct { + BaseModel + + CreatedBy uint `json:"createdBy"` + UpdatedBy uint `json:"updatedBy"` + + // adv mode + Namespace string `json:"Namespace"` + Room string `json:"room"` + EmitEvent string `json:"emitEvent"` + + RequestContent string `gorm:"type:text" json:"requestContent"` + ResponseContent string `gorm:"type:text" json:"responseContent"` // multi-line + + InterfaceId uint `json:"interfaceId"` +} + +func (WebsocketInterfaceRequest) TableName() string { + return "biz_websocket_interface_request" +} + +type WebsocketCache struct { + BaseModel + + CreatedBy uint `json:"createdBy"` + UpdatedBy uint `json:"updatedBy"` + + SerialNumber string `json:"serialNumber"` + Name string `json:"name"` + Desc string `gorm:"type:text" json:"desc"` + Data string `gorm:"type:text" json:"data"` +} + +func (WebsocketCache) TableName() string { + return "biz_websocket_cache" +} diff --git a/internal/server/modules/model/scenario-report.go b/internal/server/modules/model/scenario-report.go index 662311006..c092b4115 100644 --- a/internal/server/modules/model/scenario-report.go +++ b/internal/server/modules/model/scenario-report.go @@ -48,7 +48,7 @@ type ScenarioReport struct { Logs []*ExecLogProcessor `gorm:"-" json:"logs"` ExecEnv string `gorm:"-" json:"execEnv"` - ExecEnvId int `json:"execEnvId"` + ExecEnvId uint `json:"execEnvId"` Priority string `gorm:"-" json:"priority"` StatRaw string `json:"stat"` diff --git a/internal/server/modules/repo/condition-checkpoint.go b/internal/server/modules/repo/condition-checkpoint.go index f5b7b5b8d..39aa0e57d 100644 --- a/internal/server/modules/repo/condition-checkpoint.go +++ b/internal/server/modules/repo/condition-checkpoint.go @@ -48,6 +48,13 @@ func (r *CheckpointRepo) Save(tenantId consts.TenantId, checkpoint *model.DebugC return } func (r *CheckpointRepo) UpdateDesc(tenantId consts.TenantId, po *model.DebugConditionCheckpoint) (err error) { + /* + result := po.ActualResult + if po.ResultStatus != consts.Pass { + result = po.ExpectResult + } + */ + desc := checkpointHelpper.GenDesc(po.Type, po.Operator, po.Value, po.Expression, po.ExtractorVariable, po.ExtractorType, po.ExtractorExpression) values := map[string]interface{}{ @@ -115,8 +122,7 @@ func (r *CheckpointRepo) CreateDefault(tenantId consts.TenantId, conditionId uin po = model.DebugConditionCheckpoint{ CheckpointBase: domain.CheckpointBase{ - ConditionId: conditionId, - + ConditionId: conditionId, Type: consts.ResponseStatus, Operator: consts.Equal, Expression: "", diff --git a/internal/server/modules/repo/interface-grpc.go b/internal/server/modules/repo/interface-grpc.go new file mode 100644 index 000000000..aaf322864 --- /dev/null +++ b/internal/server/modules/repo/interface-grpc.go @@ -0,0 +1,71 @@ +package repo + +import ( + v1 "github.com/aaronchen2k/deeptest/cmd/server/v1/domain" + "github.com/aaronchen2k/deeptest/internal/pkg/consts" + "github.com/aaronchen2k/deeptest/internal/server/modules/model" + "github.com/jinzhu/copier" + "gorm.io/gorm" +) + +type GrpcInterfaceRepo struct { + DiagnoseInterfaceRepo *DiagnoseInterfaceRepo `inject:""` + + *BaseRepo `inject:""` + DB *gorm.DB `inject:""` +} + +func (r *GrpcInterfaceRepo) Get(tenantId consts.TenantId, id uint) (po model.GrpcInterface, err error) { + err = r.GetDB(tenantId).Where("id = ?", id).First(&po).Error + return +} + +func (r *GrpcInterfaceRepo) SaveDebugData(po model.GrpcInterface, tenantId consts.TenantId) (err error) { + err = r.Save(tenantId, po.ID, &po) + + return +} + +func (r *GrpcInterfaceRepo) Create(tenantId consts.TenantId, req v1.DiagnoseInterfaceSaveReq) ( + diagnoseInterface model.DiagnoseInterface, err error) { + + if req.ID == 0 { // create both Diagnose and gRPC interface + grpcInterface := model.GrpcInterface{ + Name: req.Title, + ProjectId: req.ProjectId, + CreatedBy: req.CreatedBy, + } + err = r.Save(tenantId, 0, &grpcInterface) + + copier.CopyWithOption(&diagnoseInterface, req, copier.Option{ + DeepCopy: true, + }) + diagnoseInterface.DebugInterfaceId = grpcInterface.ID + err = r.DiagnoseInterfaceRepo.Save(tenantId, &diagnoseInterface) + + err = r.UpdateDiagnoseInterfaceId(tenantId, grpcInterface.ID, diagnoseInterface.ID) + + } else { // update title + diagnoseInterface, _ = r.DiagnoseInterfaceRepo.Get(tenantId, req.ID) + diagnoseInterface.Title = req.Title + + err = r.DiagnoseInterfaceRepo.Save(tenantId, &diagnoseInterface) + } + + return +} + +func (r *GrpcInterfaceRepo) UpdateDiagnoseInterfaceId(tenantId consts.TenantId, grpcInterfaceId, + diagnoseInterfaceId uint) (err error) { + + values := map[string]interface{}{ + "diagnose_interface_id": diagnoseInterfaceId, + } + + err = r.GetDB(tenantId).Model(&model.GrpcInterface{}). + Where("id=?", grpcInterfaceId). + Updates(values). + Error + + return +} diff --git a/internal/server/modules/repo/interface-websocket.go b/internal/server/modules/repo/interface-websocket.go new file mode 100644 index 000000000..dd7806c63 --- /dev/null +++ b/internal/server/modules/repo/interface-websocket.go @@ -0,0 +1,179 @@ +package repo + +import ( + v1 "github.com/aaronchen2k/deeptest/cmd/server/v1/domain" + "github.com/aaronchen2k/deeptest/internal/pkg/consts" + "github.com/aaronchen2k/deeptest/internal/pkg/domain" + "github.com/aaronchen2k/deeptest/internal/server/modules/model" + "github.com/jinzhu/copier" + "gorm.io/gorm" +) + +type WebsocketInterfaceRepo struct { + DiagnoseInterfaceRepo *DiagnoseInterfaceRepo `inject:""` + + *BaseRepo `inject:""` + DB *gorm.DB `inject:""` +} + +func (r *WebsocketInterfaceRepo) Get(tenantId consts.TenantId, id uint) (po model.WebsocketInterface, err error) { + err = r.GetDB(tenantId).Where("id = ?", id).First(&po).Error + + return +} +func (r *WebsocketInterfaceRepo) SaveDebugData(po model.WebsocketInterface, tenantId consts.TenantId) (err error) { + err = r.Save(tenantId, po.ID, &po) + + return +} + +func (r *WebsocketInterfaceRepo) Create(tenantId consts.TenantId, req v1.DiagnoseInterfaceSaveReq) ( + diagnoseInterface model.DiagnoseInterface, err error) { + + if req.ID == 0 { // create both Diagnose and Websocket interface + websocketInterface := model.WebsocketInterface{ + Name: req.Title, + ProjectId: req.ProjectId, + CreatedBy: req.CreatedBy, + } + err = r.Save(tenantId, 0, &websocketInterface) + + copier.CopyWithOption(&diagnoseInterface, req, copier.Option{ + DeepCopy: true, + }) + diagnoseInterface.DebugInterfaceId = websocketInterface.ID + err = r.DiagnoseInterfaceRepo.Save(tenantId, &diagnoseInterface) + + err = r.UpdateDiagnoseInterfaceId(tenantId, websocketInterface.ID, diagnoseInterface.ID) + + } else { // update title + diagnoseInterface, _ = r.DiagnoseInterfaceRepo.Get(tenantId, req.ID) + diagnoseInterface.Title = req.Title + + err = r.DiagnoseInterfaceRepo.Save(tenantId, &diagnoseInterface) + } + + return +} + +func (r *WebsocketInterfaceRepo) UpdateDiagnoseInterfaceId(tenantId consts.TenantId, websocketInterfaceId, + diagnoseInterfaceId uint) (err error) { + + values := map[string]interface{}{ + "diagnose_interface_id": diagnoseInterfaceId, + } + + err = r.GetDB(tenantId).Model(&model.WebsocketInterface{}). + Where("id=?", websocketInterfaceId). + Updates(values). + Error + + return +} + +func (r *WebsocketInterfaceRepo) ListParams(tenantId consts.TenantId, interfaceId uint) ( + params []model.WebsocketInterfaceParam, err error) { + + pos := []model.WebsocketInterfaceParam{} + + err = r.GetDB(tenantId). + Where("interface_id=?", interfaceId). + Where("NOT deleted"). + Order("id ASC"). + Find(&pos).Error + + for _, po := range pos { + params = append(params, po) + } + + return +} +func (r *WebsocketInterfaceRepo) ListHeaders(tenantId consts.TenantId, interfaceId uint) ( + pos []model.WebsocketInterfaceHeader, err error) { + + err = r.GetDB(tenantId). + Where("interface_id=?", interfaceId). + Where("NOT deleted"). + Order("id ASC"). + Find(&pos).Error + + return +} + +func (r *WebsocketInterfaceRepo) SaveParams(params *[]domain.Param, id uint, tenantId consts.TenantId) (err error) { + err = r.RemoveParams(tenantId, id) + + if params == nil || len(*params) == 0 { + return + } + + var pos []model.WebsocketInterfaceParam + + for _, p := range *params { + if p.Name == "" { + continue + } + + po := model.WebsocketInterfaceParam{ + InterfaceParamBase: model.InterfaceParamBase{ + Name: p.Name, + Value: p.Value, + Type: p.Type, + InterfaceId: id, + }, + } + + pos = append(pos, po) + } + + err = r.GetDB(tenantId).Create(&pos).Error + + return +} + +func (r *WebsocketInterfaceRepo) RemoveParams(tenantId consts.TenantId, id uint) (err error) { + err = r.GetDB(tenantId). + Where("interface_id = ?", id). + Delete(&model.WebsocketInterfaceParam{}, "").Error + + return +} + +func (r *WebsocketInterfaceRepo) SaveHeaders(headers *[]domain.Header, id uint, tenantId consts.TenantId) (err error) { + err = r.RemoveHeaders(tenantId, id) + + if len(*headers) == 0 { + return + } + + var pos []model.WebsocketInterfaceHeader + + for _, p := range *headers { + if p.Name == "" { + continue + } + + po := model.WebsocketInterfaceHeader{ + InterfaceHeaderBase: model.InterfaceHeaderBase{ + Name: p.Name, + Value: p.Value, + Type: p.Type, + InterfaceId: id, + }, + } + + pos = append(pos, po) + } + + err = r.GetDB(tenantId).Create(&pos).Error + + return +} + +func (r *WebsocketInterfaceRepo) RemoveHeaders(tenantId consts.TenantId, id uint) (err error) { + err = r.GetDB(tenantId). + Where("interface_id = ?", id). + Delete(&model.WebsocketInterfaceHeader{}, "").Error + + return +} diff --git a/internal/server/modules/service/debug-interface.go b/internal/server/modules/service/debug-interface.go index a3137ccfc..dca114d39 100644 --- a/internal/server/modules/service/debug-interface.go +++ b/internal/server/modules/service/debug-interface.go @@ -554,8 +554,13 @@ func (s *DebugInterfaceService) LoadCurl(tenantId consts.TenantId, req serverDom // replace variables uuid := fmt.Sprintf("load_curl_on_server_side_user%d_%s", req.UserId, _stringUtils.Uuid()) - agentExec.SetExecScene(uuid, execObj.ExecScene) - agentExec.ReplaceVariables(&execObj.DebugData.BaseRequest, uuid) + session := agentExec.ExecSession{ + ExecUuid: uuid, + ExecScene: execObj.ExecScene, + InterfaceDebug: new(agentExec.InterfaceDebugSession), + ScenarioDebug: new(agentExec.ScenarioDebugSession), + } + agentExec.ReplaceVariables(&execObj.DebugData.BaseRequest, &session) // gen url execObj.DebugData.BaseRequest.Url, _ = agentService.UpdateUrl(execObj.DebugData) diff --git a/internal/server/modules/service/diagnose-interface.go b/internal/server/modules/service/diagnose-interface.go index 119e87a98..7f83fcd4c 100644 --- a/internal/server/modules/service/diagnose-interface.go +++ b/internal/server/modules/service/diagnose-interface.go @@ -19,8 +19,11 @@ import ( ) type DiagnoseInterfaceService struct { - EndpointInterfaceRepo *repo.EndpointInterfaceRepo `inject:""` - DiagnoseInterfaceRepo *repo.DiagnoseInterfaceRepo `inject:""` + EndpointInterfaceRepo *repo.EndpointInterfaceRepo `inject:""` + DiagnoseInterfaceRepo *repo.DiagnoseInterfaceRepo `inject:""` + WebsocketInterfaceRepo *repo.WebsocketInterfaceRepo `inject:""` + GrpcInterfaceRepo *repo.GrpcInterfaceRepo `inject:""` + EndpointRepo *repo.EndpointRepo `inject:""` ServeRepo *repo.ServeRepo `inject:""` ScenarioProcessorRepo *repo.ScenarioProcessorRepo `inject:""` @@ -52,14 +55,6 @@ func (s *DiagnoseInterfaceService) Get(tenantId consts.TenantId, id int) (ret mo func (s *DiagnoseInterfaceService) Save(tenantId consts.TenantId, req serverDomain.DiagnoseInterfaceSaveReq) (diagnoseInterface model.DiagnoseInterface, err error) { s.CopyValueFromRequest(tenantId, &diagnoseInterface, req) - //if diagnoseInterface.ID != 0 { - // oldDiagnoseInterface, err := s.DiagnoseInterfaceRepo.Get(diagnoseInterface.ID) - // if err != nil { - // return diagnoseInterface, err - // } - // diagnoseInterface.CreatedBy = oldDiagnoseInterface.CreatedBy - //} - if diagnoseInterface.Type == serverConsts.DiagnoseInterfaceTypeInterface { if req.ID == 0 { //server, _ := s.ServeServerRepo.GetDefaultByServe(diagnoseInterface.ServeId) @@ -95,19 +90,11 @@ func (s *DiagnoseInterfaceService) Save(tenantId consts.TenantId, req serverDoma err = s.DiagnoseInterfaceRepo.Save(tenantId, &diagnoseInterface) } - // create new DebugInterface - //debugInterface := model.DebugInterface{ - // InterfaceBase: model.InterfaceBase{ - // Name: req.Title, - // InterfaceConfigBase: model.InterfaceConfigBase{ - // //Url: server.Url, - // Method: consts.GET, - // }, - // }, - // ServeId: diagnoseInterface.ServeId, - // //ServerId: server.ID, - // BaseUrl: "", - //} + } else if req.Type == serverConsts.DiagnoseInterfaceTypeWebsocketInterface { + diagnoseInterface, err = s.WebsocketInterfaceRepo.Create(tenantId, req) + + } else if req.Type == serverConsts.DiagnoseInterfaceTypeGrpcInterface { + diagnoseInterface, err = s.GrpcInterfaceRepo.Create(tenantId, req) } else { if req.ID != 0 { @@ -115,6 +102,7 @@ func (s *DiagnoseInterfaceService) Save(tenantId consts.TenantId, req serverDoma diagnoseInterface.Title = req.Title } err = s.DiagnoseInterfaceRepo.Save(tenantId, &diagnoseInterface) + } return diff --git a/internal/server/modules/service/interface-grpc.go b/internal/server/modules/service/interface-grpc.go new file mode 100644 index 000000000..92a56bc1c --- /dev/null +++ b/internal/server/modules/service/interface-grpc.go @@ -0,0 +1,191 @@ +package service + +import ( + serverDomain "github.com/aaronchen2k/deeptest/cmd/server/v1/domain" + "github.com/aaronchen2k/deeptest/internal/pkg/consts" + "github.com/aaronchen2k/deeptest/internal/pkg/domain" + grpcHelper "github.com/aaronchen2k/deeptest/internal/pkg/helper/grpc" + grpcDomain "github.com/aaronchen2k/deeptest/internal/pkg/helper/grpc/domain" + "github.com/aaronchen2k/deeptest/internal/server/modules/model" + "github.com/aaronchen2k/deeptest/internal/server/modules/repo" + _stringUtils "github.com/aaronchen2k/deeptest/pkg/lib/string" + "github.com/jinzhu/copier" + "path/filepath" + "strings" + "sync" +) + +var ( + handleStore sync.Map +) + +type GrpcInterfaceService struct { + GrpcInterfaceRepo *repo.GrpcInterfaceRepo `inject:""` + DiagnoseInterfaceRepo *repo.DiagnoseInterfaceRepo `inject:""` +} + +func (s *GrpcInterfaceService) GetDebugData(tenantId consts.TenantId, id int) (ret domain.GrpcDebugData, err error) { + diagnose, err := s.DiagnoseInterfaceRepo.Get(tenantId, uint(id)) + + copier.CopyWithOption(&ret, diagnose, copier.Option{ + DeepCopy: true, + }) + + po, err := s.GrpcInterfaceRepo.Get(tenantId, diagnose.DebugInterfaceId) + if err != nil { + return + } + + copier.CopyWithOption(&ret, po, copier.Option{ + DeepCopy: true, + }) + + return +} + +func (s *GrpcInterfaceService) SaveDebugData(data domain.GrpcDebugData, tenantId consts.TenantId) (err error) { + po := model.GrpcInterface{} + copier.CopyWithOption(&po, data, copier.Option{DeepCopy: true}) + + err = s.GrpcInterfaceRepo.SaveDebugData(po, tenantId) + + return +} + +func (s *GrpcInterfaceService) ParseProto(req serverDomain.GrpcReq) ( + ret serverDomain.GrpcParseResp, err error) { + + handler := s.getHandler(req.ExecUuid) + if handler == nil { + return + } + + if req.ProtoPath != "" { + req.ProtoPath = filepath.Join(consts.WorkDir, req.ProtoPath) + } + + var services, methods []string + + if req.ProtoSrc == "local" { + services, methods, err = handler.ListWithProto(req) + } else { + services, methods, err = handler.List(req) + } + if err != nil { + return + } + + s.popServices(&ret, services) + s.popMethods(&ret, methods) + + return +} + +func (s *GrpcInterfaceService) DescribeFunction(req serverDomain.GrpcReq) ( + ret grpcDomain.Desc, err error) { + + handler := s.getHandler(req.ExecUuid) + if handler == nil { + return + } + + ret, _ = handler.DescribeFunc(req) + + ret.Schema = ret.Schema + ret.Template = _stringUtils.FormatJsonStr(ret.Template) + + return +} + +func (s *GrpcInterfaceService) InvokeFunc(req serverDomain.GrpcReq) ( + ret grpcDomain.InvRes, err error) { + + handler := s.getHandler(req.ExecUuid) + if handler == nil { + return + } + + ret, err = handler.InvokeFunc(req) + if err != nil { + return + } + + return +} + +func (s *GrpcInterfaceService) ListActiveConn(req serverDomain.GrpcReq) (result []string, err error) { + handler := s.getHandler(req.ExecUuid) + if handler == nil { + return + } + + result, err = handler.G.ListActiveConn(strings.TrimSpace(req.Address)) + if err != nil { + return + } + + return +} + +func (s *GrpcInterfaceService) DeleteHandle(req serverDomain.GrpcReq) (err error) { + handler := s.getHandler(req.ExecUuid) + if handler == nil { + return + } + + err = handler.G.CloseActiveConns("all") + if err != nil { + return + } + + handleStore.Delete(req.ExecUuid) + + return +} + +func (s *GrpcInterfaceService) popServices(resp *serverDomain.GrpcParseResp, result []string) ( + ret grpcDomain.Desc, err error) { + + for _, item := range result { + if strings.Contains(item, "grpc.reflection.") { + continue + } + + service := serverDomain.GrpcService{ + Name: strings.TrimSpace(item), + } + (*resp).Services = append((*resp).Services, service) + } + + return +} + +func (s *GrpcInterfaceService) popMethods(resp *serverDomain.GrpcParseResp, result []string) ( + ret grpcDomain.Desc, err error) { + + for _, item := range result { + service := serverDomain.GrpcMethod{ + Name: strings.TrimSpace(item), + } + (*resp).Methods = append((*resp).Methods, service) + } + + return +} + +func (s *GrpcInterfaceService) getHandler(execUuid string) (ret *grpcHelper.Handler) { + obj, ok := handleStore.Load(execUuid) + if !ok { + inst := grpcHelper.InitHandler() + handleStore.Store(execUuid, inst) + + obj, ok = handleStore.Load(execUuid) + if !ok { + return + } + } + + ret = obj.(*grpcHelper.Handler) + + return +} diff --git a/internal/server/modules/service/interface-websocket.go b/internal/server/modules/service/interface-websocket.go new file mode 100644 index 000000000..147254b94 --- /dev/null +++ b/internal/server/modules/service/interface-websocket.go @@ -0,0 +1,105 @@ +package service + +import ( + "github.com/aaronchen2k/deeptest/internal/pkg/consts" + "github.com/aaronchen2k/deeptest/internal/pkg/domain" + model "github.com/aaronchen2k/deeptest/internal/server/modules/model" + "github.com/aaronchen2k/deeptest/internal/server/modules/repo" + "github.com/jinzhu/copier" +) + +type WebsocketInterfaceService struct { + WebsocketInterfaceRepo *repo.WebsocketInterfaceRepo `inject:""` + DiagnoseInterfaceRepo *repo.DiagnoseInterfaceRepo `inject:""` +} + +func (s *WebsocketInterfaceService) GetDebugData(tenantId consts.TenantId, diagnoseInterfaceId int) ( + ret domain.WebsocketDebugData, err error) { + + diagnose, err := s.DiagnoseInterfaceRepo.Get(tenantId, uint(diagnoseInterfaceId)) + if err != nil { + return + } + + copier.CopyWithOption(&ret, diagnose, copier.Option{ + DeepCopy: true, + }) + + po, err := s.WebsocketInterfaceRepo.Get(tenantId, diagnose.DebugInterfaceId) + if err != nil { + return + } + + copier.CopyWithOption(&ret, po, copier.Option{ + DeepCopy: true, + }) + + ret.Params, _ = s.ListParams(tenantId, po.ID) + ret.Headers, _ = s.ListHeaders(tenantId, po.ID) + + if ret.Params == nil { + ret.Params = &[]domain.Param{} + } + *ret.Params = append(*ret.Params, domain.Param{Name: "", Value: ""}) + + if ret.Headers == nil { + ret.Headers = &[]domain.Header{} + } + *ret.Headers = append(*ret.Headers, domain.Header{Name: "", Value: ""}) + + return +} + +func (s *WebsocketInterfaceService) SaveDebugData(data domain.WebsocketDebugData, tenantId consts.TenantId) (err error) { + po := model.WebsocketInterface{} + copier.CopyWithOption(&po, data, copier.Option{DeepCopy: true}) + err = s.WebsocketInterfaceRepo.SaveDebugData(po, tenantId) + + err = s.WebsocketInterfaceRepo.SaveParams(data.Params, po.ID, tenantId) + err = s.WebsocketInterfaceRepo.SaveHeaders(data.Headers, po.ID, tenantId) + + return +} + +func (s *WebsocketInterfaceService) ListParams(tenantId consts.TenantId, id uint) ( + ret *[]domain.Param, err error) { + + ret = &[]domain.Param{} + + pos, _ := s.WebsocketInterfaceRepo.ListParams(tenantId, id) + + for _, po := range pos { + param := domain.Param{ + Name: po.Name, + Value: po.Value, + Disabled: po.Disabled, + Type: po.Type, + IsGlobal: false, + } + + (*ret) = append((*ret), param) + } + + return +} + +func (s *WebsocketInterfaceService) ListHeaders(tenantId consts.TenantId, id uint) ( + ret *[]domain.Header, err error) { + + ret = &[]domain.Header{} + + pos, _ := s.WebsocketInterfaceRepo.ListHeaders(tenantId, id) + + for _, po := range pos { + param := domain.Header{ + Name: po.Name, + Value: po.Value, + Disabled: po.Disabled, + Type: po.Type, + } + + (*ret) = append((*ret), param) + } + + return +} diff --git a/internal/server/modules/service/mock-advance.go b/internal/server/modules/service/mock-advance.go index a0ff4a4c1..71adb35c1 100644 --- a/internal/server/modules/service/mock-advance.go +++ b/internal/server/modules/service/mock-advance.go @@ -115,7 +115,7 @@ func (s *MockAdvanceService) ByScript(tenantId consts.TenantId, endpoint model.E return } - mockHelper.InitJsRuntime(tenantId) + mockHelper.InitJsRuntime(tenantId, endpoint.ProjectId) mockHelper.SetReqValueToGoja(req) mockHelper.SetRespValueToGoja(*resp) mockHelper.ExecScript(script.Content) diff --git a/internal/server/modules/service/scenario-exec.go b/internal/server/modules/service/scenario-exec.go index b48d30d62..f5692ce17 100644 --- a/internal/server/modules/service/scenario-exec.go +++ b/internal/server/modules/service/scenario-exec.go @@ -59,7 +59,6 @@ func (s *ScenarioExecService) LoadExecData(tenantId consts.TenantId, scenarioId, ret.ScenarioId = scenarioId ret.Name = scenario.Name ret.RootProcessor, _ = s.ScenarioNodeService.GetTree(tenantId, scenario, true) - ret.RootProcessor.Session = agentExec.Session{} // get variables s.SceneService.LoadEnvVarMapByScenario(tenantId, &ret.ExecScene, scenarioId, environmentId) diff --git a/internal/server/modules/service/scenario-node.go b/internal/server/modules/service/scenario-node.go index d2ef71369..2e6382348 100644 --- a/internal/server/modules/service/scenario-node.go +++ b/internal/server/modules/service/scenario-node.go @@ -50,8 +50,6 @@ func (s *ScenarioNodeService) GetTree(tenantId consts.TenantId, scenario model.S s.ScenarioNodeRepo.MakeTree(tenantId, tos[1:], root) - root.Session = agentExec.Session{} - root.ScenarioId = scenario.ID root.ProjectId = scenario.ProjectId @@ -62,7 +60,6 @@ func (s *ScenarioNodeService) ToTos(tenantId consts.TenantId, pos []*model.Proce for _, po := range pos { to := agentExec.Processor{ ProcessorBase: agentExec.ProcessorBase{ - Session: agentExec.Session{}, ScenarioId: po.ScenarioId, }, } diff --git a/internal/server/modules/service/snippet.go b/internal/server/modules/service/snippet.go index eb6ea3416..53549ed1d 100644 --- a/internal/server/modules/service/snippet.go +++ b/internal/server/modules/service/snippet.go @@ -1,8 +1,13 @@ package service import ( + "fmt" + serverDomain "github.com/aaronchen2k/deeptest/cmd/server/v1/domain" + agentExec "github.com/aaronchen2k/deeptest/internal/agent/exec" "github.com/aaronchen2k/deeptest/internal/pkg/consts" + "github.com/aaronchen2k/deeptest/internal/pkg/domain" jslibHelper "github.com/aaronchen2k/deeptest/internal/pkg/helper/jslib" + mockHelper "github.com/aaronchen2k/deeptest/internal/pkg/helper/mock" scriptHelper "github.com/aaronchen2k/deeptest/internal/pkg/helper/script" "github.com/aaronchen2k/deeptest/internal/server/modules/repo" fileUtils "github.com/aaronchen2k/deeptest/pkg/lib/file" @@ -16,8 +21,10 @@ var ( ) type SnippetService struct { - SnippetRepo *repo.SnippetRepo `inject:""` - JslibRepo *repo.JslibRepo `inject:""` + SnippetRepo *repo.SnippetRepo `inject:""` + JslibRepo *repo.JslibRepo `inject:""` + MockJsService *MockJsService `inject:""` + DebugInterfaceService *DebugInterfaceService `inject:""` } func (s *SnippetService) ListJslibNames(tenantId consts.TenantId, projectId int) (names []string, err error) { @@ -89,3 +96,64 @@ func (s *SnippetService) GetJslibsForAgent(tenantId consts.TenantId, loadedLibs return } + +func (s *SnippetService) ListVar(tenantId consts.TenantId, req domain.DebugInfo) (res []serverDomain.SnippetRes) { + data, err := s.DebugInterfaceService.Load(tenantId, req) + if err != nil { + return + } + + for _, item := range data.EnvDataToView.EnvVars { + expression := fmt.Sprintf("${%s}", item.Name) + res = append(res, serverDomain.SnippetRes{Label: item.Name, Value: expression, Desc: "环境变量"}) + } + + for _, item := range data.EnvDataToView.ShareVars { + expression := fmt.Sprintf("${%s}", item.Name) + res = append(res, serverDomain.SnippetRes{Label: item.Name, Value: expression, Desc: "共享变量"}) + } + + for _, item := range data.EnvDataToView.GlobalVars { + expression := fmt.Sprintf("${%s}", item.Name) + res = append(res, serverDomain.SnippetRes{Label: item.Name, Value: expression, Desc: "全局变量"}) + } + + return +} + +func (s *SnippetService) ListMock(tenantId consts.TenantId) (res []serverDomain.SnippetRes) { + list, _ := s.MockJsService.ListExpressions(tenantId) + for _, item := range list { + expression := fmt.Sprintf("${_mock('@%s')}", item.Expression) + res = append(res, serverDomain.SnippetRes{Label: "@" + item.Expression, Value: expression, Desc: item.Name}) + } + + return +} + +func (s *SnippetService) ListSysFunc() (res []serverDomain.SnippetRes) { + + for _, item := range agentExec.SysFuncList { + expression := fmt.Sprintf("${%s}", item.Value) + res = append(res, serverDomain.SnippetRes{Label: item.Label, Value: expression, Desc: item.Desc}) + } + + return +} + +func (s *SnippetService) ListCustomFunc(tenantId consts.TenantId, projectId uint) (res []serverDomain.SnippetRes) { + res = make([]serverDomain.SnippetRes, 0) + mockHelper.InitJsRuntime(tenantId, projectId) + + libs, _ := s.JslibRepo.List(tenantId, "", int(projectId), true) + for _, item := range libs { + jslib := jslibHelper.GetJslibCache(tenantId, item.ID) + for _, function := range jslib.Functions { + functionName := fmt.Sprintf("%s(%s)", function.Name, function.Args) + expression := fmt.Sprintf("${%s.%s}", item.Name, functionName) + res = append(res, serverDomain.SnippetRes{Label: functionName, Value: expression, Desc: item.Name}) + } + } + + return res +} diff --git a/pkg/lib/i118/i118.go b/pkg/lib/i118/i118.go index 2efb4fc5a..3c7edd8b2 100644 --- a/pkg/lib/i118/i118.go +++ b/pkg/lib/i118/i118.go @@ -16,10 +16,7 @@ func Init(lang string, app string) { //once.Do(func() { langRes := path.Join("res", lang, "messages.json") - fmt.Printf("path %s\n", langRes) - bytes, _ := deeptest.ReadResData(langRes) - fmt.Printf("content %s\n", string(bytes)) InitResFromAsset(bytes) diff --git a/pkg/lib/log/logger.go b/pkg/lib/log/logger.go new file mode 100644 index 000000000..ccd0b7361 --- /dev/null +++ b/pkg/lib/log/logger.go @@ -0,0 +1,9 @@ +package _logUtils + +import ( + "go.uber.org/zap" +) + +var Logger *zap.Logger + +// init in other places for server and agent diff --git a/pkg/lib/log/print.go b/pkg/lib/log/print.go index c461c4391..e8d1d9b50 100644 --- a/pkg/lib/log/print.go +++ b/pkg/lib/log/print.go @@ -2,51 +2,40 @@ package _logUtils import ( "fmt" - "go.uber.org/zap" "runtime/debug" ) -var Logger *zap.Logger - func Info(str string) { Logger.Info(str) - //log.Println(str) } func Infof(str string, args ...interface{}) { msg := fmt.Sprintf(str, args...) Logger.Info(msg) - //log.Printf(msg+"\n") } func Warn(str string) { Logger.Warn(str) - //log.Println(str) } func Warnf(str string, args ...interface{}) { msg := fmt.Sprintf(str, args...) Logger.Warn(msg) - //log.Printf(msg+"\n") } func Error(str string) { Logger.Error(str) s := string(debug.Stack()) fmt.Printf("err=%v, stack=%s\n", str, s) - //log.Println(str) } func Errorf(str string, args ...interface{}) { msg := fmt.Sprintf(str, args...) Logger.Error(msg) s := string(debug.Stack()) fmt.Printf("err=%v, stack=%s\n", msg, s) - //log.Printf(msg+"\n") } func Debug(str string) { Logger.Debug(str) - //log.Println(str) } func Debugf(str string, args ...interface{}) { msg := fmt.Sprintf(str, args...) Logger.Debug(msg) - //log.Printf(msg+"\n") } diff --git a/pkg/lib/string/json.go b/pkg/lib/string/json.go index ae5011019..7ae4f1d38 100644 --- a/pkg/lib/string/json.go +++ b/pkg/lib/string/json.go @@ -3,6 +3,7 @@ package _stringUtils import ( "bytes" "encoding/json" + "github.com/kataras/iris/v12" "strings" ) @@ -20,3 +21,18 @@ func JsonWithoutHtmlEscaped(obj interface{}) (ret string) { return } + +func FormatJsonStr(str string) (ret string) { + mp := iris.Map{} + json.Unmarshal([]byte(str), &mp) + + bytes, err := json.MarshalIndent(mp, "", " ") + + if err == nil { + ret = string(bytes) + } else { + ret = err.Error() + } + + return +} diff --git a/res/goja/export/custom.js b/res/goja/export/custom.js new file mode 100644 index 000000000..9a3ced2cb --- /dev/null +++ b/res/goja/export/custom.js @@ -0,0 +1,3 @@ +function _do_something(thing) { + return 'do ' + thing; +} \ No newline at end of file diff --git a/res/goja/export/deeptest-simple.js b/res/goja/export/deeptest-simple.js new file mode 100644 index 000000000..7024a4449 --- /dev/null +++ b/res/goja/export/deeptest-simple.js @@ -0,0 +1,11 @@ +// "use strict"; + +// datapool +const datapool = {}; + +datapool.get = getDatapoolVariable; +datapool.get.prototype = {}; + +module.exports = { // under dt. + datapool, +} diff --git a/res/goja/export/deeptest.js b/res/goja/export/deeptest.js index f7ef18759..d8eb39197 100644 --- a/res/goja/export/deeptest.js +++ b/res/goja/export/deeptest.js @@ -1,4 +1,4 @@ -"use strict"; +// "use strict"; // datapool const datapool = {}; @@ -25,4 +25,4 @@ module.exports = { // under dt. test: test, expect: expect, sendRequest: sendRequest, -} \ No newline at end of file +} diff --git a/res/server.yaml b/res/server.yaml new file mode 100644 index 000000000..a1258e3be --- /dev/null +++ b/res/server.yaml @@ -0,0 +1,75 @@ +captcha: + key-long: 6 + img-width: 240 + img-height: 80 +environment: + server-host: "" +ldap: false +limit: + disable: true + limit: 0 + burst: 5 +max-size: 1024 +maxsize: 1024 +mcs: + url: "" + mcsappid: "" + imappid: 0 + switch: false +mysql: + url: 127.0.0.1:3306 + config: charset=utf8mb4&parseTime=True&loc=Local + db-name: deeptest + username: root + password: P2ssw0rd + max-idle-conns: 0 + max-open-conns: 0 + log-mode: true + log-zap: "" +openapi: + appsecret: "" +redis: + db: 0 + addr: 127.0.0.1:6379 + password: "" + pool-size: 0 + prefix: "" +saas: + switch: false + username: "" + password: "" + url: "" + apiSign: + appKey: "" + appSecret: "" + host: "" +system: + name: "" + sysEnv: "" + level: debug + serverAddress: 0.0.0.0:8085 + agentAddress: "" + static-path: /static + web-path: ui/dist + db-type: mysql + cache-type: redis + time-format: "2006-01-02 15:04:05" +thirdparty: + username: "" + password: "" + url: "" + apiSign: + appKey: "" + appSecret: "" + host: "" + lcurl: "" +zap: + level: info + format: console + prefix: '[OP-ONLINE] ' + director: log + link-name: latest_log + show-line: true + encode-level: "" + stacktrace-key: stacktrace + log-in-console: true diff --git a/xdoc/js/md5.d.ts b/xdoc/js/md5.d.ts deleted file mode 100644 index f79ef5a75..000000000 --- a/xdoc/js/md5.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare function md5(str: string): string; \ No newline at end of file diff --git a/xdoc/js/md5.js b/xdoc/js/md5.js deleted file mode 100644 index 263b1c5f9..000000000 --- a/xdoc/js/md5.js +++ /dev/null @@ -1,402 +0,0 @@ -/* - * JavaScript MD5 - * https://github.com/blueimp/JavaScript-MD5 - * - * Copyright 2011, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * https://opensource.org/licenses/MIT - * - * Based on - * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message - * Digest Algorithm, as defined in RFC 1321. - * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * Distributed under the BSD License - * See http://pajhome.org.uk/crypt/md5 for more info. - */ - -/* global define */ - -/* eslint-disable strict */ - -;(function ($) { - 'use strict' - - /** - * Add integers, wrapping at 2^32. - * This uses 16-bit operations internally to work around bugs in interpreters. - * - * @param {number} x First integer - * @param {number} y Second integer - * @returns {number} Sum - */ - function safeAdd(x, y) { - var lsw = (x & 0xffff) + (y & 0xffff) - var msw = (x >> 16) + (y >> 16) + (lsw >> 16) - return (msw << 16) | (lsw & 0xffff) - } - - /** - * Bitwise rotate a 32-bit number to the left. - * - * @param {number} num 32-bit number - * @param {number} cnt Rotation count - * @returns {number} Rotated number - */ - function bitRotateLeft(num, cnt) { - return (num << cnt) | (num >>> (32 - cnt)) - } - - /** - * Basic operation the algorithm uses. - * - * @param {number} q q - * @param {number} a a - * @param {number} b b - * @param {number} x x - * @param {number} s s - * @param {number} t t - * @returns {number} Result - */ - function md5cmn(q, a, b, x, s, t) { - return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b) - } - /** - * Basic operation the algorithm uses. - * - * @param {number} a a - * @param {number} b b - * @param {number} c c - * @param {number} d d - * @param {number} x x - * @param {number} s s - * @param {number} t t - * @returns {number} Result - */ - function md5ff(a, b, c, d, x, s, t) { - return md5cmn((b & c) | (~b & d), a, b, x, s, t) - } - /** - * Basic operation the algorithm uses. - * - * @param {number} a a - * @param {number} b b - * @param {number} c c - * @param {number} d d - * @param {number} x x - * @param {number} s s - * @param {number} t t - * @returns {number} Result - */ - function md5gg(a, b, c, d, x, s, t) { - return md5cmn((b & d) | (c & ~d), a, b, x, s, t) - } - /** - * Basic operation the algorithm uses. - * - * @param {number} a a - * @param {number} b b - * @param {number} c c - * @param {number} d d - * @param {number} x x - * @param {number} s s - * @param {number} t t - * @returns {number} Result - */ - function md5hh(a, b, c, d, x, s, t) { - return md5cmn(b ^ c ^ d, a, b, x, s, t) - } - /** - * Basic operation the algorithm uses. - * - * @param {number} a a - * @param {number} b b - * @param {number} c c - * @param {number} d d - * @param {number} x x - * @param {number} s s - * @param {number} t t - * @returns {number} Result - */ - function md5ii(a, b, c, d, x, s, t) { - return md5cmn(c ^ (b | ~d), a, b, x, s, t) - } - - /** - * Calculate the MD5 of an array of little-endian words, and a bit length. - * - * @param {Array} x Array of little-endian words - * @param {number} len Bit length - * @returns {Array} MD5 Array - */ - function binlMD5(x, len) { - /* append padding */ - x[len >> 5] |= 0x80 << len % 32 - x[(((len + 64) >>> 9) << 4) + 14] = len - - var i - var olda - var oldb - var oldc - var oldd - var a = 1732584193 - var b = -271733879 - var c = -1732584194 - var d = 271733878 - - for (i = 0; i < x.length; i += 16) { - olda = a - oldb = b - oldc = c - oldd = d - - a = md5ff(a, b, c, d, x[i], 7, -680876936) - d = md5ff(d, a, b, c, x[i + 1], 12, -389564586) - c = md5ff(c, d, a, b, x[i + 2], 17, 606105819) - b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330) - a = md5ff(a, b, c, d, x[i + 4], 7, -176418897) - d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426) - c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341) - b = md5ff(b, c, d, a, x[i + 7], 22, -45705983) - a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416) - d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417) - c = md5ff(c, d, a, b, x[i + 10], 17, -42063) - b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162) - a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682) - d = md5ff(d, a, b, c, x[i + 13], 12, -40341101) - c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290) - b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329) - - a = md5gg(a, b, c, d, x[i + 1], 5, -165796510) - d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632) - c = md5gg(c, d, a, b, x[i + 11], 14, 643717713) - b = md5gg(b, c, d, a, x[i], 20, -373897302) - a = md5gg(a, b, c, d, x[i + 5], 5, -701558691) - d = md5gg(d, a, b, c, x[i + 10], 9, 38016083) - c = md5gg(c, d, a, b, x[i + 15], 14, -660478335) - b = md5gg(b, c, d, a, x[i + 4], 20, -405537848) - a = md5gg(a, b, c, d, x[i + 9], 5, 568446438) - d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690) - c = md5gg(c, d, a, b, x[i + 3], 14, -187363961) - b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501) - a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467) - d = md5gg(d, a, b, c, x[i + 2], 9, -51403784) - c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473) - b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734) - - a = md5hh(a, b, c, d, x[i + 5], 4, -378558) - d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463) - c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562) - b = md5hh(b, c, d, a, x[i + 14], 23, -35309556) - a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060) - d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353) - c = md5hh(c, d, a, b, x[i + 7], 16, -155497632) - b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640) - a = md5hh(a, b, c, d, x[i + 13], 4, 681279174) - d = md5hh(d, a, b, c, x[i], 11, -358537222) - c = md5hh(c, d, a, b, x[i + 3], 16, -722521979) - b = md5hh(b, c, d, a, x[i + 6], 23, 76029189) - a = md5hh(a, b, c, d, x[i + 9], 4, -640364487) - d = md5hh(d, a, b, c, x[i + 12], 11, -421815835) - c = md5hh(c, d, a, b, x[i + 15], 16, 530742520) - b = md5hh(b, c, d, a, x[i + 2], 23, -995338651) - - a = md5ii(a, b, c, d, x[i], 6, -198630844) - d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415) - c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905) - b = md5ii(b, c, d, a, x[i + 5], 21, -57434055) - a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571) - d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606) - c = md5ii(c, d, a, b, x[i + 10], 15, -1051523) - b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799) - a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359) - d = md5ii(d, a, b, c, x[i + 15], 10, -30611744) - c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380) - b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649) - a = md5ii(a, b, c, d, x[i + 4], 6, -145523070) - d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379) - c = md5ii(c, d, a, b, x[i + 2], 15, 718787259) - b = md5ii(b, c, d, a, x[i + 9], 21, -343485551) - - a = safeAdd(a, olda) - b = safeAdd(b, oldb) - c = safeAdd(c, oldc) - d = safeAdd(d, oldd) - } - return [a, b, c, d] - } - - /** - * Convert an array of little-endian words to a string - * - * @param {Array} input MD5 Array - * @returns {string} MD5 string - */ - function binl2rstr(input) { - var i - var output = '' - var length32 = input.length * 32 - for (i = 0; i < length32; i += 8) { - output += String.fromCharCode((input[i >> 5] >>> i % 32) & 0xff) - } - return output - } - - /** - * Convert a raw string to an array of little-endian words - * Characters >255 have their high-byte silently ignored. - * - * @param {string} input Raw input string - * @returns {Array} Array of little-endian words - */ - function rstr2binl(input) { - var i - var output = [] - output[(input.length >> 2) - 1] = undefined - for (i = 0; i < output.length; i += 1) { - output[i] = 0 - } - var length8 = input.length * 8 - for (i = 0; i < length8; i += 8) { - output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32 - } - return output - } - - /** - * Calculate the MD5 of a raw string - * - * @param {string} s Input string - * @returns {string} Raw MD5 string - */ - function rstrMD5(s) { - return binl2rstr(binlMD5(rstr2binl(s), s.length * 8)) - } - - /** - * Calculates the HMAC-MD5 of a key and some data (raw strings) - * - * @param {string} key HMAC key - * @param {string} data Raw input string - * @returns {string} Raw MD5 string - */ - function rstrHMACMD5(key, data) { - var i - var bkey = rstr2binl(key) - var ipad = [] - var opad = [] - var hash - ipad[15] = opad[15] = undefined - if (bkey.length > 16) { - bkey = binlMD5(bkey, key.length * 8) - } - for (i = 0; i < 16; i += 1) { - ipad[i] = bkey[i] ^ 0x36363636 - opad[i] = bkey[i] ^ 0x5c5c5c5c - } - hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8) - return binl2rstr(binlMD5(opad.concat(hash), 512 + 128)) - } - - /** - * Convert a raw string to a hex string - * - * @param {string} input Raw input string - * @returns {string} Hex encoded string - */ - function rstr2hex(input) { - var hexTab = '0123456789abcdef' - var output = '' - var x - var i - for (i = 0; i < input.length; i += 1) { - x = input.charCodeAt(i) - output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f) - } - return output - } - - /** - * Encode a string as UTF-8 - * - * @param {string} input Input string - * @returns {string} UTF8 string - */ - function str2rstrUTF8(input) { - return unescape(encodeURIComponent(input)) - } - - /** - * Encodes input string as raw MD5 string - * - * @param {string} s Input string - * @returns {string} Raw MD5 string - */ - function rawMD5(s) { - return rstrMD5(str2rstrUTF8(s)) - } - /** - * Encodes input string as Hex encoded string - * - * @param {string} s Input string - * @returns {string} Hex encoded string - */ - function hexMD5(s) { - return rstr2hex(rawMD5(s)) - } - /** - * Calculates the raw HMAC-MD5 for the given key and data - * - * @param {string} k HMAC key - * @param {string} d Input string - * @returns {string} Raw MD5 string - */ - function rawHMACMD5(k, d) { - return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d)) - } - /** - * Calculates the Hex encoded HMAC-MD5 for the given key and data - * - * @param {string} k HMAC key - * @param {string} d Input string - * @returns {string} Raw MD5 string - */ - function hexHMACMD5(k, d) { - return rstr2hex(rawHMACMD5(k, d)) - } - - /** - * Calculates MD5 value for a given string. - * If a key is provided, calculates the HMAC-MD5 value. - * Returns a Hex encoded string unless the raw argument is given. - * - * @param {string} string Input string - * @param {string} [key] HMAC key - * @param {boolean} [raw] Raw output switch - * @returns {string} MD5 output - */ - function md5(string, key, raw) { - if (!key) { - if (!raw) { - return hexMD5(string) - } - return rawMD5(string) - } - if (!raw) { - return hexHMACMD5(key, string) - } - return rawHMACMD5(key, string) - } - - if (typeof define === 'function' && define.amd) { - define(function () { - return md5 - }) - } else if (typeof module === 'object' && module.exports) { - module.exports = md5 - } else { - $.md5 = md5 - } -})(this) \ No newline at end of file diff --git a/xdoc/notes.txt b/xdoc/notes.txt index e01ec4bf2..49a3da64a 100644 --- a/xdoc/notes.txt +++ b/xdoc/notes.txt @@ -13,6 +13,7 @@ protoc --go_out=. \ greater/greater.proto redis-server /usr/local/etc/redis.conf +redis-cli shutdown npm i postman-to-openapi -g p2o xdoc/openapi/postman/v2/SimplePost.json -f xdoc/openapi/out/simple-post-v2.yml -o xdoc/openapi/options.json @@ -68,8 +69,27 @@ scp -r ~/work/qiniu/deeptest/* lighthouse@111.231.16.35:~/work/qiniu/deeptest nohup light-server -s ~/work/qiniu/deeptest > ~/light-server.log 2>&1 & git push -f github master:main - -make compile_ui_demo -ENV=dp make compile_server_linux -ENV=dp make compile_agent_linux - +git push -f github feature/chenqi99/var_in_params:feature/chenqi99/var_in_params + +ENV=dp rm -rf bin && make default + +ENV=dp make win64 +ENV=dp make win32 +ENV=dp make linux +ENV=dp make mac + +测试: +基本Websocket + ws://111.231.16.35:9090/ws +扩展Websocket + ws://localhost:8085/api/v1/ws + TestNamespace / MyRoom +gRPC + 111.231.16.35:9528 + +表达式 + ${Math.random() + +int2 + ' AND ' + g_var1} 和 ${dt.datapool.get('dp1', 'name', '1')} + ${g_var1} 加 ${_mock("@date('yyyy-MM-dd')")} 加 ${mymath.add(1,2)} + ${eval("'url is ' + escape('https://baidu.com')")} 加 ${x} + ${_do_something('test ' + g_var1 + ' ' + escape('https://baidu.com'))} + int2=='101' && function(x) {return x + 100} (6) == 106 diff --git a/xdoc/test/test.proto b/xdoc/test/test.proto new file mode 100644 index 000000000..37f718977 --- /dev/null +++ b/xdoc/test/test.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; +package ptproto; +option go_package = "/dtproto"; + +service TestService { + rpc TestBidi (stream TestRequest) returns (stream TestResponse){}; + rpc TestServerStream (TestRequest) returns (stream TestResponse){}; + rpc TestClientStream (stream TestRequest) returns (TestResponse){}; + rpc TestUnary (TestRequest) returns (TestResponse); +} + +message TestRequest { + string action = 1; + string data = 2; +} + +message TestResponse { + int32 code = 1; + string result = 2; +} + +message TestData { + string username = 1; + string email = 2; +} \ No newline at end of file