From 5dd07611f38664fb0b8793be1474acbd33c0545f Mon Sep 17 00:00:00 2001 From: wxg0103 <727495428@qq.com> Date: Thu, 1 Dec 2022 15:54:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=AE=9A=E4=B9=89=E5=A2=9E=E5=8A=A0=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E9=87=8D=E5=AE=9A=E5=90=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --story=1010712 --user=王孝刚 【接口测试】MS接口测试目前只支持跟随重定向,不支持自动重定向,需提供支持 #19501 https://www.tapd.cn/55049933/s/1309581 --- .../request/sampler/MsHTTPSamplerProxy.java | 2 + .../request/http/ApiAdvancedConfig.vue | 19 +- api-test/frontend/src/i18n/lang/en-US.js | 3 + api-test/frontend/src/i18n/lang/zh-CN.js | 3 + api-test/frontend/src/i18n/lang/zh-TW.js | 3 + .../frontend/src/model/ApiTestModel.js | 512 ++++++++++------ .../frontend/src/model/EnvTestModel.js | 513 ++++++++++------ .../sdk-parent/frontend/src/model/JMX.js | 571 +++++++++++------- .../frontend/src/model/ScenarioModel.js | 553 ++++++++++------- .../log/vo/api/DefinitionReference.java | 1 + 10 files changed, 1393 insertions(+), 787 deletions(-) diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java index f2391cb22d..92252ab7f2 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java @@ -76,6 +76,7 @@ public class MsHTTPSamplerProxy extends MsTestElement { private List rest; private String url; private boolean followRedirects; + private boolean autoRedirects; private boolean doMultipartPost; private String useEnvironment; private List arguments; @@ -123,6 +124,7 @@ public class MsHTTPSamplerProxy extends MsTestElement { sampler.setMethod(this.getMethod()); sampler.setContentEncoding(StandardCharsets.UTF_8.name()); sampler.setFollowRedirects(this.isFollowRedirects()); + sampler.setAutoRedirects(this.isAutoRedirects()); sampler.setUseKeepAlive(true); sampler.setDoMultipart(this.isDoMultipartPost()); if (config.getConfig() == null) { diff --git a/api-test/frontend/src/business/definition/components/request/http/ApiAdvancedConfig.vue b/api-test/frontend/src/business/definition/components/request/http/ApiAdvancedConfig.vue index 9615e68662..80a65020d9 100644 --- a/api-test/frontend/src/business/definition/components/request/http/ApiAdvancedConfig.vue +++ b/api-test/frontend/src/business/definition/components/request/http/ApiAdvancedConfig.vue @@ -30,10 +30,15 @@ - + + + @@ -48,6 +53,18 @@ export default { default: false, }, }, + methods: { + changeFollow() { + if (this.request.followRedirects) { + this.request.autoRedirects = false; + } + }, + changeAuto() { + if (this.request.autoRedirects) { + this.request.followRedirects = false; + } + }, + }, }; diff --git a/api-test/frontend/src/i18n/lang/en-US.js b/api-test/frontend/src/i18n/lang/en-US.js index 83b1890d7e..3a2bd619c4 100644 --- a/api-test/frontend/src/i18n/lang/en-US.js +++ b/api-test/frontend/src/i18n/lang/en-US.js @@ -28,6 +28,9 @@ const message = { plan_count: '{0} [test plan]', case_is_referenced: '{0} cases have reference relationships', scenario_is_referenced: '{0} scenarios have reference relationships', + request: { + auto_redirects: 'Auto redirects', + }, }, home: { dashboard: { diff --git a/api-test/frontend/src/i18n/lang/zh-CN.js b/api-test/frontend/src/i18n/lang/zh-CN.js index 4e1a61a643..ecadf4a1da 100644 --- a/api-test/frontend/src/i18n/lang/zh-CN.js +++ b/api-test/frontend/src/i18n/lang/zh-CN.js @@ -28,6 +28,9 @@ const message = { plan_count: '{0}个[测试计划]', case_is_referenced: '有{0}个用例存在引用关系', scenario_is_referenced: '有{0}个场景存在引用关系', + request: { + auto_redirects: '自动重定向', + }, }, home: { dashboard: { diff --git a/api-test/frontend/src/i18n/lang/zh-TW.js b/api-test/frontend/src/i18n/lang/zh-TW.js index 4ea954a626..fd5ea27bbd 100644 --- a/api-test/frontend/src/i18n/lang/zh-TW.js +++ b/api-test/frontend/src/i18n/lang/zh-TW.js @@ -28,6 +28,9 @@ const message = { plan_count: '{0}個[测试計劃]', case_is_referenced: '有{0}個用例存在引用關係', scenario_is_referenced: '有{0}個場景存在引用關係', + request: { + auto_redirects: '自動重定向', + }, }, home: { dashboard: { diff --git a/framework/sdk-parent/frontend/src/model/ApiTestModel.js b/framework/sdk-parent/frontend/src/model/ApiTestModel.js index 031afbe5c6..ade34fe714 100644 --- a/framework/sdk-parent/frontend/src/model/ApiTestModel.js +++ b/framework/sdk-parent/frontend/src/model/ApiTestModel.js @@ -29,12 +29,12 @@ import { XPath2Extractor, } from "./JMX"; import Mock from "mockjs"; -import {funcFilters} from "../utils/func-filter"; +import { funcFilters } from "../utils/func-filter"; export const uuid = function () { - let d = new Date().getTime() - let d2 = (performance && performance.now && (performance.now() * 1000)) || 0; - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + let d = new Date().getTime(); + let d2 = (performance && performance.now && performance.now() * 1000) || 0; + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { let r = Math.random() * 16; if (d > 0) { r = (d + r) % 16 | 0; @@ -43,9 +43,9 @@ export const uuid = function () { r = (d2 + r) % 16 | 0; d2 = Math.floor(d2 / 16); } - return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); + return (c === "x" ? r : (r & 0x3) | 0x8).toString(16); }); -} +}; export const BODY_FILE_DIR = "/opt/metersphere/data/body"; //存放body文件上传目录 @@ -76,7 +76,7 @@ export const calculate = function (itemValue) { } catch (e) { return itemValue; } -} +}; export const BODY_TYPE = { KV: "KeyValue", @@ -85,15 +85,15 @@ export const BODY_TYPE = { WWW_FORM: "WWW_FORM", XML: "XML", BINARY: "BINARY", - JSON: "JSON" -} + JSON: "JSON", +}; export const BODY_FORMAT = { TEXT: "text", JSON: "json", XML: "xml", HTML: "html", -} +}; export const ASSERTION_TYPE = { TEXT: "Text", @@ -103,29 +103,29 @@ export const ASSERTION_TYPE = { JSR223: "JSR223", XPATH2: "XPath2", DOCUMENT: "Document", -} +}; export const ASSERTION_REGEX_SUBJECT = { RESPONSE_CODE: "Response Code", RESPONSE_HEADERS: "Response Headers", - RESPONSE_DATA: "Response Data" -} + RESPONSE_DATA: "Response Data", +}; export const EXTRACT_TYPE = { REGEX: "Regex", JSON_PATH: "JSONPath", - XPATH: "XPath" -} + XPATH: "XPath", +}; export class BaseConfig { - set(options, notUndefined) { - options = this.initOptions(options) + options = this.initOptions(options); for (let name in options) { if (Object.prototype.hasOwnProperty.call(options, name)) { if (!(this[name] instanceof Array)) { if (notUndefined === true) { - this[name] = options[name] === undefined ? this[name] : options[name]; + this[name] = + options[name] === undefined ? this[name] : options[name]; } else { this[name] = options[name]; } @@ -135,13 +135,16 @@ export class BaseConfig { } sets(types, options) { - options = this.initOptions(options) + options = this.initOptions(options); if (types) { for (let name in types) { - if (Object.prototype.hasOwnProperty.call(types, name) && Object.prototype.hasOwnProperty.call(options, name)) { - options[name].forEach(o => { + if ( + Object.prototype.hasOwnProperty.call(types, name) && + Object.prototype.hasOwnProperty.call(options, name) + ) { + options[name].forEach((o) => { this[name].push(new types[name](o)); - }) + }); } } } @@ -160,7 +163,7 @@ export class Test extends BaseConfig { constructor(options) { super(); this.type = "MS API CONFIG"; - this.version = '1.4.0'; + this.version = "1.4.0"; this.id = uuid(); this.name = undefined; this.projectId = undefined; @@ -179,21 +182,21 @@ export class Test extends BaseConfig { if (!this.projectId) { return { isValid: false, - info: 'api_test.select_project' - } + info: "api_test.select_project", + }; } else if (!this.name) { return { isValid: false, - info: 'api_test.input_name' - } + info: "api_test.input_name", + }; } - return {isValid: true}; + return { isValid: true }; } toJMX() { return { - name: this.name + '.jmx', - xml: new JMXGenerator(this).toXML() + name: this.name + ".jmx", + xml: new JMXGenerator(this).toXML(), }; } } @@ -225,12 +228,15 @@ export class Scenario extends BaseConfig { this.databaseConfigs = []; this.tcpConfig = undefined; this.set(options); - this.sets({ - variables: KeyValue, - headers: KeyValue, - requests: RequestFactory, - databaseConfigs: DatabaseConfig - }, options); + this.sets( + { + variables: KeyValue, + headers: KeyValue, + requests: RequestFactory, + databaseConfigs: DatabaseConfig, + }, + options + ); } initOptions(options = {}) { @@ -251,25 +257,28 @@ export class Scenario extends BaseConfig { isValid() { if (this.enable) { for (let i = 0; i < this.requests.length; i++) { - let validator = this.requests[i].isValid(this.environmentId, this.environment); + let validator = this.requests[i].isValid( + this.environmentId, + this.environment + ); if (!validator.isValid) { return validator; } } } - return {isValid: true}; + return { isValid: true }; } isReference() { - return this.id.indexOf("#") !== -1 + return this.id.indexOf("#") !== -1; } } class DubboConfig extends BaseConfig { constructor(options = {}) { super(); - this.configCenter = new ConfigCenter(options.configCenter) - this.registryCenter = new RegistryCenter(options.registryCenter) + this.configCenter = new ConfigCenter(options.configCenter); + this.registryCenter = new RegistryCenter(options.registryCenter); if (options.consumerAndService === undefined) { options.consumerAndService = { timeout: undefined, @@ -279,10 +288,12 @@ class DubboConfig extends BaseConfig { group: undefined, connections: undefined, async: undefined, - loadBalance: undefined - } + loadBalance: undefined, + }; } - this.consumerAndService = new ConsumerAndService(options.consumerAndService) + this.consumerAndService = new ConsumerAndService( + options.consumerAndService + ); } } @@ -292,10 +303,10 @@ export class RequestFactory { DUBBO: "DUBBO", SQL: "SQL", TCP: "TCP", - } + }; constructor(options = {}) { - options.type = options.type || RequestFactory.TYPES.HTTP + options.type = options.type || RequestFactory.TYPES.HTTP; switch (options.type) { case RequestFactory.TYPES.DUBBO: return new DubboRequest(options); @@ -315,10 +326,10 @@ export class ResponseFactory { DUBBO: "DUBBO", SQL: "SQL", TCP: "TCP", - } + }; constructor(options = {}) { - options.type = options.type || ResponseFactory.TYPES.HTTP + options.type = options.type || ResponseFactory.TYPES.HTTP; switch (options.type) { case RequestFactory.TYPES.DUBBO: return new DubboRequest(options); @@ -364,7 +375,7 @@ export class HttpRequest extends Request { this.method = options.method || "GET"; this.parameters = []; this.rest = []; - this.authConfig = {verification: "No Auth", isEncrypt: false}; + this.authConfig = { verification: "No Auth", isEncrypt: false }; this.headers = []; this.body = new Body(options.body); this.environment = options.environment; @@ -373,9 +384,15 @@ export class HttpRequest extends Request { this.doMultipartPost = options.doMultipartPost; this.connectTimeout = options.connectTimeout || 10 * 1000; this.responseTimeout = options.responseTimeout || 10 * 1000; - this.followRedirects = options.followRedirects === undefined ? true : options.followRedirects; + this.followRedirects = + options.followRedirects === undefined ? true : options.followRedirects; + this.autoRedirects = + options.autoRedirects === undefined ? true : options.autoRedirects; - this.sets({parameters: KeyValue, rest: KeyValue, headers: KeyValue}, options); + this.sets( + { parameters: KeyValue, rest: KeyValue, headers: KeyValue }, + options + ); } isValid(environmentId, environment) { @@ -384,35 +401,35 @@ export class HttpRequest extends Request { if (!environmentId) { return { isValid: false, - info: 'api_test.request.please_configure_environment_in_scenario' - } + info: "api_test.request.please_configure_environment_in_scenario", + }; } if (!environment.config.httpConfig.socket) { return { isValid: false, - info: 'api_test.request.please_configure_socket_in_environment' - } + info: "api_test.request.please_configure_socket_in_environment", + }; } } else { if (!this.url) { return { isValid: false, - info: 'api_test.request.input_url' - } + info: "api_test.request.input_url", + }; } try { - new URL(this.url) + new URL(this.url); } catch (e) { return { isValid: false, - info: 'api_test.request.url_invalid' - } + info: "api_test.request.url_invalid", + }; } } } return { - isValid: true - } + isValid: true, + }; } showType() { @@ -422,10 +439,8 @@ export class HttpRequest extends Request { showMethod() { return this.method.toUpperCase(); } - } - export class Response extends BaseConfig { constructor(type, options = {}) { super(); @@ -440,14 +455,13 @@ export class Response extends BaseConfig { } } - export class HttpResponse extends Response { constructor(options) { super(ResponseFactory.TYPES.HTTP, options); this.headers = []; this.body = new Body(options.body); this.statusCode = []; - this.sets({statusCode: KeyValue, headers: KeyValue}, options); + this.sets({ statusCode: KeyValue, headers: KeyValue }, options); } } @@ -455,7 +469,7 @@ export class DubboRequest extends Request { static PROTOCOLS = { DUBBO: "dubbo://", RMI: "rmi://", - } + }; constructor(options = {}) { super(RequestFactory.TYPES.DUBBO, options); @@ -464,14 +478,16 @@ export class DubboRequest extends Request { this.method = options.method; this.configCenter = new ConfigCenter(options.configCenter); this.registryCenter = new RegistryCenter(options.registryCenter); - this.consumerAndService = new ConsumerAndService(options.consumerAndService); + this.consumerAndService = new ConsumerAndService( + options.consumerAndService + ); this.args = []; this.attachmentArgs = []; // Scenario.dubboConfig this.dubboConfig = undefined; this.debugReport = undefined; - this.sets({args: KeyValue, attachmentArgs: KeyValue}, options); + this.sets({ args: KeyValue, attachmentArgs: KeyValue }, options); } isValid() { @@ -479,31 +495,31 @@ export class DubboRequest extends Request { if (!this.interface) { return { isValid: false, - info: 'api_test.request.dubbo.input_interface' - } + info: "api_test.request.dubbo.input_interface", + }; } if (!this.method) { return { isValid: false, - info: 'api_test.request.dubbo.input_method' - } + info: "api_test.request.dubbo.input_method", + }; } if (!this.registryCenter.isValid()) { return { isValid: false, - info: 'api_test.request.dubbo.input_registry_center' - } + info: "api_test.request.dubbo.input_registry_center", + }; } if (!this.consumerAndService.isValid()) { return { isValid: false, - info: 'api_test.request.dubbo.input_consumer_service' - } + info: "api_test.request.dubbo.input_consumer_service", + }; } } return { - isValid: true - } + isValid: true, + }; } showType() { @@ -521,7 +537,6 @@ export class DubboRequest extends Request { } export class SqlRequest extends Request { - constructor(options = {}) { super(RequestFactory.TYPES.SQL, options); this.useEnvironment = options.useEnvironment; @@ -534,7 +549,10 @@ export class SqlRequest extends Request { // this.queryType = options.queryType; this.queryTimeout = options.queryTimeout || 60000; - this.sets({args: KeyValue, attachmentArgs: KeyValue, variables: KeyValue}, options); + this.sets( + { args: KeyValue, attachmentArgs: KeyValue, variables: KeyValue }, + options + ); } isValid() { @@ -542,19 +560,19 @@ export class SqlRequest extends Request { if (!this.name) { return { isValid: false, - info: 'api_test.request.sql.name_cannot_be_empty' - } + info: "api_test.request.sql.name_cannot_be_empty", + }; } if (!this.dataSource) { return { isValid: false, - info: 'api_test.request.sql.dataSource_cannot_be_empty' - } + info: "api_test.request.sql.dataSource_cannot_be_empty", + }; } } return { - isValid: true - } + isValid: true, + }; } showType() { @@ -571,7 +589,11 @@ export class SqlRequest extends Request { } export class TCPConfig extends BaseConfig { - static CLASSES = ["TCPClientImpl", "BinaryTCPClientImpl", "LengthPrefixedBinaryTCPClientImpl"] + static CLASSES = [ + "TCPClientImpl", + "BinaryTCPClientImpl", + "LengthPrefixedBinaryTCPClientImpl", + ]; constructor(options = {}) { super(); @@ -581,9 +603,11 @@ export class TCPConfig extends BaseConfig { this.ctimeout = options.ctimeout; // Connect this.timeout = options.timeout; // Response - this.reUseConnection = options.reUseConnection === undefined ? true : options.reUseConnection; + this.reUseConnection = + options.reUseConnection === undefined ? true : options.reUseConnection; this.nodelay = options.nodelay === undefined ? false : options.nodelay; - this.closeConnection = options.closeConnection === undefined ? false : options.closeConnection; + this.closeConnection = + options.closeConnection === undefined ? false : options.closeConnection; this.soLinger = options.soLinger; this.eolByte = options.eolByte; @@ -601,15 +625,15 @@ export class TCPRequest extends Request { //设置TCPConfig的属性 this.set(new TCPConfig(options)); - this.sets({parameters: KeyValue}, options); + this.sets({ parameters: KeyValue }, options); this.request = options.request; } isValid() { return { - isValid: true - } + isValid: true, + }; } showType() { @@ -625,7 +649,6 @@ export class TCPRequest extends Request { } } - export class ConfigCenter extends BaseConfig { static PROTOCOLS = ["zookeeper", "nacos", "apollo"]; @@ -643,12 +666,25 @@ export class ConfigCenter extends BaseConfig { } isValid() { - return !!this.protocol || !!this.group || !!this.namespace || !!this.username || !!this.address || !!this.password || !!this.timeout; + return ( + !!this.protocol || + !!this.group || + !!this.namespace || + !!this.username || + !!this.address || + !!this.password || + !!this.timeout + ); } } export class DatabaseConfig extends BaseConfig { - static DRIVER_CLASS = ["com.mysql.jdbc.Driver", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "org.postgresql.Driver", "oracle.jdbc.OracleDriver"]; + static DRIVER_CLASS = [ + "com.mysql.jdbc.Driver", + "com.microsoft.sqlserver.jdbc.SQLServerDriver", + "org.postgresql.Driver", + "oracle.jdbc.OracleDriver", + ]; constructor(options) { super(); @@ -670,12 +706,28 @@ export class DatabaseConfig extends BaseConfig { } isValid() { - return !!this.name || !!this.poolMax || !!this.timeout || !!this.driver || !!this.dbUrl || !!this.username || !!this.password; + return ( + !!this.name || + !!this.poolMax || + !!this.timeout || + !!this.driver || + !!this.dbUrl || + !!this.username || + !!this.password + ); } } export class RegistryCenter extends BaseConfig { - static PROTOCOLS = ["none", "zookeeper", "nacos", "apollo", "multicast", "redis", "simple"]; + static PROTOCOLS = [ + "none", + "zookeeper", + "nacos", + "apollo", + "multicast", + "redis", + "simple", + ]; constructor(options) { super(); @@ -690,13 +742,25 @@ export class RegistryCenter extends BaseConfig { } isValid() { - return !!this.protocol || !!this.group || !!this.username || !!this.address || !!this.password || !!this.timeout; + return ( + !!this.protocol || + !!this.group || + !!this.username || + !!this.address || + !!this.password || + !!this.timeout + ); } } export class ConsumerAndService extends BaseConfig { static ASYNC_OPTIONS = ["sync", "async"]; - static LOAD_BALANCE_OPTIONS = ["random", "roundrobin", "leastactive", "consistenthash"]; + static LOAD_BALANCE_OPTIONS = [ + "random", + "roundrobin", + "leastactive", + "consistenthash", + ]; constructor(options) { super(); @@ -713,7 +777,16 @@ export class ConsumerAndService extends BaseConfig { } isValid() { - return !!this.timeout || !!this.version || !!this.retries || !!this.cluster || !!this.group || !!this.connections || !!this.async || !!this.loadBalance; + return ( + !!this.timeout || + !!this.version || + !!this.retries || + !!this.cluster || + !!this.group || + !!this.connections || + !!this.async || + !!this.loadBalance + ); } } @@ -725,21 +798,25 @@ export class Body extends BaseConfig { this.kvs = []; this.binary = []; this.set(options); - this.sets({kvs: KeyValue}, {binary: KeyValue}, options); + this.sets({ kvs: KeyValue }, { binary: KeyValue }, options); } isValid() { if (this.isKV()) { - return this.kvs.some(kv => { + return this.kvs.some((kv) => { return kv.isValid(); - }) + }); } else { return !!this.raw; } } isKV() { - return [BODY_TYPE.FORM_DATA, BODY_TYPE.WWW_FORM, BODY_TYPE.BINARY].indexOf(this.type) > 0; + return ( + [BODY_TYPE.FORM_DATA, BODY_TYPE.WWW_FORM, BODY_TYPE.BINARY].indexOf( + this.type + ) > 0 + ); } } @@ -761,11 +838,11 @@ export class KeyValue extends BaseConfig { } isValid() { - return (!!this.name || !!this.value) && this.type !== 'file'; + return (!!this.name || !!this.value) && this.type !== "file"; } isFile() { - return (!!this.name || !!this.value) && this.type === 'file'; + return (!!this.name || !!this.value) && this.type === "file"; } } @@ -781,9 +858,22 @@ export class Assertions extends BaseConfig { this.xpath2 = []; this.duration = undefined; this.enable = true; - this.document = {type: "JSON", data: {xmlFollowAPI: false, jsonFollowAPI: false, json: [], xml: []}, enable: true}; + this.document = { + type: "JSON", + data: { xmlFollowAPI: false, jsonFollowAPI: false, json: [], xml: [] }, + enable: true, + }; this.set(options); - this.sets({text: Text, regex: Regex, jsonPath: JSONPath, jsr223: AssertionJSR223, xpath2: XPath2}, options); + this.sets( + { + text: Text, + regex: Regex, + jsonPath: JSONPath, + jsr223: AssertionJSR223, + xpath2: XPath2, + }, + options + ); } initOptions(options) { @@ -856,7 +946,6 @@ export class Text extends AssertionType { } } - export class BeanShellProcessor extends BaseConfig { constructor(options) { super(); @@ -865,7 +954,6 @@ export class BeanShellProcessor extends BaseConfig { } } - export class JSR223Processor extends BaseConfig { constructor(options) { super(); @@ -895,7 +983,6 @@ export class JDBCProcessor extends BaseConfig { } } - export class Regex extends AssertionType { constructor(options) { super(ASSERTION_TYPE.REGEX); @@ -924,7 +1011,8 @@ export class JSONPath extends AssertionType { } setJSONPathDescription() { - this.description = this.expression + " expect: " + (this.expect ? this.expect : ''); + this.description = + this.expression + " expect: " + (this.expect ? this.expect : ""); } isValid() { @@ -946,7 +1034,6 @@ export class XPath2 extends AssertionType { } } - export class Duration extends AssertionType { constructor(options) { super(ASSERTION_TYPE.DURATION); @@ -965,7 +1052,7 @@ export class Extract extends BaseConfig { super(); this.resourceId = uuid(); this.type = "Extract"; - this.xpathType = 'html'; + this.xpathType = "html"; this.regex = []; this.json = []; this.xpath = []; @@ -974,8 +1061,8 @@ export class Extract extends BaseConfig { let types = { json: ExtractJSONPath, xpath: ExtractXPath, - regex: ExtractRegex - } + regex: ExtractRegex, + }; this.sets(types, options); } } @@ -1025,11 +1112,11 @@ export class ExtractXPath extends ExtractCommon { export class Controller extends BaseConfig { static TYPES = { IF_CONTROLLER: "If Controller", - } + }; constructor(type, options = {}) { super(); - this.type = type + this.type = type; options.id = options.id || uuid(); options.resourceId = options.resourceId || uuid(); options.enable = options.enable === undefined ? true : options.enable; @@ -1099,9 +1186,25 @@ export class LoopController extends Controller { this.type = "LoopController"; this.active = false; this.loopType = "LOOP_COUNT"; - this.countController = {loops: 0, interval: 0, proceed: true, requestResult: {}}; - this.forEachController = {inputVal: "", returnVal: "", interval: 0, requestResult: {}}; - this.whileController = {variable: "", operator: "", value: "", timeout: 0, requestResult: {}}; + this.countController = { + loops: 0, + interval: 0, + proceed: true, + requestResult: {}, + }; + this.forEachController = { + inputVal: "", + returnVal: "", + interval: 0, + requestResult: {}, + }; + this.whileController = { + variable: "", + operator: "", + value: "", + timeout: 0, + requestResult: {}, + }; this.hashTree = []; this.set(options); } @@ -1145,7 +1248,7 @@ export class TransactionController extends Controller { label() { if (this.isValid()) { - let label = this.$t('api_test.automation.transaction_controller'); + let label = this.$t("api_test.automation.transaction_controller"); if (this.name != null && this.name !== "") { label = this.name; } @@ -1155,11 +1258,10 @@ export class TransactionController extends Controller { } } - export class Timer extends BaseConfig { static TYPES = { CONSTANT_TIMER: "Constant Timer", - } + }; constructor(type, options = {}) { super(); @@ -1198,8 +1300,8 @@ const JMX_ASSERTION_CONDITION = { NOT: 1 << 2, EQUALS: 1 << 3, SUBSTRING: 1 << 4, - OR: 1 << 5 -} + OR: 1 << 5, +}; class JMXHttpRequest { constructor(request, environment) { @@ -1207,24 +1309,38 @@ class JMXHttpRequest { this.useEnvironment = request.useEnvironment; this.method = request.method; if (!request.useEnvironment) { - if (!request.url.startsWith("http://") && !request.url.startsWith("https://")) { - request.url = 'http://' + request.url; + if ( + !request.url.startsWith("http://") && + !request.url.startsWith("https://") + ) { + request.url = "http://" + request.url; } let url = new URL(request.url); this.domain = decodeURIComponent(url.hostname); this.port = url.port; this.protocol = url.protocol.split(":")[0]; - this.path = this.getPostQueryParameters(request, decodeURIComponent(url.pathname)); + this.path = this.getPostQueryParameters( + request, + decodeURIComponent(url.pathname) + ); } else { this.domain = environment.config.httpConfig.domain; this.port = environment.config.httpConfig.port; this.protocol = environment.config.httpConfig.protocol; - let url = new URL(environment.config.httpConfig.protocol + "://" + environment.config.httpConfig.socket); - this.path = this.getPostQueryParameters(request, decodeURIComponent(url.pathname + (request.path ? request.path : ''))); + let url = new URL( + environment.config.httpConfig.protocol + + "://" + + environment.config.httpConfig.socket + ); + this.path = this.getPostQueryParameters( + request, + decodeURIComponent(url.pathname + (request.path ? request.path : "")) + ); } this.connectTimeout = request.connectTimeout; this.responseTimeout = request.responseTimeout; this.followRedirects = request.followRedirects; + this.autoRedirects = request.autoRedirects; this.doMultipartPost = request.doMultipartPost; } } @@ -1232,19 +1348,19 @@ class JMXHttpRequest { getPostQueryParameters(request, path) { if (this.method.toUpperCase() !== "GET") { let parameters = []; - request.parameters.forEach(parameter => { + request.parameters.forEach((parameter) => { if (parameter.name && parameter.value && parameter.enable === true) { parameters.push(parameter); } }); if (parameters.length > 0) { - path += '?'; + path += "?"; } for (let i = 0; i < parameters.length; i++) { let parameter = parameters[i]; - path += (parameter.name + '=' + parameter.value); + path += parameter.name + "=" + parameter.value; if (i !== parameters.length - 1) { - path += '&'; + path += "&"; } } } @@ -1257,10 +1373,10 @@ class JMXDubboRequest { // Request 复制 let obj = request.clone(); // 去掉无效的kv - obj.args = obj.args.filter(arg => { + obj.args = obj.args.filter((arg) => { return arg.isValid(); }); - obj.attachmentArgs = obj.attachmentArgs.filter(arg => { + obj.attachmentArgs = obj.attachmentArgs.filter((arg) => { return arg.isValid(); }); return obj; @@ -1299,8 +1415,10 @@ class JMXTCPRequest { class JMeterTestPlan extends Element { constructor() { - super('jmeterTestPlan', { - version: "1.2", properties: "5.0", jmeter: "5.2.1" + super("jmeterTestPlan", { + version: "1.2", + properties: "5.0", + jmeter: "5.2.1", }); this.add(new HashTree()); @@ -1325,15 +1443,20 @@ class JMXGenerator { } addScenarios(testPlan, testId, request) { - let threadGroup = new ThreadGroup(request.name || ""); if (!request.isValid()) return; let sampler; if (request instanceof DubboRequest) { - sampler = new DubboSample(request.name || "", new JMXDubboRequest(request)); + sampler = new DubboSample( + request.name || "", + new JMXDubboRequest(request) + ); } else if (request instanceof HttpRequest) { - sampler = new HTTPSamplerProxy(request.name || "", new JMXHttpRequest(request, false)); + sampler = new HTTPSamplerProxy( + request.name || "", + new JMXHttpRequest(request, false) + ); this.addRequestHeader(sampler, request); this.addRequestArguments(sampler, request); this.addRequestBody(sampler, request, testId); @@ -1354,7 +1477,11 @@ class JMXGenerator { this.addConstantsTimer(sampler, request); - if (request.controller && request.controller.isValid() && request.controller.enable) { + if ( + request.controller && + request.controller.isValid() && + request.controller.enable + ) { if (request.controller instanceof IfController) { let controller = this.getController(sampler, request); threadGroup.put(controller); @@ -1367,18 +1494,18 @@ class JMXGenerator { addEnvironments(environments, target) { let keys = new Set(); - target.forEach(item => { + target.forEach((item) => { keys.add(item.name); }); let envArray = environments; if (!(envArray instanceof Array)) { envArray = JSON.parse(environments); } - envArray.forEach(item => { + envArray.forEach((item) => { if (item.name && !keys.has(item.name)) { - target.push(new KeyValue({name: item.name, value: item.value})); + target.push(new KeyValue({ name: item.name, value: item.value })); } - }) + }); } addScenarioVariables(threadGroup, scenario) { @@ -1387,7 +1514,7 @@ class JMXGenerator { if (!(scenario.environment.config instanceof Object)) { config = JSON.parse(scenario.environment.config); } - this.addEnvironments(config.commonConfig.variables, scenario.variables) + this.addEnvironments(config.commonConfig.variables, scenario.variables); } let args = this.filterKV(scenario.variables); if (args.length > 0) { @@ -1419,9 +1546,12 @@ class JMXGenerator { // 强化判断,如果未匹配到合适的host则不开启DNSCache let domain = environment.config.httpConfig.domain; let validHosts = []; - hosts.forEach(item => { + hosts.forEach((item) => { if (item.domain !== undefined && domain !== undefined) { - let d = item.domain.trim().replace("http://", "").replace("https://", ""); + let d = item.domain + .trim() + .replace("http://", "") + .replace("https://", ""); if (d === domain.trim()) { item.domain = d; // 域名去掉协议 validHosts.push(item); @@ -1438,7 +1568,7 @@ class JMXGenerator { addJDBCDataSources(threadGroup, scenario) { let names = new Set(); let databaseConfigMap = new Map(); - scenario.databaseConfigs.forEach(config => { + scenario.databaseConfigs.forEach((config) => { let name = config.name + "JDBCDataSource"; threadGroup.put(new JDBCDataSource(name, config)); names.add(name); @@ -1449,7 +1579,7 @@ class JMXGenerator { if (!(scenario.environment.config instanceof Object)) { config = JSON.parse(scenario.environment.config); } - config.databaseConfigs.forEach(config => { + config.databaseConfigs.forEach((config) => { if (!names.has(config.name)) { let name = config.name + "JDBCDataSource"; threadGroup.put(new JDBCDataSource(name, config)); @@ -1466,7 +1596,7 @@ class JMXGenerator { if (!(scenario.environment.config instanceof Object)) { config = JSON.parse(scenario.environment.config); } - this.addEnvironments(config.httpConfig.headers, scenario.headers) + this.addEnvironments(config.httpConfig.headers, scenario.headers); } let headers = this.filterKV(scenario.headers); if (headers.length > 0) { @@ -1504,12 +1634,12 @@ class JMXGenerator { if (request.controller.isValid() && request.controller.enable) { if (request.controller instanceof IfController) { let name = request.controller.label(); - let variable = "\"" + request.controller.variable + "\""; + let variable = '"' + request.controller.variable + '"'; let operator = request.controller.operator; - let value = "\"" + request.controller.value + "\""; + let value = '"' + request.controller.value + '"'; if (operator === "=~" || operator === "!~") { - value = "\".*" + request.controller.value + ".*\""; + value = '".*' + request.controller.value + '.*"'; } if (operator === "is empty") { @@ -1525,7 +1655,7 @@ class JMXGenerator { } let condition = "${__jexl3(" + variable + operator + value + ")}"; - let controller = new JMXIfController(name, {condition: condition}); + let controller = new JMXIfController(name, { condition: condition }); controller.put(sampler); return controller; } @@ -1537,13 +1667,13 @@ class JMXGenerator { if (!request.body.isKV() && bodyFormat) { switch (bodyFormat) { case BODY_FORMAT.JSON: - this.addContentType(request, 'application/json'); + this.addContentType(request, "application/json"); break; case BODY_FORMAT.HTML: - this.addContentType(request, 'text/html'); + this.addContentType(request, "text/html"); break; case BODY_FORMAT.XML: - this.addContentType(request, 'text/xml'); + this.addContentType(request, "text/xml"); break; default: break; @@ -1554,13 +1684,13 @@ class JMXGenerator { addContentType(request, type) { for (let index in request.headers) { if (Object.prototype.hasOwnProperty.call(request.headers, index)) { - if (request.headers[index].name === 'Content-Type') { + if (request.headers[index].name === "Content-Type") { request.headers.splice(index, 1); break; } } } - request.headers.push(new KeyValue({name: 'Content-Type', value: type})); + request.headers.push(new KeyValue({ name: "Content-Type", value: type })); } addRequestArguments(httpSamplerProxy, request) { @@ -1576,11 +1706,16 @@ class JMXGenerator { body = this.filterKV(request.body.kvs); this.addRequestBodyFile(httpSamplerProxy, request, testId); } else { - httpSamplerProxy.boolProp('HTTPSampler.postBodyRaw', true); - body.push({name: '', value: request.body.raw, encode: false, enable: true}); + httpSamplerProxy.boolProp("HTTPSampler.postBodyRaw", true); + body.push({ + name: "", + value: request.body.raw, + encode: false, + enable: true, + }); } - if (request.method !== 'GET') { + if (request.method !== "GET") { httpSamplerProxy.add(new HTTPSamplerArguments(body)); } } @@ -1588,12 +1723,13 @@ class JMXGenerator { addRequestBodyFile(httpSamplerProxy, request, testId) { let files = []; let kvs = this.filterKVFile(request.body.kvs); - kvs.forEach(kv => { - if ((kv.enable !== false) && kv.files) { - kv.files.forEach(file => { + kvs.forEach((kv) => { + if (kv.enable !== false && kv.files) { + kv.files.forEach((file) => { let arg = {}; arg.name = kv.name; - arg.value = BODY_FILE_DIR + '/' + testId + '/' + file.id + '_' + file.name; + arg.value = + BODY_FILE_DIR + "/" + testId + "/" + file.id + "_" + file.name; files.push(arg); }); } @@ -1604,20 +1740,22 @@ class JMXGenerator { addRequestAssertion(httpSamplerProxy, request) { let assertions = request.assertions; if (assertions.regex.length > 0) { - assertions.regex.filter(this.filter).forEach(regex => { + assertions.regex.filter(this.filter).forEach((regex) => { httpSamplerProxy.put(this.getResponseAssertion(regex)); - }) + }); } if (assertions.jsonPath.length > 0) { - assertions.jsonPath.filter(this.filter).forEach(item => { + assertions.jsonPath.filter(this.filter).forEach((item) => { httpSamplerProxy.put(this.getJSONPathAssertion(item)); - }) + }); } if (assertions.duration.isValid()) { - let name = "Response In Time: " + assertions.duration.value - httpSamplerProxy.put(new DurationAssertion(name, assertions.duration.value)); + let name = "Response In Time: " + assertions.duration.value; + httpSamplerProxy.put( + new DurationAssertion(name, assertions.duration.value) + ); } } @@ -1644,21 +1782,21 @@ class JMXGenerator { addRequestExtractor(httpSamplerProxy, request) { let extract = request.extract; if (extract.regex.length > 0) { - extract.regex.filter(this.filter).forEach(regex => { + extract.regex.filter(this.filter).forEach((regex) => { httpSamplerProxy.put(this.getExtractor(regex)); - }) + }); } if (extract.json.length > 0) { - extract.json.filter(this.filter).forEach(json => { + extract.json.filter(this.filter).forEach((json) => { httpSamplerProxy.put(this.getExtractor(json)); - }) + }); } if (extract.xpath.length > 0) { - extract.xpath.filter(this.filter).forEach(xpath => { + extract.xpath.filter(this.filter).forEach((xpath) => { httpSamplerProxy.put(this.getExtractor(xpath)); - }) + }); } } @@ -1666,9 +1804,9 @@ class JMXGenerator { let props = { name: extractCommon.variable, expression: extractCommon.expression, - match: extractCommon.multipleMatching ? -1 : undefined - } - let testName = props.name + match: extractCommon.multipleMatching ? -1 : undefined, + }; + let testName = props.name; switch (extractCommon.type) { case EXTRACT_TYPE.REGEX: testName += " RegexExtractor"; @@ -1693,7 +1831,7 @@ class JMXGenerator { } filterKVFile(kvs) { - return kvs.filter(kv => { + return kvs.filter((kv) => { return kv.isFile(); }); } @@ -1704,5 +1842,3 @@ class JMXGenerator { return xml; } } - - diff --git a/framework/sdk-parent/frontend/src/model/EnvTestModel.js b/framework/sdk-parent/frontend/src/model/EnvTestModel.js index 5f79b50f2a..8c0b6cbdd0 100644 --- a/framework/sdk-parent/frontend/src/model/EnvTestModel.js +++ b/framework/sdk-parent/frontend/src/model/EnvTestModel.js @@ -29,12 +29,12 @@ import { XPath2Extractor, } from "./JMX"; import Mock from "mockjs"; -import {funcFilters} from "../utils/func-filter"; +import { funcFilters } from "../utils/func-filter"; export const uuid = function () { - let d = new Date().getTime() - let d2 = (performance && performance.now && (performance.now() * 1000)) || 0; - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + let d = new Date().getTime(); + let d2 = (performance && performance.now && performance.now() * 1000) || 0; + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { let r = Math.random() * 16; if (d > 0) { r = (d + r) % 16 | 0; @@ -43,9 +43,9 @@ export const uuid = function () { r = (d2 + r) % 16 | 0; d2 = Math.floor(d2 / 16); } - return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); + return (c === "x" ? r : (r & 0x3) | 0x8).toString(16); }); -} +}; export const BODY_FILE_DIR = "/opt/metersphere/data/body"; //存放body文件上传目录 @@ -76,7 +76,7 @@ export const calculate = function (itemValue) { } catch (e) { return itemValue; } -} +}; export const BODY_TYPE = { KV: "KeyValue", @@ -85,15 +85,15 @@ export const BODY_TYPE = { WWW_FORM: "WWW_FORM", XML: "XML", BINARY: "BINARY", - JSON: "JSON" -} + JSON: "JSON", +}; export const BODY_FORMAT = { TEXT: "text", JSON: "json", XML: "xml", HTML: "html", -} +}; export const ASSERTION_TYPE = { TEXT: "Text", @@ -103,29 +103,29 @@ export const ASSERTION_TYPE = { JSR223: "JSR223", XPATH2: "XPath2", DOCUMENT: "Document", -} +}; export const ASSERTION_REGEX_SUBJECT = { RESPONSE_CODE: "Response Code", RESPONSE_HEADERS: "Response Headers", - RESPONSE_DATA: "Response Data" -} + RESPONSE_DATA: "Response Data", +}; export const EXTRACT_TYPE = { REGEX: "Regex", JSON_PATH: "JSONPath", - XPATH: "XPath" -} + XPATH: "XPath", +}; export class BaseConfig { - set(options, notUndefined) { - options = this.initOptions(options) + options = this.initOptions(options); for (let name in options) { if (Object.prototype.hasOwnProperty.call(options, name)) { if (!(this[name] instanceof Array)) { if (notUndefined === true) { - this[name] = options[name] === undefined ? this[name] : options[name]; + this[name] = + options[name] === undefined ? this[name] : options[name]; } else { this[name] = options[name]; } @@ -135,13 +135,16 @@ export class BaseConfig { } sets(types, options) { - options = this.initOptions(options) + options = this.initOptions(options); if (types) { for (let name in types) { - if (Object.prototype.hasOwnProperty.call(types, name) && Object.prototype.hasOwnProperty.call(options, name)) { - options[name].forEach(o => { + if ( + Object.prototype.hasOwnProperty.call(types, name) && + Object.prototype.hasOwnProperty.call(options, name) + ) { + options[name].forEach((o) => { this[name].push(new types[name](o)); - }) + }); } } } @@ -160,7 +163,7 @@ export class Test extends BaseConfig { constructor(options) { super(); this.type = "MS API CONFIG"; - this.version = '1.4.0'; + this.version = "1.4.0"; this.id = uuid(); this.name = undefined; this.projectId = undefined; @@ -179,21 +182,21 @@ export class Test extends BaseConfig { if (!this.projectId) { return { isValid: false, - info: 'api_test.select_project' - } + info: "api_test.select_project", + }; } else if (!this.name) { return { isValid: false, - info: 'api_test.input_name' - } + info: "api_test.input_name", + }; } - return {isValid: true}; + return { isValid: true }; } toJMX() { return { - name: this.name + '.jmx', - xml: new JMXGenerator(this).toXML() + name: this.name + ".jmx", + xml: new JMXGenerator(this).toXML(), }; } } @@ -225,12 +228,15 @@ export class Scenario extends BaseConfig { this.databaseConfigs = []; this.tcpConfig = undefined; this.set(options); - this.sets({ - variables: KeyValue, - headers: KeyValue, - requests: RequestFactory, - databaseConfigs: DatabaseConfig - }, options); + this.sets( + { + variables: KeyValue, + headers: KeyValue, + requests: RequestFactory, + databaseConfigs: DatabaseConfig, + }, + options + ); } initOptions(options = {}) { @@ -251,25 +257,28 @@ export class Scenario extends BaseConfig { isValid() { if (this.enable) { for (let i = 0; i < this.requests.length; i++) { - let validator = this.requests[i].isValid(this.environmentId, this.environment); + let validator = this.requests[i].isValid( + this.environmentId, + this.environment + ); if (!validator.isValid) { return validator; } } } - return {isValid: true}; + return { isValid: true }; } isReference() { - return this.id.indexOf("#") !== -1 + return this.id.indexOf("#") !== -1; } } class DubboConfig extends BaseConfig { constructor(options = {}) { super(); - this.configCenter = new ConfigCenter(options.configCenter) - this.registryCenter = new RegistryCenter(options.registryCenter) + this.configCenter = new ConfigCenter(options.configCenter); + this.registryCenter = new RegistryCenter(options.registryCenter); if (options.consumerAndService === undefined) { options.consumerAndService = { timeout: undefined, @@ -279,10 +288,12 @@ class DubboConfig extends BaseConfig { group: undefined, connections: undefined, async: undefined, - loadBalance: undefined - } + loadBalance: undefined, + }; } - this.consumerAndService = new ConsumerAndService(options.consumerAndService) + this.consumerAndService = new ConsumerAndService( + options.consumerAndService + ); } } @@ -292,10 +303,10 @@ export class RequestFactory { DUBBO: "DUBBO", SQL: "SQL", TCP: "TCP", - } + }; constructor(options = {}) { - options.type = options.type || RequestFactory.TYPES.HTTP + options.type = options.type || RequestFactory.TYPES.HTTP; switch (options.type) { case RequestFactory.TYPES.DUBBO: return new DubboRequest(options); @@ -315,10 +326,10 @@ export class ResponseFactory { DUBBO: "DUBBO", SQL: "SQL", TCP: "TCP", - } + }; constructor(options = {}) { - options.type = options.type || ResponseFactory.TYPES.HTTP + options.type = options.type || ResponseFactory.TYPES.HTTP; switch (options.type) { case RequestFactory.TYPES.DUBBO: return new DubboRequest(options); @@ -364,7 +375,7 @@ export class HttpRequest extends Request { this.method = options.method || "GET"; this.parameters = []; this.rest = []; - this.authConfig = {verification: "No Auth", isEncrypt: false}; + this.authConfig = { verification: "No Auth", isEncrypt: false }; this.headers = []; this.body = new Body(options.body); this.environment = options.environment; @@ -373,9 +384,14 @@ export class HttpRequest extends Request { this.doMultipartPost = options.doMultipartPost; this.connectTimeout = options.connectTimeout || 10 * 1000; this.responseTimeout = options.responseTimeout || 10 * 1000; - this.followRedirects = options.followRedirects === undefined ? true : options.followRedirects; - - this.sets({parameters: KeyValue, rest: KeyValue, headers: KeyValue}, options); + this.followRedirects = + options.followRedirects === undefined ? true : options.followRedirects; + this.autoRedirects = + options.autoRedirects === undefined ? true : options.autoRedirects; + this.sets( + { parameters: KeyValue, rest: KeyValue, headers: KeyValue }, + options + ); } isValid(environmentId, environment) { @@ -384,35 +400,35 @@ export class HttpRequest extends Request { if (!environmentId) { return { isValid: false, - info: 'api_test.request.please_configure_environment_in_scenario' - } + info: "api_test.request.please_configure_environment_in_scenario", + }; } if (!environment.config.httpConfig.socket) { return { isValid: false, - info: 'api_test.request.please_configure_socket_in_environment' - } + info: "api_test.request.please_configure_socket_in_environment", + }; } } else { if (!this.url) { return { isValid: false, - info: 'api_test.request.input_url' - } + info: "api_test.request.input_url", + }; } try { - new URL(this.url) + new URL(this.url); } catch (e) { return { isValid: false, - info: 'api_test.request.url_invalid' - } + info: "api_test.request.url_invalid", + }; } } } return { - isValid: true - } + isValid: true, + }; } showType() { @@ -422,10 +438,8 @@ export class HttpRequest extends Request { showMethod() { return this.method.toUpperCase(); } - } - export class Response extends BaseConfig { constructor(type, options = {}) { super(); @@ -440,14 +454,13 @@ export class Response extends BaseConfig { } } - export class HttpResponse extends Response { constructor(options) { super(ResponseFactory.TYPES.HTTP, options); this.headers = []; this.body = new Body(options.body); this.statusCode = []; - this.sets({statusCode: KeyValue, headers: KeyValue}, options); + this.sets({ statusCode: KeyValue, headers: KeyValue }, options); } } @@ -455,7 +468,7 @@ export class DubboRequest extends Request { static PROTOCOLS = { DUBBO: "dubbo://", RMI: "rmi://", - } + }; constructor(options = {}) { super(RequestFactory.TYPES.DUBBO, options); @@ -464,14 +477,16 @@ export class DubboRequest extends Request { this.method = options.method; this.configCenter = new ConfigCenter(options.configCenter); this.registryCenter = new RegistryCenter(options.registryCenter); - this.consumerAndService = new ConsumerAndService(options.consumerAndService); + this.consumerAndService = new ConsumerAndService( + options.consumerAndService + ); this.args = []; this.attachmentArgs = []; // Scenario.dubboConfig this.dubboConfig = undefined; this.debugReport = undefined; - this.sets({args: KeyValue, attachmentArgs: KeyValue}, options); + this.sets({ args: KeyValue, attachmentArgs: KeyValue }, options); } isValid() { @@ -479,31 +494,31 @@ export class DubboRequest extends Request { if (!this.interface) { return { isValid: false, - info: 'api_test.request.dubbo.input_interface' - } + info: "api_test.request.dubbo.input_interface", + }; } if (!this.method) { return { isValid: false, - info: 'api_test.request.dubbo.input_method' - } + info: "api_test.request.dubbo.input_method", + }; } if (!this.registryCenter.isValid()) { return { isValid: false, - info: 'api_test.request.dubbo.input_registry_center' - } + info: "api_test.request.dubbo.input_registry_center", + }; } if (!this.consumerAndService.isValid()) { return { isValid: false, - info: 'api_test.request.dubbo.input_consumer_service' - } + info: "api_test.request.dubbo.input_consumer_service", + }; } } return { - isValid: true - } + isValid: true, + }; } showType() { @@ -521,7 +536,6 @@ export class DubboRequest extends Request { } export class SqlRequest extends Request { - constructor(options = {}) { super(RequestFactory.TYPES.SQL, options); this.useEnvironment = options.useEnvironment; @@ -534,7 +548,10 @@ export class SqlRequest extends Request { // this.queryType = options.queryType; this.queryTimeout = options.queryTimeout || 60000; - this.sets({args: KeyValue, attachmentArgs: KeyValue, variables: KeyValue}, options); + this.sets( + { args: KeyValue, attachmentArgs: KeyValue, variables: KeyValue }, + options + ); } isValid() { @@ -542,19 +559,19 @@ export class SqlRequest extends Request { if (!this.name) { return { isValid: false, - info: 'api_test.request.sql.name_cannot_be_empty' - } + info: "api_test.request.sql.name_cannot_be_empty", + }; } if (!this.dataSource) { return { isValid: false, - info: 'api_test.request.sql.dataSource_cannot_be_empty' - } + info: "api_test.request.sql.dataSource_cannot_be_empty", + }; } } return { - isValid: true - } + isValid: true, + }; } showType() { @@ -571,7 +588,11 @@ export class SqlRequest extends Request { } export class TCPConfig extends BaseConfig { - static CLASSES = ["TCPClientImpl", "BinaryTCPClientImpl", "LengthPrefixedBinaryTCPClientImpl"] + static CLASSES = [ + "TCPClientImpl", + "BinaryTCPClientImpl", + "LengthPrefixedBinaryTCPClientImpl", + ]; constructor(options = {}) { super(); @@ -581,9 +602,11 @@ export class TCPConfig extends BaseConfig { this.ctimeout = options.ctimeout; // Connect this.timeout = options.timeout; // Response - this.reUseConnection = options.reUseConnection === undefined ? true : options.reUseConnection; + this.reUseConnection = + options.reUseConnection === undefined ? true : options.reUseConnection; this.nodelay = options.nodelay === undefined ? false : options.nodelay; - this.closeConnection = options.closeConnection === undefined ? false : options.closeConnection; + this.closeConnection = + options.closeConnection === undefined ? false : options.closeConnection; this.soLinger = options.soLinger; this.eolByte = options.eolByte; @@ -601,15 +624,15 @@ export class TCPRequest extends Request { //设置TCPConfig的属性 this.set(new TCPConfig(options)); - this.sets({parameters: KeyValue}, options); + this.sets({ parameters: KeyValue }, options); this.request = options.request; } isValid() { return { - isValid: true - } + isValid: true, + }; } showType() { @@ -625,7 +648,6 @@ export class TCPRequest extends Request { } } - export class ConfigCenter extends BaseConfig { static PROTOCOLS = ["zookeeper", "nacos", "apollo"]; @@ -643,12 +665,25 @@ export class ConfigCenter extends BaseConfig { } isValid() { - return !!this.protocol || !!this.group || !!this.namespace || !!this.username || !!this.address || !!this.password || !!this.timeout; + return ( + !!this.protocol || + !!this.group || + !!this.namespace || + !!this.username || + !!this.address || + !!this.password || + !!this.timeout + ); } } export class DatabaseConfig extends BaseConfig { - static DRIVER_CLASS = ["com.mysql.jdbc.Driver", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "org.postgresql.Driver", "oracle.jdbc.OracleDriver"]; + static DRIVER_CLASS = [ + "com.mysql.jdbc.Driver", + "com.microsoft.sqlserver.jdbc.SQLServerDriver", + "org.postgresql.Driver", + "oracle.jdbc.OracleDriver", + ]; constructor(options) { super(); @@ -670,12 +705,28 @@ export class DatabaseConfig extends BaseConfig { } isValid() { - return !!this.name || !!this.poolMax || !!this.timeout || !!this.driver || !!this.dbUrl || !!this.username || !!this.password; + return ( + !!this.name || + !!this.poolMax || + !!this.timeout || + !!this.driver || + !!this.dbUrl || + !!this.username || + !!this.password + ); } } export class RegistryCenter extends BaseConfig { - static PROTOCOLS = ["none", "zookeeper", "nacos", "apollo", "multicast", "redis", "simple"]; + static PROTOCOLS = [ + "none", + "zookeeper", + "nacos", + "apollo", + "multicast", + "redis", + "simple", + ]; constructor(options) { super(); @@ -690,13 +741,25 @@ export class RegistryCenter extends BaseConfig { } isValid() { - return !!this.protocol || !!this.group || !!this.username || !!this.address || !!this.password || !!this.timeout; + return ( + !!this.protocol || + !!this.group || + !!this.username || + !!this.address || + !!this.password || + !!this.timeout + ); } } export class ConsumerAndService extends BaseConfig { static ASYNC_OPTIONS = ["sync", "async"]; - static LOAD_BALANCE_OPTIONS = ["random", "roundrobin", "leastactive", "consistenthash"]; + static LOAD_BALANCE_OPTIONS = [ + "random", + "roundrobin", + "leastactive", + "consistenthash", + ]; constructor(options) { super(); @@ -713,7 +776,16 @@ export class ConsumerAndService extends BaseConfig { } isValid() { - return !!this.timeout || !!this.version || !!this.retries || !!this.cluster || !!this.group || !!this.connections || !!this.async || !!this.loadBalance; + return ( + !!this.timeout || + !!this.version || + !!this.retries || + !!this.cluster || + !!this.group || + !!this.connections || + !!this.async || + !!this.loadBalance + ); } } @@ -725,21 +797,25 @@ export class Body extends BaseConfig { this.kvs = []; this.binary = []; this.set(options); - this.sets({kvs: KeyValue}, {binary: KeyValue}, options); + this.sets({ kvs: KeyValue }, { binary: KeyValue }, options); } isValid() { if (this.isKV()) { - return this.kvs.some(kv => { + return this.kvs.some((kv) => { return kv.isValid(); - }) + }); } else { return !!this.raw; } } isKV() { - return [BODY_TYPE.FORM_DATA, BODY_TYPE.WWW_FORM, BODY_TYPE.BINARY].indexOf(this.type) > 0; + return ( + [BODY_TYPE.FORM_DATA, BODY_TYPE.WWW_FORM, BODY_TYPE.BINARY].indexOf( + this.type + ) > 0 + ); } } @@ -761,11 +837,11 @@ export class KeyValue extends BaseConfig { } isValid() { - return (!!this.name || !!this.value) && this.type !== 'file'; + return (!!this.name || !!this.value) && this.type !== "file"; } isFile() { - return (!!this.name || !!this.value) && this.type === 'file'; + return (!!this.name || !!this.value) && this.type === "file"; } } @@ -781,9 +857,22 @@ export class Assertions extends BaseConfig { this.xpath2 = []; this.duration = undefined; this.enable = true; - this.document = {type: "JSON", data: {xmlFollowAPI: false, jsonFollowAPI: false, json: [], xml: []}, enable: true}; + this.document = { + type: "JSON", + data: { xmlFollowAPI: false, jsonFollowAPI: false, json: [], xml: [] }, + enable: true, + }; this.set(options); - this.sets({text: Text, regex: Regex, jsonPath: JSONPath, jsr223: AssertionJSR223, xpath2: XPath2}, options); + this.sets( + { + text: Text, + regex: Regex, + jsonPath: JSONPath, + jsr223: AssertionJSR223, + xpath2: XPath2, + }, + options + ); } initOptions(options) { @@ -856,7 +945,6 @@ export class Text extends AssertionType { } } - export class BeanShellProcessor extends BaseConfig { constructor(options) { super(); @@ -865,7 +953,6 @@ export class BeanShellProcessor extends BaseConfig { } } - export class JSR223Processor extends BaseConfig { constructor(options) { super(); @@ -895,7 +982,6 @@ export class JDBCProcessor extends BaseConfig { } } - export class Regex extends AssertionType { constructor(options) { super(ASSERTION_TYPE.REGEX); @@ -924,7 +1010,8 @@ export class JSONPath extends AssertionType { } setJSONPathDescription() { - this.description = this.expression + " expect: " + (this.expect ? this.expect : ''); + this.description = + this.expression + " expect: " + (this.expect ? this.expect : ""); } isValid() { @@ -946,7 +1033,6 @@ export class XPath2 extends AssertionType { } } - export class Duration extends AssertionType { constructor(options) { super(ASSERTION_TYPE.DURATION); @@ -965,7 +1051,7 @@ export class Extract extends BaseConfig { super(); this.resourceId = uuid(); this.type = "Extract"; - this.xpathType = 'html'; + this.xpathType = "html"; this.regex = []; this.json = []; this.xpath = []; @@ -974,8 +1060,8 @@ export class Extract extends BaseConfig { let types = { json: ExtractJSONPath, xpath: ExtractXPath, - regex: ExtractRegex - } + regex: ExtractRegex, + }; this.sets(types, options); } } @@ -1025,11 +1111,11 @@ export class ExtractXPath extends ExtractCommon { export class Controller extends BaseConfig { static TYPES = { IF_CONTROLLER: "If Controller", - } + }; constructor(type, options = {}) { super(); - this.type = type + this.type = type; options.id = options.id || uuid(); options.resourceId = options.resourceId || uuid(); options.enable = options.enable === undefined ? true : options.enable; @@ -1099,9 +1185,25 @@ export class LoopController extends Controller { this.type = "LoopController"; this.active = false; this.loopType = "LOOP_COUNT"; - this.countController = {loops: 0, interval: 0, proceed: true, requestResult: {}}; - this.forEachController = {inputVal: "", returnVal: "", interval: 0, requestResult: {}}; - this.whileController = {variable: "", operator: "", value: "", timeout: 0, requestResult: {}}; + this.countController = { + loops: 0, + interval: 0, + proceed: true, + requestResult: {}, + }; + this.forEachController = { + inputVal: "", + returnVal: "", + interval: 0, + requestResult: {}, + }; + this.whileController = { + variable: "", + operator: "", + value: "", + timeout: 0, + requestResult: {}, + }; this.hashTree = []; this.set(options); } @@ -1145,7 +1247,7 @@ export class TransactionController extends Controller { label() { if (this.isValid()) { - let label = this.$t('api_test.automation.transaction_controller'); + let label = this.$t("api_test.automation.transaction_controller"); if (this.name != null && this.name !== "") { label = this.name; } @@ -1155,11 +1257,10 @@ export class TransactionController extends Controller { } } - export class Timer extends BaseConfig { static TYPES = { CONSTANT_TIMER: "Constant Timer", - } + }; constructor(type, options = {}) { super(); @@ -1198,8 +1299,8 @@ const JMX_ASSERTION_CONDITION = { NOT: 1 << 2, EQUALS: 1 << 3, SUBSTRING: 1 << 4, - OR: 1 << 5 -} + OR: 1 << 5, +}; class JMXHttpRequest { constructor(request, environment) { @@ -1207,24 +1308,38 @@ class JMXHttpRequest { this.useEnvironment = request.useEnvironment; this.method = request.method; if (!request.useEnvironment) { - if (!request.url.startsWith("http://") && !request.url.startsWith("https://")) { - request.url = 'http://' + request.url; + if ( + !request.url.startsWith("http://") && + !request.url.startsWith("https://") + ) { + request.url = "http://" + request.url; } let url = new URL(request.url); this.domain = decodeURIComponent(url.hostname); this.port = url.port; this.protocol = url.protocol.split(":")[0]; - this.path = this.getPostQueryParameters(request, decodeURIComponent(url.pathname)); + this.path = this.getPostQueryParameters( + request, + decodeURIComponent(url.pathname) + ); } else { this.domain = environment.config.httpConfig.domain; this.port = environment.config.httpConfig.port; this.protocol = environment.config.httpConfig.protocol; - let url = new URL(environment.config.httpConfig.protocol + "://" + environment.config.httpConfig.socket); - this.path = this.getPostQueryParameters(request, decodeURIComponent(url.pathname + (request.path ? request.path : ''))); + let url = new URL( + environment.config.httpConfig.protocol + + "://" + + environment.config.httpConfig.socket + ); + this.path = this.getPostQueryParameters( + request, + decodeURIComponent(url.pathname + (request.path ? request.path : "")) + ); } this.connectTimeout = request.connectTimeout; this.responseTimeout = request.responseTimeout; this.followRedirects = request.followRedirects; + this.autoRedirects = request.autoRedirects; this.doMultipartPost = request.doMultipartPost; } } @@ -1232,19 +1347,19 @@ class JMXHttpRequest { getPostQueryParameters(request, path) { if (this.method.toUpperCase() !== "GET") { let parameters = []; - request.parameters.forEach(parameter => { + request.parameters.forEach((parameter) => { if (parameter.name && parameter.value && parameter.enable === true) { parameters.push(parameter); } }); if (parameters.length > 0) { - path += '?'; + path += "?"; } for (let i = 0; i < parameters.length; i++) { let parameter = parameters[i]; - path += (parameter.name + '=' + parameter.value); + path += parameter.name + "=" + parameter.value; if (i !== parameters.length - 1) { - path += '&'; + path += "&"; } } } @@ -1257,10 +1372,10 @@ class JMXDubboRequest { // Request 复制 let obj = request.clone(); // 去掉无效的kv - obj.args = obj.args.filter(arg => { + obj.args = obj.args.filter((arg) => { return arg.isValid(); }); - obj.attachmentArgs = obj.attachmentArgs.filter(arg => { + obj.attachmentArgs = obj.attachmentArgs.filter((arg) => { return arg.isValid(); }); return obj; @@ -1299,8 +1414,10 @@ class JMXTCPRequest { class JMeterTestPlan extends Element { constructor() { - super('jmeterTestPlan', { - version: "1.2", properties: "5.0", jmeter: "5.2.1" + super("jmeterTestPlan", { + version: "1.2", + properties: "5.0", + jmeter: "5.2.1", }); this.add(new HashTree()); @@ -1325,15 +1442,20 @@ class JMXGenerator { } addScenarios(testPlan, testId, request) { - let threadGroup = new ThreadGroup(request.name || ""); if (!request.isValid()) return; let sampler; if (request instanceof DubboRequest) { - sampler = new DubboSample(request.name || "", new JMXDubboRequest(request)); + sampler = new DubboSample( + request.name || "", + new JMXDubboRequest(request) + ); } else if (request instanceof HttpRequest) { - sampler = new HTTPSamplerProxy(request.name || "", new JMXHttpRequest(request, false)); + sampler = new HTTPSamplerProxy( + request.name || "", + new JMXHttpRequest(request, false) + ); this.addRequestHeader(sampler, request); this.addRequestArguments(sampler, request); this.addRequestBody(sampler, request, testId); @@ -1354,7 +1476,11 @@ class JMXGenerator { this.addConstantsTimer(sampler, request); - if (request.controller && request.controller.isValid() && request.controller.enable) { + if ( + request.controller && + request.controller.isValid() && + request.controller.enable + ) { if (request.controller instanceof IfController) { let controller = this.getController(sampler, request); threadGroup.put(controller); @@ -1367,18 +1493,18 @@ class JMXGenerator { addEnvironments(environments, target) { let keys = new Set(); - target.forEach(item => { + target.forEach((item) => { keys.add(item.name); }); let envArray = environments; if (!(envArray instanceof Array)) { envArray = JSON.parse(environments); } - envArray.forEach(item => { + envArray.forEach((item) => { if (item.name && !keys.has(item.name)) { - target.push(new KeyValue({name: item.name, value: item.value})); + target.push(new KeyValue({ name: item.name, value: item.value })); } - }) + }); } addScenarioVariables(threadGroup, scenario) { @@ -1387,7 +1513,7 @@ class JMXGenerator { if (!(scenario.environment.config instanceof Object)) { config = JSON.parse(scenario.environment.config); } - this.addEnvironments(config.commonConfig.variables, scenario.variables) + this.addEnvironments(config.commonConfig.variables, scenario.variables); } let args = this.filterKV(scenario.variables); if (args.length > 0) { @@ -1419,9 +1545,12 @@ class JMXGenerator { // 强化判断,如果未匹配到合适的host则不开启DNSCache let domain = environment.config.httpConfig.domain; let validHosts = []; - hosts.forEach(item => { + hosts.forEach((item) => { if (item.domain !== undefined && domain !== undefined) { - let d = item.domain.trim().replace("http://", "").replace("https://", ""); + let d = item.domain + .trim() + .replace("http://", "") + .replace("https://", ""); if (d === domain.trim()) { item.domain = d; // 域名去掉协议 validHosts.push(item); @@ -1438,7 +1567,7 @@ class JMXGenerator { addJDBCDataSources(threadGroup, scenario) { let names = new Set(); let databaseConfigMap = new Map(); - scenario.databaseConfigs.forEach(config => { + scenario.databaseConfigs.forEach((config) => { let name = config.name + "JDBCDataSource"; threadGroup.put(new JDBCDataSource(name, config)); names.add(name); @@ -1449,7 +1578,7 @@ class JMXGenerator { if (!(scenario.environment.config instanceof Object)) { config = JSON.parse(scenario.environment.config); } - config.databaseConfigs.forEach(config => { + config.databaseConfigs.forEach((config) => { if (!names.has(config.name)) { let name = config.name + "JDBCDataSource"; threadGroup.put(new JDBCDataSource(name, config)); @@ -1466,7 +1595,7 @@ class JMXGenerator { if (!(scenario.environment.config instanceof Object)) { config = JSON.parse(scenario.environment.config); } - this.addEnvironments(config.httpConfig.headers, scenario.headers) + this.addEnvironments(config.httpConfig.headers, scenario.headers); } let headers = this.filterKV(scenario.headers); if (headers.length > 0) { @@ -1504,12 +1633,12 @@ class JMXGenerator { if (request.controller.isValid() && request.controller.enable) { if (request.controller instanceof IfController) { let name = request.controller.label(); - let variable = "\"" + request.controller.variable + "\""; + let variable = '"' + request.controller.variable + '"'; let operator = request.controller.operator; - let value = "\"" + request.controller.value + "\""; + let value = '"' + request.controller.value + '"'; if (operator === "=~" || operator === "!~") { - value = "\".*" + request.controller.value + ".*\""; + value = '".*' + request.controller.value + '.*"'; } if (operator === "is empty") { @@ -1525,7 +1654,7 @@ class JMXGenerator { } let condition = "${__jexl3(" + variable + operator + value + ")}"; - let controller = new JMXIfController(name, {condition: condition}); + let controller = new JMXIfController(name, { condition: condition }); controller.put(sampler); return controller; } @@ -1537,13 +1666,13 @@ class JMXGenerator { if (!request.body.isKV() && bodyFormat) { switch (bodyFormat) { case BODY_FORMAT.JSON: - this.addContentType(request, 'application/json'); + this.addContentType(request, "application/json"); break; case BODY_FORMAT.HTML: - this.addContentType(request, 'text/html'); + this.addContentType(request, "text/html"); break; case BODY_FORMAT.XML: - this.addContentType(request, 'text/xml'); + this.addContentType(request, "text/xml"); break; default: break; @@ -1554,13 +1683,13 @@ class JMXGenerator { addContentType(request, type) { for (let index in request.headers) { if (Object.prototype.hasOwnProperty.call(request.headers, index)) { - if (request.headers[index].name === 'Content-Type') { + if (request.headers[index].name === "Content-Type") { request.headers.splice(index, 1); break; } } } - request.headers.push(new KeyValue({name: 'Content-Type', value: type})); + request.headers.push(new KeyValue({ name: "Content-Type", value: type })); } addRequestArguments(httpSamplerProxy, request) { @@ -1576,11 +1705,16 @@ class JMXGenerator { body = this.filterKV(request.body.kvs); this.addRequestBodyFile(httpSamplerProxy, request, testId); } else { - httpSamplerProxy.boolProp('HTTPSampler.postBodyRaw', true); - body.push({name: '', value: request.body.raw, encode: false, enable: true}); + httpSamplerProxy.boolProp("HTTPSampler.postBodyRaw", true); + body.push({ + name: "", + value: request.body.raw, + encode: false, + enable: true, + }); } - if (request.method !== 'GET') { + if (request.method !== "GET") { httpSamplerProxy.add(new HTTPSamplerArguments(body)); } } @@ -1588,12 +1722,13 @@ class JMXGenerator { addRequestBodyFile(httpSamplerProxy, request, testId) { let files = []; let kvs = this.filterKVFile(request.body.kvs); - kvs.forEach(kv => { - if ((kv.enable !== false) && kv.files) { - kv.files.forEach(file => { + kvs.forEach((kv) => { + if (kv.enable !== false && kv.files) { + kv.files.forEach((file) => { let arg = {}; arg.name = kv.name; - arg.value = BODY_FILE_DIR + '/' + testId + '/' + file.id + '_' + file.name; + arg.value = + BODY_FILE_DIR + "/" + testId + "/" + file.id + "_" + file.name; files.push(arg); }); } @@ -1604,20 +1739,22 @@ class JMXGenerator { addRequestAssertion(httpSamplerProxy, request) { let assertions = request.assertions; if (assertions.regex.length > 0) { - assertions.regex.filter(this.filter).forEach(regex => { + assertions.regex.filter(this.filter).forEach((regex) => { httpSamplerProxy.put(this.getResponseAssertion(regex)); - }) + }); } if (assertions.jsonPath.length > 0) { - assertions.jsonPath.filter(this.filter).forEach(item => { + assertions.jsonPath.filter(this.filter).forEach((item) => { httpSamplerProxy.put(this.getJSONPathAssertion(item)); - }) + }); } if (assertions.duration.isValid()) { - let name = "Response In Time: " + assertions.duration.value - httpSamplerProxy.put(new DurationAssertion(name, assertions.duration.value)); + let name = "Response In Time: " + assertions.duration.value; + httpSamplerProxy.put( + new DurationAssertion(name, assertions.duration.value) + ); } } @@ -1644,21 +1781,21 @@ class JMXGenerator { addRequestExtractor(httpSamplerProxy, request) { let extract = request.extract; if (extract.regex.length > 0) { - extract.regex.filter(this.filter).forEach(regex => { + extract.regex.filter(this.filter).forEach((regex) => { httpSamplerProxy.put(this.getExtractor(regex)); - }) + }); } if (extract.json.length > 0) { - extract.json.filter(this.filter).forEach(json => { + extract.json.filter(this.filter).forEach((json) => { httpSamplerProxy.put(this.getExtractor(json)); - }) + }); } if (extract.xpath.length > 0) { - extract.xpath.filter(this.filter).forEach(xpath => { + extract.xpath.filter(this.filter).forEach((xpath) => { httpSamplerProxy.put(this.getExtractor(xpath)); - }) + }); } } @@ -1666,9 +1803,9 @@ class JMXGenerator { let props = { name: extractCommon.variable, expression: extractCommon.expression, - match: extractCommon.multipleMatching ? -1 : undefined - } - let testName = props.name + match: extractCommon.multipleMatching ? -1 : undefined, + }; + let testName = props.name; switch (extractCommon.type) { case EXTRACT_TYPE.REGEX: testName += " RegexExtractor"; @@ -1693,7 +1830,7 @@ class JMXGenerator { } filterKVFile(kvs) { - return kvs.filter(kv => { + return kvs.filter((kv) => { return kv.isFile(); }); } @@ -1704,5 +1841,3 @@ class JMXGenerator { return xml; } } - - diff --git a/framework/sdk-parent/frontend/src/model/JMX.js b/framework/sdk-parent/frontend/src/model/JMX.js index 107b00407f..8b8a51e182 100644 --- a/framework/sdk-parent/frontend/src/model/JMX.js +++ b/framework/sdk-parent/frontend/src/model/JMX.js @@ -1,9 +1,9 @@ -const INDENT = ' '; // 缩进2空格 +const INDENT = " "; // 缩进2空格 export class Element { constructor(name, attributes, value) { - this.indent = ''; - this.name = name; // 标签名 + this.indent = ""; + this.name = name; // 标签名 this.attributes = attributes || {}; // 属性 this.value = undefined; // 基础类型的内容 this.elements = []; // 子节点 @@ -34,35 +34,37 @@ export class Element { commonValue(tag, name, value, defaultValue) { let v = this.getDefault(value, defaultValue); - return this.add(new Element(tag, {name: name}, v)); + return this.add(new Element(tag, { name: name }, v)); } boolProp(name, value, defaultValue) { - return this.commonValue('boolProp', name, value, defaultValue); + return this.commonValue("boolProp", name, value, defaultValue); } intProp(name, value, defaultValue) { - return this.commonValue('intProp', name, value, defaultValue); + return this.commonValue("intProp", name, value, defaultValue); } longProp(name, value, defaultValue) { - return this.commonValue('longProp', name, value, defaultValue); + return this.commonValue("longProp", name, value, defaultValue); } stringProp(name, value, defaultValue) { - return this.commonValue('stringProp', name, value, defaultValue); + return this.commonValue("stringProp", name, value, defaultValue); } collectionProp(name) { - return this.commonValue('collectionProp', name); + return this.commonValue("collectionProp", name); } elementProp(name, elementType) { - return this.add(new Element('elementProp', {name: name, elementType: elementType})); + return this.add( + new Element("elementProp", { name: name, elementType: elementType }) + ); } isEmptyValue() { - return this.value === undefined || this.value === ''; + return this.value === undefined || this.value === ""; } isEmptyElement() { @@ -74,8 +76,15 @@ export class Element { } replace(str) { - if (!str || !(typeof str === 'string')) return str; - return str.replace(/&/g, "&").replace(//g, ">").replace(/'/g, "'").replace(/"/g, """); + if (!str || !(typeof str === "string")) { + return str; + } + return str + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/'/g, "'") + .replace(/"/g, """); } toXML(indent) { @@ -90,16 +99,21 @@ export class Element { } start() { - let str = this.indent + '<' + this.replace(this.name); + let str = this.indent + "<" + this.replace(this.name); for (let key in this.attributes) { if (Object.prototype.hasOwnProperty.call(this.attributes, key)) { - str += ' ' + this.replace(key) + '="' + this.replace(this.attributes[key]) + '"'; + str += + " " + + this.replace(key) + + '="' + + this.replace(this.attributes[key]) + + '"'; } } if (this.isEmpty()) { - str += '/>'; + str += "/>"; } else { - str += '>'; + str += ">"; } return str; } @@ -109,11 +123,11 @@ export class Element { return this.replace(this.value); } - let str = ''; + let str = ""; let parent = this; if (this.elements.length > 0) { - str += '\n'; - this.elements.forEach(e => { + str += "\n"; + this.elements.forEach((e) => { e.indent += parent.indent + INDENT; str += e.toXML(); }); @@ -123,9 +137,9 @@ export class Element { end() { if (this.isEmpty()) { - return '\n'; + return "\n"; } - let str = '\n'; + let str = "\n"; if (!this.isEmptyValue()) { return str; } @@ -138,7 +152,7 @@ export class Element { // HashTree, 只能添加TestElement的子元素,没有基础类型内容 export class HashTree extends Element { constructor() { - super('hashTree'); + super("hashTree"); } add(te) { @@ -173,15 +187,15 @@ export class DefaultTestElement extends TestElement { super(tag, { guiclass: guiclass, testclass: testclass, - testname: testname === undefined ? tag + ' Name' : testname, - enabled: enabled || true + testname: testname === undefined ? tag + " Name" : testname, + enabled: enabled || true, }); } } export class TestPlan extends DefaultTestElement { constructor(testName, props) { - super('TestPlan', 'TestPlanGui', 'TestPlan', testName); + super("TestPlan", "TestPlanGui", "TestPlan", testName); props = props || {}; this.boolProp("TestPlan.functional_mode", props.mode, false); @@ -189,13 +203,19 @@ export class TestPlan extends DefaultTestElement { this.boolProp("TestPlan.tearDown_on_shutdown", props.tos, true); this.stringProp("TestPlan.comments", props.comments); this.stringProp("TestPlan.user_define_classpath", props.classpath); - this.add(new ElementArguments(props.args, "TestPlan.user_defined_variables", "User Defined Variables")); + this.add( + new ElementArguments( + props.args, + "TestPlan.user_defined_variables", + "User Defined Variables" + ) + ); } } export class ThreadGroup extends DefaultTestElement { constructor(testName, props) { - super('ThreadGroup', 'ThreadGroupGui', 'ThreadGroup', testName); + super("ThreadGroup", "ThreadGroupGui", "ThreadGroup", testName); props = props || {}; this.intProp("ThreadGroup.num_threads", props.threads, 1); @@ -211,72 +231,139 @@ export class ThreadGroup extends DefaultTestElement { guiclass: "LoopControlPanel", testclass: "LoopController", testname: "Loop Controller", - enabled: "true" + enabled: "true", }; let loopProps = props.loopProps || {}; - let loopController = this.add(new Element('elementProp', loopAttrs)); - loopController.boolProp('LoopController.continue_forever', loopProps.continue, false); - loopController.stringProp('LoopController.loops', loopProps.loops, 1); + let loopController = this.add(new Element("elementProp", loopAttrs)); + loopController.boolProp( + "LoopController.continue_forever", + loopProps.continue, + false + ); + loopController.stringProp("LoopController.loops", loopProps.loops, 1); } } export class DubboSample extends DefaultTestElement { constructor(testName, request = {}) { - super('io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample', - 'io.github.ningyu.jmeter.plugin.dubbo.gui.DubboSampleGui', - 'io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample', testName); + super( + "io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample", + "io.github.ningyu.jmeter.plugin.dubbo.gui.DubboSampleGui", + "io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample", + testName + ); this.request = request; - this.stringProp("FIELD_DUBBO_CONFIG_CENTER_PROTOCOL", this.request.configCenter.protocol); - this.stringProp("FIELD_DUBBO_CONFIG_CENTER_GROUP", this.request.configCenter.group); - this.stringProp("FIELD_DUBBO_CONFIG_CENTER_NAMESPACE", this.request.configCenter.namespace); - this.stringProp("FIELD_DUBBO_CONFIG_CENTER_USER_NAME", this.request.configCenter.username); - this.stringProp("FIELD_DUBBO_CONFIG_CENTER_PASSWORD", this.request.configCenter.password); - this.stringProp("FIELD_DUBBO_CONFIG_CENTER_ADDRESS", this.request.configCenter.address); - this.stringProp("FIELD_DUBBO_CONFIG_CENTER_TIMEOUT", this.request.configCenter.timeout); + this.stringProp( + "FIELD_DUBBO_CONFIG_CENTER_PROTOCOL", + this.request.configCenter.protocol + ); + this.stringProp( + "FIELD_DUBBO_CONFIG_CENTER_GROUP", + this.request.configCenter.group + ); + this.stringProp( + "FIELD_DUBBO_CONFIG_CENTER_NAMESPACE", + this.request.configCenter.namespace + ); + this.stringProp( + "FIELD_DUBBO_CONFIG_CENTER_USER_NAME", + this.request.configCenter.username + ); + this.stringProp( + "FIELD_DUBBO_CONFIG_CENTER_PASSWORD", + this.request.configCenter.password + ); + this.stringProp( + "FIELD_DUBBO_CONFIG_CENTER_ADDRESS", + this.request.configCenter.address + ); + this.stringProp( + "FIELD_DUBBO_CONFIG_CENTER_TIMEOUT", + this.request.configCenter.timeout + ); - this.stringProp("FIELD_DUBBO_REGISTRY_PROTOCOL", this.request.registryCenter.protocol); - this.stringProp("FIELD_DUBBO_REGISTRY_GROUP", this.request.registryCenter.group); - this.stringProp("FIELD_DUBBO_REGISTRY_USER_NAME", this.request.registryCenter.username); - this.stringProp("FIELD_DUBBO_REGISTRY_PASSWORD", this.request.registryCenter.password); + this.stringProp( + "FIELD_DUBBO_REGISTRY_PROTOCOL", + this.request.registryCenter.protocol + ); + this.stringProp( + "FIELD_DUBBO_REGISTRY_GROUP", + this.request.registryCenter.group + ); + this.stringProp( + "FIELD_DUBBO_REGISTRY_USER_NAME", + this.request.registryCenter.username + ); + this.stringProp( + "FIELD_DUBBO_REGISTRY_PASSWORD", + this.request.registryCenter.password + ); this.stringProp("FIELD_DUBBO_ADDRESS", this.request.registryCenter.address); - this.stringProp("FIELD_DUBBO_REGISTRY_TIMEOUT", this.request.registryCenter.timeout); + this.stringProp( + "FIELD_DUBBO_REGISTRY_TIMEOUT", + this.request.registryCenter.timeout + ); - this.stringProp("FIELD_DUBBO_TIMEOUT", this.request.consumerAndService.timeout); - this.stringProp("FIELD_DUBBO_VERSION", this.request.consumerAndService.version); - this.stringProp("FIELD_DUBBO_RETRIES", this.request.consumerAndService.retries); + this.stringProp( + "FIELD_DUBBO_TIMEOUT", + this.request.consumerAndService.timeout + ); + this.stringProp( + "FIELD_DUBBO_VERSION", + this.request.consumerAndService.version + ); + this.stringProp( + "FIELD_DUBBO_RETRIES", + this.request.consumerAndService.retries + ); this.stringProp("FIELD_DUBBO_GROUP", this.request.consumerAndService.group); - this.stringProp("FIELD_DUBBO_CONNECTIONS", this.request.consumerAndService.connections); - this.stringProp("FIELD_DUBBO_LOADBALANCE", this.request.consumerAndService.loadBalance); + this.stringProp( + "FIELD_DUBBO_CONNECTIONS", + this.request.consumerAndService.connections + ); + this.stringProp( + "FIELD_DUBBO_LOADBALANCE", + this.request.consumerAndService.loadBalance + ); this.stringProp("FIELD_DUBBO_ASYNC", this.request.consumerAndService.async); - this.stringProp("FIELD_DUBBO_CLUSTER", this.request.consumerAndService.cluster); + this.stringProp( + "FIELD_DUBBO_CLUSTER", + this.request.consumerAndService.cluster + ); this.stringProp("FIELD_DUBBO_RPC_PROTOCOL", this.request.protocol); this.stringProp("FIELD_DUBBO_INTERFACE", this.request.interface); this.stringProp("FIELD_DUBBO_METHOD", this.request.method); this.intProp("FIELD_DUBBO_METHOD_ARGS_SIZE", this.request.args.length); - this.intProp("FIELD_DUBBO_ATTACHMENT_ARGS_SIZE", this.request.attachmentArgs.length); + this.intProp( + "FIELD_DUBBO_ATTACHMENT_ARGS_SIZE", + this.request.attachmentArgs.length + ); this.request.args.forEach((arg, i) => { if (!!arg.name || !!arg.value) { let index = i + 1; this.stringProp("FIELD_DUBBO_METHOD_ARGS_PARAM_TYPE" + index, arg.name); - this.stringProp("FIELD_DUBBO_METHOD_ARGS_PARAM_VALUE" + index, arg.value); + this.stringProp( + "FIELD_DUBBO_METHOD_ARGS_PARAM_VALUE" + index, + arg.value + ); } - }) + }); this.request.attachmentArgs.forEach((arg, i) => { if (!!arg.name || !!arg.value) { let index = i + 1; this.stringProp("FIELD_DUBBO_ATTACHMENT_ARGS_KEY" + index, arg.name); this.stringProp("FIELD_DUBBO_ATTACHMENT_ARGS_VALUE" + index, arg.value); } - }) + }); } } export class JDBCSampler extends DefaultTestElement { constructor(testName, request = {}) { - super('JDBCSampler', 'TestBeanGUI', 'JDBCSampler', testName); + super("JDBCSampler", "TestBeanGUI", "JDBCSampler", testName); this.stringProp("dataSource", request.dataSource); this.stringProp("query", request.query); @@ -286,14 +373,14 @@ export class JDBCSampler extends DefaultTestElement { this.stringProp("queryArguments"); this.stringProp("queryArgumentsTypes"); this.stringProp("resultSetMaxRows"); - this.stringProp("resultSetHandler", 'Store as String'); - this.stringProp("queryType", 'Callable Statement'); + this.stringProp("resultSetHandler", "Store as String"); + this.stringProp("queryType", "Callable Statement"); } } export class TCPSampler extends DefaultTestElement { constructor(testName, request = {}) { - super('TCPSampler', 'TCPSamplerGui', 'TCPSampler', testName); + super("TCPSampler", "TCPSamplerGui", "TCPSampler", testName); this.stringProp("TCPSampler.classname", request.classname); this.stringProp("TCPSampler.server", request.server); @@ -313,7 +400,12 @@ export class TCPSampler extends DefaultTestElement { export class HTTPSamplerProxy extends DefaultTestElement { constructor(testName, options = {}) { - super('HTTPSamplerProxy', 'HttpTestSampleGui', 'HTTPSamplerProxy', testName); + super( + "HTTPSamplerProxy", + "HttpTestSampleGui", + "HTTPSamplerProxy", + testName + ); this.stringProp("HTTPSampler.domain", options.domain); this.stringProp("HTTPSampler.protocol", options.protocol); @@ -327,46 +419,62 @@ export class HTTPSamplerProxy extends DefaultTestElement { this.stringProp("HTTPSampler.port", options.port); } if (options.connectTimeout) { - this.stringProp('HTTPSampler.connect_timeout', options.connectTimeout); + this.stringProp("HTTPSampler.connect_timeout", options.connectTimeout); } if (options.responseTimeout) { - this.stringProp('HTTPSampler.response_timeout', options.responseTimeout); + this.stringProp("HTTPSampler.response_timeout", options.responseTimeout); } if (options.followRedirects) { - this.boolProp('HTTPSampler.follow_redirects', options.followRedirects, true); + this.boolProp( + "HTTPSampler.follow_redirects", + options.followRedirects, + true + ); + } + if (options.autoRedirects) { + this.boolProp("HTTPSampler.auto_redirects", options.autoRedirects, false); } this.boolProp("HTTPSampler.use_keepalive", options.keepalive, true); - this.boolProp("HTTPSampler.DO_MULTIPART_POST", options.doMultipartPost, false); + this.boolProp( + "HTTPSampler.DO_MULTIPART_POST", + options.doMultipartPost, + false + ); } } // 这是一个Element export class HTTPSamplerArguments extends Element { constructor(args) { - super('elementProp', { + super("elementProp", { name: "HTTPsampler.Arguments", // s必须小写 elementType: "Arguments", guiclass: "HTTPArgumentsPanel", testclass: "Arguments", - enabled: "true" + enabled: "true", }); this.args = args || []; - let collectionProp = this.collectionProp('Arguments.arguments'); - this.args.forEach(arg => { - if (arg.enable === true || arg.enable === undefined) { // 非禁用的条件加入执行 - let elementProp = collectionProp.elementProp(arg.name, 'HTTPArgument'); - elementProp.boolProp('HTTPArgument.always_encode', arg.encode, true); - elementProp.boolProp('HTTPArgument.use_equals', arg.equals, true); + let collectionProp = this.collectionProp("Arguments.arguments"); + this.args.forEach((arg) => { + if (arg.enable === true || arg.enable === undefined) { + // 非禁用的条件加入执行 + let elementProp = collectionProp.elementProp(arg.name, "HTTPArgument"); + elementProp.boolProp("HTTPArgument.always_encode", arg.encode, true); + elementProp.boolProp("HTTPArgument.use_equals", arg.equals, true); if (arg.name) { - elementProp.stringProp('Argument.name', arg.name); + elementProp.stringProp("Argument.name", arg.name); } - elementProp.stringProp('Argument.value', arg.value); - elementProp.stringProp('Argument.metadata', arg.metadata || "="); + elementProp.stringProp("Argument.value", arg.value); + elementProp.stringProp("Argument.metadata", arg.metadata || "="); if (arg.contentType) { - elementProp.stringProp('HTTPArgument.content_type', arg.contentType, ""); + elementProp.stringProp( + "HTTPArgument.content_type", + arg.contentType, + "" + ); } } }); @@ -375,51 +483,59 @@ export class HTTPSamplerArguments extends Element { export class HTTPsamplerFiles extends Element { constructor(args) { - super('elementProp', { + super("elementProp", { name: "HTTPsampler.Files", elementType: "HTTPFileArgs", }); this.args = args || {}; - let collectionProp = this.collectionProp('HTTPFileArgs.files'); - this.args.forEach(arg => { - let elementProp = collectionProp.elementProp(arg.value, 'HTTPFileArg'); - elementProp.stringProp('File.path', arg.value); - elementProp.stringProp('File.paramname', arg.name); - elementProp.stringProp('File.mimetype', arg.contentType || "application/octet-stream"); + let collectionProp = this.collectionProp("HTTPFileArgs.files"); + this.args.forEach((arg) => { + let elementProp = collectionProp.elementProp(arg.value, "HTTPFileArg"); + elementProp.stringProp("File.path", arg.value); + elementProp.stringProp("File.paramname", arg.name); + elementProp.stringProp( + "File.mimetype", + arg.contentType || "application/octet-stream" + ); }); } } export class CookieManager extends DefaultTestElement { constructor(testName) { - super('CookieManager', 'CookiePanel', 'CookieManager', testName); - this.collectionProp('CookieManager.cookies'); - this.boolProp('CookieManager.clearEachIteration', false, false); - this.boolProp('CookieManager.controlledByThreadGroup', false, false); + super("CookieManager", "CookiePanel", "CookieManager", testName); + this.collectionProp("CookieManager.cookies"); + this.boolProp("CookieManager.clearEachIteration", false, false); + this.boolProp("CookieManager.controlledByThreadGroup", false, false); } } export class DurationAssertion extends DefaultTestElement { constructor(testName, duration) { - super('DurationAssertion', 'DurationAssertionGui', 'DurationAssertion', testName); + super( + "DurationAssertion", + "DurationAssertionGui", + "DurationAssertion", + testName + ); this.duration = duration || 0; - this.stringProp('DurationAssertion.duration', this.duration); + this.stringProp("DurationAssertion.duration", this.duration); } } export class ResponseAssertion extends DefaultTestElement { constructor(testName, assertion) { - super('ResponseAssertion', 'AssertionGui', 'ResponseAssertion', testName); + super("ResponseAssertion", "AssertionGui", "ResponseAssertion", testName); this.assertion = assertion || {}; - this.stringProp('Assertion.test_field', this.assertion.field); - this.boolProp('Assertion.assume_success', this.assertion.assumeSuccess); - this.intProp('Assertion.test_type', this.assertion.type); - this.stringProp('Assertion.custom_message', this.assertion.message); + this.stringProp("Assertion.test_field", this.assertion.field); + this.boolProp("Assertion.assume_success", this.assertion.assumeSuccess); + this.intProp("Assertion.test_type", this.assertion.type); + this.stringProp("Assertion.custom_message", this.assertion.message); - let collectionProp = this.collectionProp('Asserion.test_strings'); + let collectionProp = this.collectionProp("Asserion.test_strings"); let random = Math.floor(Math.random() * 10000); collectionProp.stringProp(random, this.assertion.value); } @@ -427,54 +543,59 @@ export class ResponseAssertion extends DefaultTestElement { export class JSONPathAssertion extends DefaultTestElement { constructor(testName, jsonPath) { - super('JSONPathAssertion', 'JSONPathAssertionGui', 'JSONPathAssertion', testName); + super( + "JSONPathAssertion", + "JSONPathAssertionGui", + "JSONPathAssertion", + testName + ); this.jsonPath = jsonPath || {}; - this.stringProp('JSON_PATH', this.jsonPath.expression); - this.stringProp('EXPECTED_VALUE', this.jsonPath.expect); - this.boolProp('JSONVALIDATION', true); - this.boolProp('EXPECT_NULL', false); - this.boolProp('INVERT', false); - this.boolProp('ISREGEX', true); + this.stringProp("JSON_PATH", this.jsonPath.expression); + this.stringProp("EXPECTED_VALUE", this.jsonPath.expect); + this.boolProp("JSONVALIDATION", true); + this.boolProp("EXPECT_NULL", false); + this.boolProp("INVERT", false); + this.boolProp("ISREGEX", true); } } export class ResponseCodeAssertion extends ResponseAssertion { constructor(testName, type, value, assumeSuccess, message) { let assertion = { - field: 'Assertion.response_code', + field: "Assertion.response_code", type: type, value: value, assumeSuccess: assumeSuccess, message: message, - } - super(testName, assertion) + }; + super(testName, assertion); } } export class ResponseDataAssertion extends ResponseAssertion { constructor(testName, type, value, assumeSuccess, message) { let assertion = { - field: 'Assertion.response_data', + field: "Assertion.response_data", type: type, value: value, assumeSuccess: assumeSuccess, message: message, - } - super(testName, assertion) + }; + super(testName, assertion); } } export class ResponseHeadersAssertion extends ResponseAssertion { constructor(testName, type, value, assumeSuccess, message) { let assertion = { - field: 'Assertion.response_headers', + field: "Assertion.response_headers", type: type, value: value, assumeSuccess: assumeSuccess, message: message, - } - super(testName, assertion) + }; + super(testName, assertion); } } @@ -482,10 +603,10 @@ export class BeanShellProcessor extends DefaultTestElement { constructor(tag, guiclass, testclass, testname, processor) { super(tag, guiclass, testclass, testname); this.processor = processor || {}; - this.boolProp('resetInterpreter', false); - this.stringProp('parameters'); - this.stringProp('filename'); - this.stringProp('script', processor.script); + this.boolProp("resetInterpreter", false); + this.stringProp("parameters"); + this.stringProp("filename"); + this.stringProp("script", processor.script); } } @@ -493,78 +614,106 @@ export class JSR223Processor extends DefaultTestElement { constructor(tag, guiclass, testclass, testname, processor) { super(tag, guiclass, testclass, testname); this.processor = processor || {}; - this.stringProp('cacheKey', 'true'); - this.stringProp('filename'); - this.stringProp('parameters'); - this.stringProp('script', this.processor.script); - this.stringProp('scriptLanguage', this.processor.language); + this.stringProp("cacheKey", "true"); + this.stringProp("filename"); + this.stringProp("parameters"); + this.stringProp("script", this.processor.script); + this.stringProp("scriptLanguage", this.processor.language); } } export class JSR223PreProcessor extends JSR223Processor { constructor(testName, processor) { - super('JSR223PreProcessor', 'TestBeanGUI', 'JSR223PreProcessor', testName, processor) + super( + "JSR223PreProcessor", + "TestBeanGUI", + "JSR223PreProcessor", + testName, + processor + ); } } export class JSR223PostProcessor extends JSR223Processor { constructor(testName, processor) { - super('JSR223PostProcessor', 'TestBeanGUI', 'JSR223PostProcessor', testName, processor) + super( + "JSR223PostProcessor", + "TestBeanGUI", + "JSR223PostProcessor", + testName, + processor + ); } } export class BeanShellPreProcessor extends BeanShellProcessor { constructor(testName, processor) { - super('BeanShellPreProcessor', 'TestBeanGUI', 'BeanShellPreProcessor', testName, processor) + super( + "BeanShellPreProcessor", + "TestBeanGUI", + "BeanShellPreProcessor", + testName, + processor + ); } } export class BeanShellPostProcessor extends BeanShellProcessor { constructor(testName, processor) { - super('BeanShellPostProcessor', 'TestBeanGUI', 'BeanShellPostProcessor', testName, processor) + super( + "BeanShellPostProcessor", + "TestBeanGUI", + "BeanShellPostProcessor", + testName, + processor + ); } } export class IfController extends DefaultTestElement { constructor(testName, controller = {}) { - super('IfController', 'IfControllerPanel', 'IfController', testName); + super("IfController", "IfControllerPanel", "IfController", testName); - this.stringProp('IfController.comments', controller.comments); - this.stringProp('IfController.condition', controller.condition); - this.boolProp('IfController.evaluateAll', controller.evaluateAll, false); - this.boolProp('IfController.useExpression', controller.useExpression, true); + this.stringProp("IfController.comments", controller.comments); + this.stringProp("IfController.condition", controller.condition); + this.boolProp("IfController.evaluateAll", controller.evaluateAll, false); + this.boolProp("IfController.useExpression", controller.useExpression, true); } } export class TransactionController extends DefaultTestElement { constructor(testName, controller = {}) { - super('TransactionController', 'TransactionControllerPanel', 'TransactionController', testName); + super( + "TransactionController", + "TransactionControllerPanel", + "TransactionController", + testName + ); - this.stringProp('TransactionController.comments', controller.comments); - this.boolProp('TransactionController.parent', controller.parent, true); + this.stringProp("TransactionController.comments", controller.comments); + this.boolProp("TransactionController.parent", controller.parent, true); } } - export class ConstantTimer extends DefaultTestElement { constructor(testName, timer = {}) { - super('ConstantTimer', 'ConstantTimerGui', 'ConstantTimer', testName); + super("ConstantTimer", "ConstantTimerGui", "ConstantTimer", testName); - this.stringProp('ConstantTimer.delay', timer.delay); + this.stringProp("ConstantTimer.delay", timer.delay); } } export class HeaderManager extends DefaultTestElement { constructor(testName, headers) { - super('HeaderManager', 'HeaderPanel', 'HeaderManager', testName); + super("HeaderManager", "HeaderPanel", "HeaderManager", testName); this.headers = headers || []; - let collectionProp = this.collectionProp('HeaderManager.headers'); - this.headers.forEach(header => { + let collectionProp = this.collectionProp("HeaderManager.headers"); + this.headers.forEach((header) => { if (header.enable === true || header.enable === undefined) { - let elementProp = collectionProp.elementProp('', 'Header'); - elementProp.stringProp('Header.name', header.name); - elementProp.stringProp('Header.value', header.value); + let elementProp = collectionProp.elementProp("", "Header"); + elementProp.stringProp("Header.name", header.name); + elementProp.stringProp("Header.value", header.value); } }); } @@ -572,57 +721,61 @@ export class HeaderManager extends DefaultTestElement { export class DNSCacheManager extends DefaultTestElement { constructor(testName, hosts) { - super('DNSCacheManager', 'DNSCachePanel', 'DNSCacheManager', testName); - let collectionPropServers = this.collectionProp('DNSCacheManager.servers'); - let collectionPropHosts = this.collectionProp('DNSCacheManager.hosts'); + super("DNSCacheManager", "DNSCachePanel", "DNSCacheManager", testName); + let collectionPropServers = this.collectionProp("DNSCacheManager.servers"); + let collectionPropHosts = this.collectionProp("DNSCacheManager.hosts"); - hosts.forEach(host => { - let elementProp = collectionPropHosts.elementProp(host.domain, 'StaticHost'); - elementProp.stringProp('StaticHost.Name', host.domain); - elementProp.stringProp('StaticHost.Address', host.ip); + hosts.forEach((host) => { + let elementProp = collectionPropHosts.elementProp( + host.domain, + "StaticHost" + ); + elementProp.stringProp("StaticHost.Name", host.domain); + elementProp.stringProp("StaticHost.Address", host.ip); }); - let boolProp = this.boolProp('DNSCacheManager.isCustomResolver', true); + let boolProp = this.boolProp("DNSCacheManager.isCustomResolver", true); } } export class JDBCDataSource extends DefaultTestElement { constructor(testName, datasource) { - super('JDBCDataSource', 'TestBeanGUI', 'JDBCDataSource', testName); + super("JDBCDataSource", "TestBeanGUI", "JDBCDataSource", testName); - this.boolProp('autocommit', true); - this.boolProp('keepAlive', true); - this.boolProp('preinit', false); - this.stringProp('dataSource', datasource.name); - this.stringProp('dbUrl', datasource.dbUrl); - this.stringProp('driver', datasource.driver); - this.stringProp('username', datasource.username); - this.stringProp('password', datasource.password); - this.stringProp('poolMax', datasource.poolMax); - this.stringProp('timeout', datasource.timeout); - this.stringProp('connectionAge', '5000'); - this.stringProp('trimInterval', '60000'); - this.stringProp('transactionIsolation', 'DEFAULT'); - this.stringProp('checkQuery'); - this.stringProp('initQuery'); - this.stringProp('connectionProperties'); + this.boolProp("autocommit", true); + this.boolProp("keepAlive", true); + this.boolProp("preinit", false); + this.stringProp("dataSource", datasource.name); + this.stringProp("dbUrl", datasource.dbUrl); + this.stringProp("driver", datasource.driver); + this.stringProp("username", datasource.username); + this.stringProp("password", datasource.password); + this.stringProp("poolMax", datasource.poolMax); + this.stringProp("timeout", datasource.timeout); + this.stringProp("connectionAge", "5000"); + this.stringProp("trimInterval", "60000"); + this.stringProp("transactionIsolation", "DEFAULT"); + this.stringProp("checkQuery"); + this.stringProp("initQuery"); + this.stringProp("connectionProperties"); } } export class Arguments extends DefaultTestElement { constructor(testName, args) { - super('Arguments', 'ArgumentsPanel', 'Arguments', testName); + super("Arguments", "ArgumentsPanel", "Arguments", testName); this.args = args || []; - let collectionProp = this.collectionProp('Arguments.arguments'); + let collectionProp = this.collectionProp("Arguments.arguments"); - this.args.forEach(arg => { - if (arg.enable === true || arg.enable === undefined) { // 非禁用的条件加入执行 - let elementProp = collectionProp.elementProp(arg.name, 'Argument'); - elementProp.stringProp('Argument.name', arg.name); - elementProp.stringProp('Argument.value', arg.value); - elementProp.stringProp('Argument.desc', arg.desc); - elementProp.stringProp('Argument.metadata', arg.metadata, "="); + this.args.forEach((arg) => { + if (arg.enable === true || arg.enable === undefined) { + // 非禁用的条件加入执行 + let elementProp = collectionProp.elementProp(arg.name, "Argument"); + elementProp.stringProp("Argument.name", arg.name); + elementProp.stringProp("Argument.value", arg.value); + elementProp.stringProp("Argument.desc", arg.desc); + elementProp.stringProp("Argument.metadata", arg.metadata, "="); } }); } @@ -630,23 +783,24 @@ export class Arguments extends DefaultTestElement { export class ElementArguments extends Element { constructor(args, name, testName) { - super('elementProp', { + super("elementProp", { name: name || "arguments", elementType: "Arguments", guiclass: "ArgumentsPanel", testclass: "Arguments", testname: testName || "", - enabled: "true" + enabled: "true", }); - let collectionProp = this.collectionProp('Arguments.arguments'); + let collectionProp = this.collectionProp("Arguments.arguments"); if (args) { - args.forEach(arg => { - if (arg.enable === true || arg.enable === undefined) { // 非禁用的条件加入执行 - let elementProp = collectionProp.elementProp(arg.name, 'Argument'); - elementProp.stringProp('Argument.name', arg.name); - elementProp.stringProp('Argument.value', arg.value); - elementProp.stringProp('Argument.metadata', arg.metadata, "="); + args.forEach((arg) => { + if (arg.enable === true || arg.enable === undefined) { + // 非禁用的条件加入执行 + let elementProp = collectionProp.elementProp(arg.name, "Argument"); + elementProp.stringProp("Argument.name", arg.name); + elementProp.stringProp("Argument.value", arg.value); + elementProp.stringProp("Argument.metadata", arg.metadata, "="); } }); } @@ -655,35 +809,40 @@ export class ElementArguments extends Element { export class RegexExtractor extends DefaultTestElement { constructor(testName, props) { - super('RegexExtractor', 'RegexExtractorGui', 'RegexExtractor', testName); - this.props = props || {} - this.stringProp('RegexExtractor.useHeaders', props.headers); - this.stringProp('RegexExtractor.refname', props.name); - this.stringProp('RegexExtractor.regex', props.expression); - this.stringProp('RegexExtractor.template', props.template); - this.stringProp('RegexExtractor.default', props.default); - this.stringProp('RegexExtractor.match_number', props.match); + super("RegexExtractor", "RegexExtractorGui", "RegexExtractor", testName); + this.props = props || {}; + this.stringProp("RegexExtractor.useHeaders", props.headers); + this.stringProp("RegexExtractor.refname", props.name); + this.stringProp("RegexExtractor.regex", props.expression); + this.stringProp("RegexExtractor.template", props.template); + this.stringProp("RegexExtractor.default", props.default); + this.stringProp("RegexExtractor.match_number", props.match); } } export class JSONPostProcessor extends DefaultTestElement { constructor(testName, props) { - super('JSONPostProcessor', 'JSONPostProcessorGui', 'JSONPostProcessor', testName); - this.props = props || {} - this.stringProp('JSONPostProcessor.referenceNames', props.name); - this.stringProp('JSONPostProcessor.jsonPathExprs', props.expression); - this.stringProp('JSONPostProcessor.match_numbers', props.match); + super( + "JSONPostProcessor", + "JSONPostProcessorGui", + "JSONPostProcessor", + testName + ); + this.props = props || {}; + this.stringProp("JSONPostProcessor.referenceNames", props.name); + this.stringProp("JSONPostProcessor.jsonPathExprs", props.expression); + this.stringProp("JSONPostProcessor.match_numbers", props.match); } } export class XPath2Extractor extends DefaultTestElement { constructor(testName, props) { - super('XPath2Extractor', 'XPath2ExtractorGui', 'XPath2Extractor', testName); - this.props = props || {} - this.stringProp('XPathExtractor2.default', props.default); - this.stringProp('XPathExtractor2.refname', props.name); - this.stringProp('XPathExtractor2.xpathQuery', props.expression); - this.stringProp('XPathExtractor2.namespaces', props.namespaces); - this.stringProp('XPathExtractor2.matchNumber', props.match); + super("XPath2Extractor", "XPath2ExtractorGui", "XPath2Extractor", testName); + this.props = props || {}; + this.stringProp("XPathExtractor2.default", props.default); + this.stringProp("XPathExtractor2.refname", props.name); + this.stringProp("XPathExtractor2.xpathQuery", props.expression); + this.stringProp("XPathExtractor2.namespaces", props.namespaces); + this.stringProp("XPathExtractor2.matchNumber", props.match); } } diff --git a/framework/sdk-parent/frontend/src/model/ScenarioModel.js b/framework/sdk-parent/frontend/src/model/ScenarioModel.js index 42e5d882ca..b62001e5c3 100644 --- a/framework/sdk-parent/frontend/src/model/ScenarioModel.js +++ b/framework/sdk-parent/frontend/src/model/ScenarioModel.js @@ -30,12 +30,12 @@ import { XPath2Extractor, } from "./JMX"; import Mock from "mockjs"; -import {funcFilters} from "../utils/func-filter"; +import { funcFilters } from "../utils/func-filter"; export const uuid = function () { - let d = new Date().getTime() - let d2 = (performance && performance.now && (performance.now() * 1000)) || 0; - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + let d = new Date().getTime(); + let d2 = (performance && performance.now && performance.now() * 1000) || 0; + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { let r = Math.random() * 16; if (d > 0) { r = (d + r) % 16 | 0; @@ -44,9 +44,9 @@ export const uuid = function () { r = (d2 + r) % 16 | 0; d2 = Math.floor(d2 / 16); } - return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); + return (c === "x" ? r : (r & 0x3) | 0x8).toString(16); }); -} +}; export const BODY_FILE_DIR = "/opt/metersphere/data/body"; //存放body文件上传目录 @@ -77,20 +77,20 @@ export const calculate = function (itemValue) { } catch (e) { return itemValue; } -} +}; export const BODY_TYPE = { KV: "KeyValue", FORM_DATA: "Form Data", - RAW: "Raw" -} + RAW: "Raw", +}; export const BODY_FORMAT = { TEXT: "text", JSON: "json", XML: "xml", HTML: "html", -} +}; export const ASSERTION_TYPE = { TEXT: "Text", @@ -99,30 +99,29 @@ export const ASSERTION_TYPE = { DURATION: "Duration", JSR223: "JSR223", XPATH2: "XPath2", -} +}; export const ASSERTION_REGEX_SUBJECT = { RESPONSE_CODE: "Response Code", RESPONSE_HEADERS: "Response Headers", - RESPONSE_DATA: "Response Data" -} + RESPONSE_DATA: "Response Data", +}; export const EXTRACT_TYPE = { REGEX: "Regex", JSON_PATH: "JSONPath", - XPATH: "XPath" -} - + XPATH: "XPath", +}; export class BaseConfig { - set(options, notUndefined) { - options = this.initOptions(options) + options = this.initOptions(options); for (let name in options) { if (Object.prototype.hasOwnProperty.call(options, name)) { if (!(this[name] instanceof Array)) { if (notUndefined === true) { - this[name] = options[name] === undefined ? this[name] : options[name]; + this[name] = + options[name] === undefined ? this[name] : options[name]; } else { this[name] = options[name]; } @@ -132,13 +131,17 @@ export class BaseConfig { } sets(types, options) { - options = this.initOptions(options) + options = this.initOptions(options); if (types) { for (let name in types) { - if (options[name] !== null && Object.prototype.hasOwnProperty.call(types, name) && Object.prototype.hasOwnProperty.call(options, name)) { - options[name].forEach(o => { + if ( + options[name] !== null && + Object.prototype.hasOwnProperty.call(types, name) && + Object.prototype.hasOwnProperty.call(options, name) + ) { + options[name].forEach((o) => { this[name].push(new types[name](o)); - }) + }); } } } @@ -157,7 +160,7 @@ export class Test extends BaseConfig { constructor(options) { super(); this.type = "MS API CONFIG"; - this.version = '1.5.0'; + this.version = "1.5.0"; this.id = uuid(); this.name = undefined; this.projectId = undefined; @@ -165,14 +168,14 @@ export class Test extends BaseConfig { this.schedule = {}; this.set(options); - this.sets({scenarioDefinition: Scenario}, options); + this.sets({ scenarioDefinition: Scenario }, options); } export() { let obj = { type: this.type, version: this.version, - scenarios: this.scenarioDefinition + scenarios: this.scenarioDefinition, }; return JSON.stringify(obj); @@ -194,21 +197,21 @@ export class Test extends BaseConfig { if (!this.projectId) { return { isValid: false, - info: 'api_test.select_project' - } + info: "api_test.select_project", + }; } else if (!this.name) { return { isValid: false, - info: 'api_test.input_name' - } + info: "api_test.input_name", + }; } - return {isValid: true}; + return { isValid: true }; } toJMX() { return { - name: this.name + '.jmx', - xml: new JMXGenerator(this).toXML() + name: this.name + ".jmx", + xml: new JMXGenerator(this).toXML(), }; } } @@ -233,12 +236,15 @@ export class Scenario extends BaseConfig { this.referenceEnable = undefined; this.set(options); - this.sets({ - variables: KeyValue, - headers: KeyValue, - requests: RequestFactory, - databaseConfigs: DatabaseConfig - }, options); + this.sets( + { + variables: KeyValue, + headers: KeyValue, + requests: RequestFactory, + databaseConfigs: DatabaseConfig, + }, + options + ); } initOptions(options = {}) { @@ -260,17 +266,20 @@ export class Scenario extends BaseConfig { isValid() { if (this.enable) { for (let i = 0; i < this.requests.length; i++) { - let validator = this.requests[i].isValid(this.environmentId, this.environment); + let validator = this.requests[i].isValid( + this.environmentId, + this.environment + ); if (!validator.isValid) { return validator; } } } - return {isValid: true}; + return { isValid: true }; } isReference() { - return this.id.indexOf("#") !== -1 + return this.id.indexOf("#") !== -1; } isEnable() { @@ -285,8 +294,8 @@ export class Scenario extends BaseConfig { class DubboConfig extends BaseConfig { constructor(options = {}) { super(); - this.configCenter = new ConfigCenter(options.configCenter) - this.registryCenter = new RegistryCenter(options.registryCenter) + this.configCenter = new ConfigCenter(options.configCenter); + this.registryCenter = new RegistryCenter(options.registryCenter); if (options.consumerAndService === undefined) { options.consumerAndService = { timeout: undefined, @@ -296,10 +305,12 @@ class DubboConfig extends BaseConfig { group: undefined, connections: undefined, async: undefined, - loadBalance: undefined - } + loadBalance: undefined, + }; } - this.consumerAndService = new ConsumerAndService(options.consumerAndService) + this.consumerAndService = new ConsumerAndService( + options.consumerAndService + ); } } @@ -309,10 +320,10 @@ export class RequestFactory { DUBBO: "DUBBO", SQL: "SQL", TCP: "TCP", - } + }; constructor(options = {}) { - options.type = options.type || RequestFactory.TYPES.HTTP + options.type = options.type || RequestFactory.TYPES.HTTP; switch (options.type) { case RequestFactory.TYPES.DUBBO: return new DubboRequest(options); @@ -365,9 +376,11 @@ export class HttpRequest extends Request { this.doMultipartPost = options.doMultipartPost; this.connectTimeout = options.connectTimeout || 60 * 1000; this.responseTimeout = options.responseTimeout; - this.followRedirects = options.followRedirects === undefined ? true : options.followRedirects; - - this.sets({parameters: KeyValue, headers: KeyValue}, options); + this.followRedirects = + options.followRedirects === undefined ? true : options.followRedirects; + this.autoRedirects = + options.autoRedirects === undefined ? true : options.autoRedirects; + this.sets({ parameters: KeyValue, headers: KeyValue }, options); } isValid(environmentId, environment) { @@ -376,48 +389,57 @@ export class HttpRequest extends Request { if (!environmentId) { return { isValid: false, - info: 'api_test.request.please_configure_environment_in_scenario' - } + info: "api_test.request.please_configure_environment_in_scenario", + }; } let url = null; - if (environment && environment.config && environment.config.httpConfig - && environment.config.httpConfig.conditions && environment.config.httpConfig.conditions.length > 0) { - environment.config.httpConfig.conditions.forEach(item => { - if (item.type === 'NONE') { - url = item.protocol + '://' + item.socket; + if ( + environment && + environment.config && + environment.config.httpConfig && + environment.config.httpConfig.conditions && + environment.config.httpConfig.conditions.length > 0 + ) { + environment.config.httpConfig.conditions.forEach((item) => { + if (item.type === "NONE") { + url = item.protocol + "://" + item.socket; } - }) + }); } if (url === null) { - url = (environment && environment.config.httpConfig.socket) ? - environment.config.httpConfig.protocol + '://' + environment.config.httpConfig.socket : null; + url = + environment && environment.config.httpConfig.socket + ? environment.config.httpConfig.protocol + + "://" + + environment.config.httpConfig.socket + : null; } if (url === null) { return { isValid: false, - info: 'api_test.request.please_configure_socket_in_environment' - } + info: "api_test.request.please_configure_socket_in_environment", + }; } } else { if (!this.url) { return { isValid: false, - info: 'api_test.request.input_url' - } + info: "api_test.request.input_url", + }; } try { - new URL(this.url) + new URL(this.url); } catch (e) { return { isValid: false, - info: 'api_test.request.url_invalid' - } + info: "api_test.request.url_invalid", + }; } } } return { - isValid: true - } + isValid: true, + }; } showType() { @@ -427,14 +449,13 @@ export class HttpRequest extends Request { showMethod() { return this.method.toUpperCase(); } - } export class DubboRequest extends Request { static PROTOCOLS = { DUBBO: "dubbo://", RMI: "rmi://", - } + }; constructor(options = {}) { super(RequestFactory.TYPES.DUBBO, options); @@ -443,14 +464,16 @@ export class DubboRequest extends Request { this.method = options.method; this.configCenter = new ConfigCenter(options.configCenter); this.registryCenter = new RegistryCenter(options.registryCenter); - this.consumerAndService = new ConsumerAndService(options.consumerAndService); + this.consumerAndService = new ConsumerAndService( + options.consumerAndService + ); this.args = []; this.attachmentArgs = []; // Scenario.dubboConfig this.dubboConfig = undefined; this.debugReport = undefined; - this.sets({args: KeyValue, attachmentArgs: KeyValue}, options); + this.sets({ args: KeyValue, attachmentArgs: KeyValue }, options); } isValid() { @@ -458,31 +481,31 @@ export class DubboRequest extends Request { if (!this.interface) { return { isValid: false, - info: 'api_test.request.dubbo.input_interface' - } + info: "api_test.request.dubbo.input_interface", + }; } if (!this.method) { return { isValid: false, - info: 'api_test.request.dubbo.input_method' - } + info: "api_test.request.dubbo.input_method", + }; } if (!this.registryCenter.isValid()) { return { isValid: false, - info: 'api_test.request.dubbo.input_registry_center' - } + info: "api_test.request.dubbo.input_registry_center", + }; } if (!this.consumerAndService.isValid()) { return { isValid: false, - info: 'api_test.request.dubbo.input_consumer_service' - } + info: "api_test.request.dubbo.input_consumer_service", + }; } } return { - isValid: true - } + isValid: true, + }; } showType() { @@ -500,7 +523,6 @@ export class DubboRequest extends Request { } export class SqlRequest extends Request { - constructor(options = {}) { super(RequestFactory.TYPES.SQL, options); this.useEnvironment = options.useEnvironment; @@ -513,7 +535,10 @@ export class SqlRequest extends Request { // this.queryType = options.queryType; this.queryTimeout = options.queryTimeout || 60000; - this.sets({args: KeyValue, attachmentArgs: KeyValue, variables: KeyValue}, options); + this.sets( + { args: KeyValue, attachmentArgs: KeyValue, variables: KeyValue }, + options + ); } isValid() { @@ -521,19 +546,19 @@ export class SqlRequest extends Request { if (!this.name) { return { isValid: false, - info: 'api_test.request.sql.name_cannot_be_empty' - } + info: "api_test.request.sql.name_cannot_be_empty", + }; } if (!this.dataSource) { return { isValid: false, - info: 'api_test.request.sql.dataSource_cannot_be_empty' - } + info: "api_test.request.sql.dataSource_cannot_be_empty", + }; } } return { - isValid: true - } + isValid: true, + }; } showType() { @@ -550,7 +575,11 @@ export class SqlRequest extends Request { } export class TCPConfig extends BaseConfig { - static CLASSES = ["TCPClientImpl", "BinaryTCPClientImpl", "LengthPrefixedBinaryTCPClientImpl"] + static CLASSES = [ + "TCPClientImpl", + "BinaryTCPClientImpl", + "LengthPrefixedBinaryTCPClientImpl", + ]; constructor(options = {}) { super(); @@ -560,9 +589,11 @@ export class TCPConfig extends BaseConfig { this.ctimeout = options.ctimeout; // Connect this.timeout = options.timeout; // Response - this.reUseConnection = options.reUseConnection === undefined ? true : options.reUseConnection; + this.reUseConnection = + options.reUseConnection === undefined ? true : options.reUseConnection; this.nodelay = options.nodelay === undefined ? false : options.nodelay; - this.closeConnection = options.closeConnection === undefined ? false : options.closeConnection; + this.closeConnection = + options.closeConnection === undefined ? false : options.closeConnection; this.soLinger = options.soLinger; this.eolByte = options.eolByte; @@ -586,8 +617,8 @@ export class TCPRequest extends Request { isValid() { return { - isValid: true - } + isValid: true, + }; } showType() { @@ -603,7 +634,6 @@ export class TCPRequest extends Request { } } - export class ConfigCenter extends BaseConfig { static PROTOCOLS = ["zookeeper", "nacos", "apollo"]; @@ -621,12 +651,25 @@ export class ConfigCenter extends BaseConfig { } isValid() { - return !!this.protocol || !!this.group || !!this.namespace || !!this.username || !!this.address || !!this.password || !!this.timeout; + return ( + !!this.protocol || + !!this.group || + !!this.namespace || + !!this.username || + !!this.address || + !!this.password || + !!this.timeout + ); } } export class DatabaseConfig extends BaseConfig { - static DRIVER_CLASS = ["com.mysql.jdbc.Driver", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "org.postgresql.Driver", "oracle.jdbc.OracleDriver"]; + static DRIVER_CLASS = [ + "com.mysql.jdbc.Driver", + "com.microsoft.sqlserver.jdbc.SQLServerDriver", + "org.postgresql.Driver", + "oracle.jdbc.OracleDriver", + ]; constructor(options) { super(); @@ -648,12 +691,28 @@ export class DatabaseConfig extends BaseConfig { } isValid() { - return !!this.name || !!this.poolMax || !!this.timeout || !!this.driver || !!this.dbUrl || !!this.username || !!this.password; + return ( + !!this.name || + !!this.poolMax || + !!this.timeout || + !!this.driver || + !!this.dbUrl || + !!this.username || + !!this.password + ); } } export class RegistryCenter extends BaseConfig { - static PROTOCOLS = ["none", "zookeeper", "nacos", "apollo", "multicast", "redis", "simple"]; + static PROTOCOLS = [ + "none", + "zookeeper", + "nacos", + "apollo", + "multicast", + "redis", + "simple", + ]; constructor(options) { super(); @@ -668,13 +727,25 @@ export class RegistryCenter extends BaseConfig { } isValid() { - return !!this.protocol || !!this.group || !!this.username || !!this.address || !!this.password || !!this.timeout; + return ( + !!this.protocol || + !!this.group || + !!this.username || + !!this.address || + !!this.password || + !!this.timeout + ); } } export class ConsumerAndService extends BaseConfig { static ASYNC_OPTIONS = ["sync", "async"]; - static LOAD_BALANCE_OPTIONS = ["random", "roundrobin", "leastactive", "consistenthash"]; + static LOAD_BALANCE_OPTIONS = [ + "random", + "roundrobin", + "leastactive", + "consistenthash", + ]; constructor(options) { super(); @@ -691,7 +762,16 @@ export class ConsumerAndService extends BaseConfig { } isValid() { - return !!this.timeout || !!this.version || !!this.retries || !!this.cluster || !!this.group || !!this.connections || !!this.async || !!this.loadBalance; + return ( + !!this.timeout || + !!this.version || + !!this.retries || + !!this.cluster || + !!this.group || + !!this.connections || + !!this.async || + !!this.loadBalance + ); } } @@ -703,14 +783,14 @@ export class Body extends BaseConfig { this.kvs = []; this.set(options); - this.sets({kvs: KeyValue}, options); + this.sets({ kvs: KeyValue }, options); } isValid() { if (this.isKV()) { - return this.kvs.some(kv => { + return this.kvs.some((kv) => { return kv.isValid(); - }) + }); } else { return !!this.raw; } @@ -739,11 +819,11 @@ export class KeyValue extends BaseConfig { } isValid() { - return (!!this.name || !!this.value) && this.type !== 'file'; + return (!!this.name || !!this.value) && this.type !== "file"; } isFile() { - return (!!this.name || !!this.value) && this.type === 'file'; + return (!!this.name || !!this.value) && this.type === "file"; } } @@ -775,7 +855,16 @@ export class Assertions extends BaseConfig { this.duration = undefined; this.set(options); - this.sets({text: Text, regex: Regex, jsonPath: JSONPath, jsr223: AssertionJSR223, xpath2: XPath2}, options); + this.sets( + { + text: Text, + regex: Regex, + jsonPath: JSONPath, + jsr223: AssertionJSR223, + xpath2: XPath2, + }, + options + ); } initOptions(options) { @@ -849,7 +938,8 @@ export class JSONPath extends AssertionType { } setJSONPathDescription() { - this.description = this.expression + " expect: " + (this.expect ? this.expect : ''); + this.description = + this.expression + " expect: " + (this.expect ? this.expect : ""); } isValid() { @@ -898,8 +988,8 @@ export class Extract extends BaseConfig { let types = { json: ExtractJSONPath, xpath: ExtractXPath, - regex: ExtractRegex - } + regex: ExtractRegex, + }; this.sets(types, options); } } @@ -950,11 +1040,11 @@ export class ExtractXPath extends ExtractCommon { export class Controller extends BaseConfig { static TYPES = { IF_CONTROLLER: "If Controller", - } + }; constructor(type, options = {}) { super(); - this.type = type + this.type = type; options.id = options.id || uuid(); options.enable = options.enable === undefined ? true : options.enable; } @@ -1020,7 +1110,7 @@ export class TransactionController extends Controller { export class Timer extends BaseConfig { static TYPES = { CONSTANT_TIMER: "Constant Timer", - } + }; constructor(type, options = {}) { super(); @@ -1057,8 +1147,8 @@ const JMX_ASSERTION_CONDITION = { NOT: 1 << 2, EQUALS: 1 << 3, SUBSTRING: 1 << 4, - OR: 1 << 5 -} + OR: 1 << 5, +}; class JMXHttpRequest { constructor(request, environment) { @@ -1066,42 +1156,63 @@ class JMXHttpRequest { this.useEnvironment = request.useEnvironment; this.method = request.method; if (!request.useEnvironment) { - if (!request.url.startsWith("http://") && !request.url.startsWith("https://")) { - request.url = 'http://' + request.url; + if ( + !request.url.startsWith("http://") && + !request.url.startsWith("https://") + ) { + request.url = "http://" + request.url; } let url = new URL(request.url); this.domain = decodeURIComponent(url.hostname); this.port = url.port; this.protocol = url.protocol.split(":")[0]; - this.path = this.getPostQueryParameters(request, decodeURIComponent(url.pathname)); + this.path = this.getPostQueryParameters( + request, + decodeURIComponent(url.pathname) + ); } else { let isNewEnv = false; - if (environment && environment.config.httpConfig - && environment.config.httpConfig.conditions && environment.config.httpConfig.conditions.length > 0) { - environment.config.httpConfig.conditions.forEach(item => { - if (item.type === 'NONE') { + if ( + environment && + environment.config.httpConfig && + environment.config.httpConfig.conditions && + environment.config.httpConfig.conditions.length > 0 + ) { + environment.config.httpConfig.conditions.forEach((item) => { + if (item.type === "NONE") { isNewEnv = true; this.domain = item.domain; this.port = item.port; this.protocol = item.protocol; let url = new URL(item.protocol + "://" + item.socket); - let envPath = url.pathname === '/' ? '' : url.pathname; - this.path = this.getPostQueryParameters(request, decodeURIComponent(envPath + (request.path ? request.path : ''))); + let envPath = url.pathname === "/" ? "" : url.pathname; + this.path = this.getPostQueryParameters( + request, + decodeURIComponent(envPath + (request.path ? request.path : "")) + ); } - }) + }); } if (!isNewEnv) { this.domain = environment.config.httpConfig.domain; this.port = environment.config.httpConfig.port; this.protocol = environment.config.httpConfig.protocol; - let url = new URL(environment.config.httpConfig.protocol + "://" + environment.config.httpConfig.socket); - let envPath = url.pathname === '/' ? '' : url.pathname; - this.path = this.getPostQueryParameters(request, decodeURIComponent(envPath + (request.path ? request.path : ''))); + let url = new URL( + environment.config.httpConfig.protocol + + "://" + + environment.config.httpConfig.socket + ); + let envPath = url.pathname === "/" ? "" : url.pathname; + this.path = this.getPostQueryParameters( + request, + decodeURIComponent(envPath + (request.path ? request.path : "")) + ); } } this.connectTimeout = request.connectTimeout; this.responseTimeout = request.responseTimeout; this.followRedirects = request.followRedirects; + this.autoRedirects = request.autoRedirects; this.doMultipartPost = request.doMultipartPost; } } @@ -1109,19 +1220,19 @@ class JMXHttpRequest { getPostQueryParameters(request, path) { if (this.method.toUpperCase() !== "GET") { let parameters = []; - request.parameters.forEach(parameter => { + request.parameters.forEach((parameter) => { if (parameter.name && parameter.value && parameter.enable === true) { parameters.push(parameter); } }); if (parameters.length > 0) { - path += '?'; + path += "?"; } for (let i = 0; i < parameters.length; i++) { let parameter = parameters[i]; - path += (parameter.name + '=' + parameter.value); + path += parameter.name + "=" + parameter.value; if (i !== parameters.length - 1) { - path += '&'; + path += "&"; } } } @@ -1134,10 +1245,10 @@ class JMXDubboRequest { // Request 复制 let obj = request.clone(); // 去掉无效的kv - obj.args = obj.args.filter(arg => { + obj.args = obj.args.filter((arg) => { return arg.isValid(); }); - obj.attachmentArgs = obj.attachmentArgs.filter(arg => { + obj.attachmentArgs = obj.attachmentArgs.filter((arg) => { return arg.isValid(); }); @@ -1186,8 +1297,10 @@ class JMXTCPRequest { class JMeterTestPlan extends Element { constructor() { - super('jmeterTestPlan', { - version: "1.2", properties: "5.0", jmeter: "5.2.1" + super("jmeterTestPlan", { + version: "1.2", + properties: "5.0", + jmeter: "5.2.1", }); this.add(new HashTree()); @@ -1212,8 +1325,7 @@ class JMXGenerator { } addScenarios(testPlan, testId, scenarios) { - scenarios.forEach(s => { - + scenarios.forEach((s) => { if (s.isEnable()) { let scenario = s.clone(); @@ -1229,27 +1341,42 @@ class JMXGenerator { this.addAssertion(threadGroup, scenario); - scenario.requests.forEach(request => { + scenario.requests.forEach((request) => { if (request.enable) { if (!request.isValid()) return; let sampler; if (request instanceof DubboRequest) { - sampler = new DubboSample(request.name || "", new JMXDubboRequest(request, scenario.dubboConfig)); + sampler = new DubboSample( + request.name || "", + new JMXDubboRequest(request, scenario.dubboConfig) + ); } else if (request instanceof HttpRequest) { - sampler = new HTTPSamplerProxy(request.name || "", new JMXHttpRequest(request, scenario.environment)); + sampler = new HTTPSamplerProxy( + request.name || "", + new JMXHttpRequest(request, scenario.environment) + ); this.addRequestHeader(sampler, request, scenario); this.addRequestArguments(sampler, request); this.addRequestBody(sampler, request, testId); } else if (request instanceof SqlRequest) { - request.dataSource = scenario.databaseConfigMap.get(request.dataSource); + request.dataSource = scenario.databaseConfigMap.get( + request.dataSource + ); sampler = new JDBCSampler(request.name || "", request); } else if (request instanceof TCPRequest) { - sampler = new TCPSampler(request.name || "", new JMXTCPRequest(request, scenario)); + sampler = new TCPSampler( + request.name || "", + new JMXTCPRequest(request, scenario) + ); } this.addRequestVariables(sampler, request, scenario); - this.addDNSCacheManager(sampler, scenario.environment, request.useEnvironment); + this.addDNSCacheManager( + sampler, + scenario.environment, + request.useEnvironment + ); this.addRequestExtractor(sampler, request); @@ -1259,7 +1386,11 @@ class JMXGenerator { this.addConstantsTimer(sampler, request); - if (request.controller && request.controller.isValid() && request.controller.enable) { + if ( + request.controller && + request.controller.isValid() && + request.controller.enable + ) { if (request.controller instanceof IfController) { let controller = this.getController(sampler, request); threadGroup.put(controller); @@ -1268,16 +1399,15 @@ class JMXGenerator { threadGroup.put(sampler); } } - }) + }); testPlan.put(threadGroup); } - - }) + }); } addEnvironments(environments, target) { let targetMap = new Map(); - target.forEach(item => { + target.forEach((item) => { if (item.name) { targetMap.set(item.name, item.enable); } @@ -1286,18 +1416,18 @@ class JMXGenerator { if (!(envArray instanceof Array)) { envArray = JSON.parse(environments); } - envArray.forEach(item => { + envArray.forEach((item) => { let targetItem = targetMap.get(item.name); let hasItem = undefined; if (targetItem) { - hasItem = (targetItem.enable === false ? false : true); + hasItem = targetItem.enable === false ? false : true; } else { hasItem = false; } if (item.enable != false && item.name && !hasItem) { - target.push(new KeyValue({name: item.name, value: item.value})); + target.push(new KeyValue({ name: item.name, value: item.value })); } - }) + }); } addScenarioVariables(threadGroup, scenario) { @@ -1317,7 +1447,7 @@ class JMXGenerator { if (!request.variables) { request.variables = []; } - this.addEnvironments(config.commonConfig.variables, request.variables) + this.addEnvironments(config.commonConfig.variables, request.variables); } let name = request.name + " Variables"; if (request.variables) { @@ -1343,9 +1473,12 @@ class JMXGenerator { // 强化判断,如果未匹配到合适的host则不开启DNSCache let domain = environment.config.httpConfig.domain; let validHosts = []; - hosts.forEach(item => { + hosts.forEach((item) => { if (item.domain !== undefined && domain !== undefined) { - let d = item.domain.trim().replace("http://", "").replace("https://", ""); + let d = item.domain + .trim() + .replace("http://", "") + .replace("https://", ""); if (d === domain.trim()) { item.domain = d; // 域名去掉协议 validHosts.push(item); @@ -1362,7 +1495,7 @@ class JMXGenerator { addJDBCDataSources(threadGroup, scenario) { let names = new Set(); let databaseConfigMap = new Map(); - scenario.databaseConfigs.forEach(config => { + scenario.databaseConfigs.forEach((config) => { let name = config.name + "JDBCDataSource"; threadGroup.put(new JDBCDataSource(name, config)); names.add(name); @@ -1373,7 +1506,7 @@ class JMXGenerator { if (!(scenario.environment.config instanceof Object)) { config = JSON.parse(scenario.environment.config); } - config.databaseConfigs.forEach(config => { + config.databaseConfigs.forEach((config) => { if (!names.has(config.name)) { let name = config.name + "JDBCDataSource"; threadGroup.put(new JDBCDataSource(name, config)); @@ -1423,7 +1556,9 @@ class JMXGenerator { addConstantsTimer(sampler, request) { if (request.timer && request.timer.isValid() && request.timer.enable) { - sampler.put(new JMXConstantTimer(request.timer.getLabel(), request.timer)); + sampler.put( + new JMXConstantTimer(request.timer.getLabel(), request.timer) + ); } } @@ -1431,12 +1566,12 @@ class JMXGenerator { if (request.controller.isValid() && request.controller.enable) { if (request.controller instanceof IfController) { let name = request.controller.getLabel(); - let variable = "\"" + request.controller.variable + "\""; + let variable = '"' + request.controller.variable + '"'; let operator = request.controller.operator; - let value = "\"" + request.controller.value + "\""; + let value = '"' + request.controller.value + '"'; if (operator === "=~" || operator === "!~") { - value = "\".*" + request.controller.value + ".*\""; + value = '".*' + request.controller.value + '.*"'; } if (operator === "is empty") { @@ -1452,7 +1587,7 @@ class JMXGenerator { } let condition = "${__jexl3(" + variable + operator + value + ")}"; - let controller = new JMXIfController(name, {condition: condition}); + let controller = new JMXIfController(name, { condition: condition }); controller.put(sampler); return controller; } @@ -1464,13 +1599,13 @@ class JMXGenerator { if (!request.body.isKV() && bodyFormat) { switch (bodyFormat) { case BODY_FORMAT.JSON: - this.addContentType(request, 'application/json'); + this.addContentType(request, "application/json"); break; case BODY_FORMAT.HTML: - this.addContentType(request, 'text/html'); + this.addContentType(request, "text/html"); break; case BODY_FORMAT.XML: - this.addContentType(request, 'text/xml'); + this.addContentType(request, "text/xml"); break; default: break; @@ -1482,21 +1617,27 @@ class JMXGenerator { let hasContentType = false; for (let index in request.headers) { if (request.headers.hasOwnProperty(index)) { - if (request.headers[index].name === 'Content-Type' && request.headers[index].enable != false) { + if ( + request.headers[index].name === "Content-Type" && + request.headers[index].enable !== false + ) { hasContentType = true; break; } } } if (!hasContentType) { - request.headers.push(new KeyValue({name: 'Content-Type', value: type})); + request.headers.push(new KeyValue({ name: "Content-Type", value: type })); } } removeContentType(request) { for (let index in request.headers) { if (request.headers.hasOwnProperty(index)) { - if (request.headers[index].name === 'Content-Type' && request.headers[index].enable != false) { + if ( + request.headers[index].name === "Content-Type" && + request.headers[index].enable != false + ) { request.headers.splice(index, 1); break; } @@ -1518,12 +1659,17 @@ class JMXGenerator { this.addRequestBodyFile(httpSamplerProxy, request, testId); } else { if (request.body.raw) { - httpSamplerProxy.boolProp('HTTPSampler.postBodyRaw', true); - body.push({name: '', value: request.body.raw, encode: false, enable: true}); + httpSamplerProxy.boolProp("HTTPSampler.postBodyRaw", true); + body.push({ + name: "", + value: request.body.raw, + encode: false, + enable: true, + }); } } - if (request.method !== 'GET') { + if (request.method !== "GET") { httpSamplerProxy.add(new HTTPSamplerArguments(body)); } } @@ -1531,12 +1677,13 @@ class JMXGenerator { addRequestBodyFile(httpSamplerProxy, request, testId) { let files = []; let kvs = this.filterKVFile(request.body.kvs); - kvs.forEach(kv => { - if ((kv.enable !== false) && kv.files) { - kv.files.forEach(file => { + kvs.forEach((kv) => { + if (kv.enable !== false && kv.files) { + kv.files.forEach((file) => { let arg = {}; arg.name = kv.name; - arg.value = BODY_FILE_DIR + '/' + testId + '/' + file.id + '_' + file.name; + arg.value = + BODY_FILE_DIR + "/" + testId + "/" + file.id + "_" + file.name; arg.contentType = kv.contentType; files.push(arg); }); @@ -1548,32 +1695,34 @@ class JMXGenerator { addAssertion(httpSamplerProxy, request) { let assertions = request.assertions; if (assertions.regex.length > 0) { - assertions.regex.filter(this.filter).forEach(regex => { + assertions.regex.filter(this.filter).forEach((regex) => { httpSamplerProxy.put(this.getResponseAssertion(regex)); - }) + }); } if (assertions.jsonPath.length > 0) { - assertions.jsonPath.filter(this.filter).forEach(item => { + assertions.jsonPath.filter(this.filter).forEach((item) => { httpSamplerProxy.put(this.getJSONPathAssertion(item)); - }) + }); } if (assertions.xpath2.length > 0) { - assertions.xpath2.filter(this.filter).forEach(item => { + assertions.xpath2.filter(this.filter).forEach((item) => { httpSamplerProxy.put(this.getXpathAssertion(item)); - }) + }); } if (assertions.jsr223.length > 0) { - assertions.jsr223.filter(this.filter).forEach(item => { + assertions.jsr223.filter(this.filter).forEach((item) => { httpSamplerProxy.put(this.getJSR223Assertion(item)); - }) + }); } if (assertions.duration.isValid()) { - let name = "Response In Time: " + assertions.duration.value - httpSamplerProxy.put(new DurationAssertion(name, assertions.duration.value)); + let name = "Response In Time: " + assertions.duration.value; + httpSamplerProxy.put( + new DurationAssertion(name, assertions.duration.value) + ); } } @@ -1610,21 +1759,21 @@ class JMXGenerator { addRequestExtractor(httpSamplerProxy, request) { let extract = request.extract; if (extract.regex.length > 0) { - extract.regex.filter(this.filter).forEach(regex => { + extract.regex.filter(this.filter).forEach((regex) => { httpSamplerProxy.put(this.getExtractor(regex)); - }) + }); } if (extract.json.length > 0) { - extract.json.filter(this.filter).forEach(json => { + extract.json.filter(this.filter).forEach((json) => { httpSamplerProxy.put(this.getExtractor(json)); - }) + }); } if (extract.xpath.length > 0) { - extract.xpath.filter(this.filter).forEach(xpath => { + extract.xpath.filter(this.filter).forEach((xpath) => { httpSamplerProxy.put(this.getExtractor(xpath)); - }) + }); } } @@ -1632,9 +1781,9 @@ class JMXGenerator { let props = { name: extractCommon.variable, expression: extractCommon.expression, - match: extractCommon.multipleMatching ? -1 : undefined - } - let testName = props.name + match: extractCommon.multipleMatching ? -1 : undefined, + }; + let testName = props.name; switch (extractCommon.type) { case EXTRACT_TYPE.REGEX: testName += " RegexExtractor"; @@ -1659,7 +1808,7 @@ class JMXGenerator { } filterKVFile(kvs) { - return kvs.filter(kv => { + return kvs.filter((kv) => { return kv.isFile(); }); } @@ -1670,5 +1819,3 @@ class JMXGenerator { return xml; } } - - diff --git a/framework/sdk-parent/sdk/src/main/java/io/metersphere/log/vo/api/DefinitionReference.java b/framework/sdk-parent/sdk/src/main/java/io/metersphere/log/vo/api/DefinitionReference.java index c1535bcad1..0279e4f0d9 100644 --- a/framework/sdk-parent/sdk/src/main/java/io/metersphere/log/vo/api/DefinitionReference.java +++ b/framework/sdk-parent/sdk/src/main/java/io/metersphere/log/vo/api/DefinitionReference.java @@ -58,6 +58,7 @@ public class DefinitionReference { httpColumns.put("responseTimeout", "响应超时"); httpColumns.put("alias", "证书别名"); httpColumns.put("followRedirects", "跟随重定向"); + httpColumns.put("autoRedirects", "自动重定向"); // http auth authColumns.put("verification", "认证方式"); authColumns.put("username", "用户名");