From a08dc695ba1a09a0629dbfd3b5f60b617ea8c0f3 Mon Sep 17 00:00:00 2001 From: ylnxwlp <2720203124@qq.com> Date: Mon, 3 Nov 2025 18:01:05 +0800 Subject: [PATCH] fix: fix the bugs about http-method-specify in spring-webmvc-v6x and incorrect time unit in dashboard (#3569) * Fix the bugs mentioned in issue #3562 and #3563. Fix the issue in the spring-webmvc-v6x-adapter where, after configuring http-method-specify:true, the prefixes for different request methods were not concatenated. Fix incorrect time unit in the tooltip when adding a new circuit breaker rule with statistical window duration. * Fix the bugs mentioned in issue #3562 and #3563. Fix the issue in the spring-webmvc-v6x-adapter where, after configuring http-method-specify:true, the prefixes for different request methods were not concatenated. Fix incorrect time unit in the tooltip when adding a new circuit breaker rule with statistical window duration. * Fixed the issue where the original test class was affected by the concatenation of resource names after adding the logic for the HTTP prefix concatenation. * remove mseHttpMethodSpecify field and update the unit test as suggested. * retrigger CI --- .../webmvc_v6x/SentinelWebInterceptor.java | 4 + .../config/SentinelWebMvcConfig.java | 13 -- .../SentinelSpringMvcIntegrationTest.java | 6 + ...nelWebInterceptorHttpMethodPrefixTest.java | 127 ++++++++++++++++++ .../src/main/webapp/resources/dist/js/app.js | 2 +- 5 files changed, 138 insertions(+), 14 deletions(-) create mode 100644 sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebInterceptorHttpMethodPrefixTest.java diff --git a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebInterceptor.java b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebInterceptor.java index 524bd040..14d1e23e 100644 --- a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebInterceptor.java +++ b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebInterceptor.java @@ -18,6 +18,7 @@ package com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x; import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.config.SentinelWebMvcConfig; import com.alibaba.csp.sentinel.adapter.web.common.UrlCleaner; +import com.alibaba.csp.sentinel.util.StringUtil; import jakarta.servlet.http.HttpServletRequest; import org.springframework.web.servlet.HandlerMapping; @@ -62,6 +63,9 @@ public class SentinelWebInterceptor extends AbstractSentinelInterceptor { if (config.isContextPathSpecify() && request.getContextPath() != null) { resourceName = request.getContextPath() + resourceName; } + if (StringUtil.isNotEmpty(resourceName) && config.isHttpMethodSpecify()) { + resourceName = request.getMethod().toUpperCase() + ":" + resourceName; + } return resourceName; } diff --git a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/config/SentinelWebMvcConfig.java b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/config/SentinelWebMvcConfig.java index 97ffc5cd..ab72e257 100755 --- a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/config/SentinelWebMvcConfig.java +++ b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/config/SentinelWebMvcConfig.java @@ -34,11 +34,6 @@ public class SentinelWebMvcConfig extends BaseWebMvcConfig { */ private boolean httpMethodSpecify; - /** - * Specify whether the URL resource name should contain the HTTP method prefix in MSE (e.g. {@code GET:}). - */ - private static boolean mseHttpMethodSpecify; - /** * Specify whether unify web context(i.e. use the default context name), and is true by default. @@ -56,10 +51,6 @@ public class SentinelWebMvcConfig extends BaseWebMvcConfig { super(); setRequestAttributeName(DEFAULT_REQUEST_ATTRIBUTE_NAME); try { - String enable = System.getProperty("spring.cloud.mse.sentinel.web.http-method-prefix","true"); - if(enable != null){ - mseHttpMethodSpecify = Boolean.parseBoolean(enable); - } String enableContextPath = System.getProperty("spring.cloud.ahas.sentinel.web.context-path", "true"); if (enableContextPath != null) { contextPathSpecify = Boolean.parseBoolean(enableContextPath); @@ -86,10 +77,6 @@ public class SentinelWebMvcConfig extends BaseWebMvcConfig { return this; } - public static boolean isMseHttpMethodSpecify() { - return mseHttpMethodSpecify; - } - public boolean isWebContextUnify() { return webContextUnify; } diff --git a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelSpringMvcIntegrationTest.java b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelSpringMvcIntegrationTest.java index f7e7ac77..ac8a8447 100644 --- a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelSpringMvcIntegrationTest.java +++ b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelSpringMvcIntegrationTest.java @@ -33,6 +33,7 @@ import com.alibaba.csp.sentinel.util.StringUtil; import java.util.Collections; import org.junit.After; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -54,6 +55,11 @@ public class SentinelSpringMvcIntegrationTest { @Autowired private MockMvc mvc; + @BeforeClass + public static void disableMseHttpMethodPrefix() { + System.setProperty("spring.cloud.mse.sentinel.web.http-method-prefix", "false"); + } + @Test public void testBase() throws Exception { String url = "/hello"; diff --git a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebInterceptorHttpMethodPrefixTest.java b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebInterceptorHttpMethodPrefixTest.java new file mode 100644 index 00000000..df7355e0 --- /dev/null +++ b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebInterceptorHttpMethodPrefixTest.java @@ -0,0 +1,127 @@ +package com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x; + +import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.config.SentinelWebMvcConfig; +import com.alibaba.csp.sentinel.adapter.web.common.UrlCleaner; +import jakarta.servlet.http.HttpServletRequest; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.web.servlet.HandlerMapping; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.when; + +/** + * The test for the resource request prefix concatenation in spring-webmvc-v6x. + * + * @author ylnxwlp + */ +public class SentinelWebInterceptorHttpMethodPrefixTest { + + private SentinelWebInterceptor interceptor; + private HttpServletRequest mockRequest; + + @Before + public void setUp() { + mockRequest = Mockito.mock(HttpServletRequest.class); + } + + @Test + public void testGetResourceNameWithHttpMethodSpecifyEnabled() { + SentinelWebMvcConfig config = new SentinelWebMvcConfig(); + config.setHttpMethodSpecify(true); + interceptor = new SentinelWebInterceptor(config); + when(mockRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)) + .thenReturn("/test/path"); + when(mockRequest.getMethod()).thenReturn("POST"); + when(mockRequest.getContextPath()).thenReturn(""); + String resourceName = interceptor.getResourceName(mockRequest); + assertEquals("POST:/test/path", resourceName); + } + + @Test + public void testGetResourceNameWithHttpMethodSpecifyDisabled() { + SentinelWebMvcConfig config = new SentinelWebMvcConfig(); + config.setHttpMethodSpecify(false); + interceptor = new SentinelWebInterceptor(config); + when(mockRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)) + .thenReturn("/no/prefix"); + when(mockRequest.getMethod()).thenReturn("DELETE"); + when(mockRequest.getContextPath()).thenReturn(""); + String resourceName = interceptor.getResourceName(mockRequest); + assertEquals("/no/prefix", resourceName); + } + + @Test + public void testGetResourceNameEmptyResourceNameShouldReturnEmptyString() { + SentinelWebMvcConfig config = new SentinelWebMvcConfig(); + config.setHttpMethodSpecify(true); + interceptor = new SentinelWebInterceptor(config); + when(mockRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)) + .thenReturn(""); + when(mockRequest.getMethod()).thenReturn("PUT"); + when(mockRequest.getContextPath()).thenReturn(""); + String resourceName = interceptor.getResourceName(mockRequest); + assertEquals("", resourceName); + } + + @Test + public void testGetResourceNameNullResourceNameShouldReturnNull() { + SentinelWebMvcConfig config = new SentinelWebMvcConfig(); + config.setHttpMethodSpecify(true); + interceptor = new SentinelWebInterceptor(config); + when(mockRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)) + .thenReturn(null); + when(mockRequest.getMethod()).thenReturn("PATCH"); + String resourceName = interceptor.getResourceName(mockRequest); + assertNull(resourceName); + } + + @Test + public void testGetResourceNameWithUrlCleaner() { + SentinelWebMvcConfig config = new SentinelWebMvcConfig(); + config.setHttpMethodSpecify(true); + config.setUrlCleaner(new UrlCleaner() { + @Override + public String clean(String originUrl) { + return "/cleaned"; + } + }); + interceptor = new SentinelWebInterceptor(config); + when(mockRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)) + .thenReturn("/dirty/path"); + when(mockRequest.getMethod()).thenReturn("GET"); + when(mockRequest.getContextPath()).thenReturn(""); + String resourceName = interceptor.getResourceName(mockRequest); + assertEquals("GET:/cleaned", resourceName); + } + + @Test + public void testGetResourceNameWithContextPath() { + SentinelWebMvcConfig config = new SentinelWebMvcConfig(); + config.setHttpMethodSpecify(true); + config.setContextPathSpecify(true); + interceptor = new SentinelWebInterceptor(config); + when(mockRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)) + .thenReturn("/api/user"); + when(mockRequest.getMethod()).thenReturn("GET"); + when(mockRequest.getContextPath()).thenReturn("/myapp"); + String resourceName = interceptor.getResourceName(mockRequest); + assertEquals("GET:/myapp/api/user", resourceName); + } + + @Test + public void testGetResourceNameWithContextPathDisabled() { + SentinelWebMvcConfig config = new SentinelWebMvcConfig(); + config.setHttpMethodSpecify(true); + config.setContextPathSpecify(false); + interceptor = new SentinelWebInterceptor(config); + when(mockRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)) + .thenReturn("/api/user"); + when(mockRequest.getMethod()).thenReturn("GET"); + when(mockRequest.getContextPath()).thenReturn("/myapp"); + String resourceName = interceptor.getResourceName(mockRequest); + assertEquals("GET:/api/user", resourceName); + } +} \ No newline at end of file diff --git a/sentinel-dashboard/src/main/webapp/resources/dist/js/app.js b/sentinel-dashboard/src/main/webapp/resources/dist/js/app.js index fa4c66e0..47f8b570 100755 --- a/sentinel-dashboard/src/main/webapp/resources/dist/js/app.js +++ b/sentinel-dashboard/src/main/webapp/resources/dist/js/app.js @@ -1 +1 @@ -"use strict";var app;angular.module("sentinelDashboardApp",["oc.lazyLoad","ui.router","ui.bootstrap","angular-loading-bar","ngDialog","ui.bootstrap.datetimepicker","ui-notification","rzTable","angular-clipboard","selectize","angularUtils.directives.dirPagination"]).factory("AuthInterceptor",["$window","$state",function(r,t){return{responseError:function(e){return 401===e.status&&(r.localStorage.removeItem("session_sentinel_admin"),t.go("login")),e},response:function(e){return e},request:function(e){var t=r.document.getElementsByTagName("base")[0].href;return e.url=t+e.url,e},requestError:function(e){return e}}}]).config(["$stateProvider","$urlRouterProvider","$ocLazyLoadProvider","$httpProvider",function(e,t,r,a){a.interceptors.push("AuthInterceptor"),r.config({debug:!1,events:!0}),t.otherwise("/dashboard/home"),e.state("login",{url:"/login",templateUrl:"app/views/login.html",controller:"LoginCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/login.js"]})}]}}).state("dashboard",{url:"/dashboard",templateUrl:"app/views/dashboard/main.html",resolve:{loadMyDirectives:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/directives/header/header.js","app/scripts/directives/sidebar/sidebar.js","app/scripts/directives/sidebar/sidebar-search/sidebar-search.js"]})}]}}).state("dashboard.home",{url:"/home",templateUrl:"app/views/dashboard/home.html",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/main.js"]})}]}}).state("dashboard.flowV1",{templateUrl:"app/views/flow_v1.html",url:"/flow/:app",controller:"FlowControllerV1",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/flow_v1.js"]})}]}}).state("dashboard.flow",{templateUrl:"app/views/flow_v2.html",url:"/v2/flow/:app",controller:"FlowControllerV2",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/flow_v2.js"]})}]}}).state("dashboard.paramFlow",{templateUrl:"app/views/param_flow.html",url:"/paramFlow/:app",controller:"ParamFlowController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/param_flow.js"]})}]}}).state("dashboard.clusterAppAssignManage",{templateUrl:"app/views/cluster_app_assign_manage.html",url:"/cluster/assign_manage/:app",controller:"SentinelClusterAppAssignManageController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/cluster_app_assign_manage.js"]})}]}}).state("dashboard.clusterAppServerList",{templateUrl:"app/views/cluster_app_server_list.html",url:"/cluster/server/:app",controller:"SentinelClusterAppServerListController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/cluster_app_server_list.js"]})}]}}).state("dashboard.clusterAppClientList",{templateUrl:"app/views/cluster_app_client_list.html",url:"/cluster/client/:app",controller:"SentinelClusterAppTokenClientListController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/cluster_app_token_client_list.js"]})}]}}).state("dashboard.clusterSingle",{templateUrl:"app/views/cluster_single_config.html",url:"/cluster/single/:app",controller:"SentinelClusterSingleController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/cluster_single.js"]})}]}}).state("dashboard.authority",{templateUrl:"app/views/authority.html",url:"/authority/:app",controller:"AuthorityRuleController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/authority.js"]})}]}}).state("dashboard.degrade",{templateUrl:"app/views/degrade.html",url:"/degrade/:app",controller:"DegradeCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/degrade.js"]})}]}}).state("dashboard.system",{templateUrl:"app/views/system.html",url:"/system/:app",controller:"SystemCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/system.js"]})}]}}).state("dashboard.machine",{templateUrl:"app/views/machine.html",url:"/app/:app",controller:"MachineCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/machine.js"]})}]}}).state("dashboard.identity",{templateUrl:"app/views/identity.html",url:"/identity/:app",controller:"IdentityCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/identity.js"]})}]}}).state("dashboard.gatewayIdentity",{templateUrl:"app/views/gateway/identity.html",url:"/gateway/identity/:app",controller:"GatewayIdentityCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/gateway/identity.js"]})}]}}).state("dashboard.metric",{templateUrl:"app/views/metric.html",url:"/metric/:app",controller:"MetricCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/metric.js"]})}]}}).state("dashboard.gatewayApi",{templateUrl:"app/views/gateway/api.html",url:"/gateway/api/:app",controller:"GatewayApiCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/gateway/api.js"]})}]}}).state("dashboard.gatewayFlow",{templateUrl:"app/views/gateway/flow.html",url:"/gateway/flow/:app",controller:"GatewayFlowCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/gateway/flow.js"]})}]}})}]),(app=angular.module("sentinelDashboardApp")).filter("range",[function(){return function(e,t){if(isNaN(t)||t<=0)return[];e=[];for(var r=1;r<=t;r++)e.push(r);return e}}]),(app=angular.module("sentinelDashboardApp")).service("VersionService",["$http",function(e){this.version=function(){return e({url:"/version",method:"GET"})}}]),(app=angular.module("sentinelDashboardApp")).service("AuthService",["$http",function(t){this.check=function(){return t({url:"/auth/check",method:"POST"})},this.login=function(e){return t({url:"/auth/login",params:e,method:"POST"})},this.logout=function(){return t({url:"/auth/logout",method:"POST"})}}]),(app=angular.module("sentinelDashboardApp")).service("AppService",["$http",function(e){this.getApps=function(){return e({url:"app/briefinfos.json",method:"GET"})}}]),(app=angular.module("sentinelDashboardApp")).service("FlowServiceV1",["$http",function(a){function t(e){return void 0===e||""===e||isNaN(e)||e<=0}this.queryMachineRules=function(e,t,r){return a({url:"/v1/flow/rules",params:{app:e,ip:t,port:r},method:"GET"})},this.newRule=function(e){e.resource,e.limitApp,e.grade,e.count,e.strategy,e.refResource,e.controlBehavior,e.warmUpPeriodSec,e.maxQueueingTimeMs,e.app,e.ip,e.port;return a({url:"/v1/flow/rule",data:e,method:"POST"})},this.saveRule=function(e){var t={id:e.id,resource:e.resource,limitApp:e.limitApp,grade:e.grade,count:e.count,strategy:e.strategy,refResource:e.refResource,controlBehavior:e.controlBehavior,warmUpPeriodSec:e.warmUpPeriodSec,maxQueueingTimeMs:e.maxQueueingTimeMs};return a({url:"/v1/flow/save.json",params:t,method:"PUT"})},this.deleteRule=function(e){var t={id:e.id,app:e.app};return a({url:"/v1/flow/delete.json",params:t,method:"DELETE"})},this.checkRuleValid=function(e){return void 0===e.resource||""===e.resource?(alert("资源名称不能为空"),!1):void 0===e.count||e.count<0?(alert("限流阈值必须大于等于 0"),!1):void 0===e.strategy||e.strategy<0?(alert("无效的流控模式"),!1):1!=e.strategy&&2!=e.strategy||void 0!==e.refResource&&""!=e.refResource?void 0===e.controlBehavior||e.controlBehavior<0?(alert("无效的流控整形方式"),!1):1==e.controlBehavior&&t(e.warmUpPeriodSec)?(alert("预热时长必须大于 0"),!1):2==e.controlBehavior&&t(e.maxQueueingTimeMs)?(alert("排队超时时间必须大于 0"),!1):!e.clusterMode||void 0!==e.clusterConfig&&void 0!==e.clusterConfig.thresholdType||(alert("集群限流配置不正确"),!1):(alert("请填写关联资源或入口"),!1)}}]),(app=angular.module("sentinelDashboardApp")).service("FlowServiceV2",["$http",function(a){function t(e){return void 0===e||""===e||isNaN(e)||e<=0}this.queryMachineRules=function(e,t,r){return a({url:"/v2/flow/rules",params:{app:e,ip:t,port:r},method:"GET"})},this.newRule=function(e){return a({url:"/v2/flow/rule",data:e,method:"POST"})},this.saveRule=function(e){return a({url:"/v2/flow/rule/"+e.id,data:e,method:"PUT"})},this.deleteRule=function(e){return a({url:"/v2/flow/rule/"+e.id,method:"DELETE"})},this.checkRuleValid=function(e){return void 0===e.resource||""===e.resource?(alert("资源名称不能为空"),!1):void 0===e.count||e.count<0?(alert("限流阈值必须大于等于 0"),!1):void 0===e.strategy||e.strategy<0?(alert("无效的流控模式"),!1):1!=e.strategy&&2!=e.strategy||void 0!==e.refResource&&""!=e.refResource?void 0===e.controlBehavior||e.controlBehavior<0?(alert("无效的流控整形方式"),!1):1==e.controlBehavior&&t(e.warmUpPeriodSec)?(alert("预热时长必须大于 0"),!1):2==e.controlBehavior&&t(e.maxQueueingTimeMs)?(alert("排队超时时间必须大于 0"),!1):!e.clusterMode||void 0!==e.clusterConfig&&void 0!==e.clusterConfig.thresholdType||(alert("集群限流配置不正确"),!1):(alert("请填写关联资源或入口"),!1)}}]),(app=angular.module("sentinelDashboardApp")).service("DegradeService",["$http",function(a){this.queryMachineRules=function(e,t,r){return a({url:"degrade/rules.json",params:{app:e,ip:t,port:r},method:"GET"})},this.newRule=function(e){return a({url:"/degrade/rule",data:e,method:"POST"})},this.saveRule=function(e){var t={id:e.id,resource:e.resource,limitApp:e.limitApp,grade:e.grade,count:e.count,timeWindow:e.timeWindow,statIntervalMs:e.statIntervalMs,minRequestAmount:e.minRequestAmount,slowRatioThreshold:e.slowRatioThreshold};return a({url:"/degrade/rule/"+e.id,data:t,method:"PUT"})},this.deleteRule=function(e){return a({url:"/degrade/rule/"+e.id,method:"DELETE"})},this.checkRuleValid=function(e){if(void 0===e.resource||""===e.resource)return alert("资源名称不能为空"),!1;if(void 0===e.grade||e.grade<0)return alert("未知的降级策略"),!1;if(void 0===e.count||""===e.count||e.count<0)return alert("降级阈值不能为空或小于 0"),!1;if(null==e.timeWindow||""===e.timeWindow||e.timeWindow<=0)return alert("熔断时长必须大于 0s"),!1;if(null==e.minRequestAmount||e.minRequestAmount<=0)return alert("最小请求数目需大于 0"),!1;if(null==e.statIntervalMs||e.statIntervalMs<=0)return alert("统计窗口时长需大于 0s"),!1;if(void 0!==e.statIntervalMs&&12e4