@@ -38,6 +38,11 @@ | |||||
<artifactId>sentinel-parameter-flow-control</artifactId> | <artifactId>sentinel-parameter-flow-control</artifactId> | ||||
<version>${project.version}</version> | <version>${project.version}</version> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<artifactId>sentinel-api-gateway-adapter-common</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>org.springframework.boot</groupId> | <groupId>org.springframework.boot</groupId> | ||||
@@ -30,9 +30,12 @@ import java.util.concurrent.CompletableFuture; | |||||
import java.util.concurrent.ExecutionException; | import java.util.concurrent.ExecutionException; | ||||
import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; | |||||
import com.alibaba.csp.sentinel.command.CommandConstants; | import com.alibaba.csp.sentinel.command.CommandConstants; | ||||
import com.alibaba.csp.sentinel.config.SentinelConfig; | import com.alibaba.csp.sentinel.config.SentinelConfig; | ||||
import com.alibaba.csp.sentinel.command.vo.NodeVo; | import com.alibaba.csp.sentinel.command.vo.NodeVo; | ||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity; | |||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity; | |||||
import com.alibaba.csp.sentinel.dashboard.util.AsyncUtils; | import com.alibaba.csp.sentinel.dashboard.util.AsyncUtils; | ||||
import com.alibaba.csp.sentinel.slots.block.Rule; | import com.alibaba.csp.sentinel.slots.block.Rule; | ||||
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; | import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; | ||||
@@ -109,6 +112,12 @@ public class SentinelApiClient { | |||||
private static final String MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH = "cluster/server/modifyFlowConfig"; | private static final String MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH = "cluster/server/modifyFlowConfig"; | ||||
private static final String MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH = "cluster/server/modifyNamespaceSet"; | private static final String MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH = "cluster/server/modifyNamespaceSet"; | ||||
private static final String FETCH_GATEWAY_API_PATH = "gateway/getApiDefinitions"; | |||||
private static final String MODIFY_GATEWAY_API_PATH = "gateway/updateApiDefinitions"; | |||||
private static final String FETCH_GATEWAY_FLOW_RULE_PATH = "gateway/getRules"; | |||||
private static final String MODIFY_GATEWAY_FLOW_RULE_PATH = "gateway/updateRules"; | |||||
private static final String FLOW_RULE_TYPE = "flow"; | private static final String FLOW_RULE_TYPE = "flow"; | ||||
private static final String DEGRADE_RULE_TYPE = "degrade"; | private static final String DEGRADE_RULE_TYPE = "degrade"; | ||||
private static final String SYSTEM_RULE_TYPE = "system"; | private static final String SYSTEM_RULE_TYPE = "system"; | ||||
@@ -693,4 +702,90 @@ public class SentinelApiClient { | |||||
return AsyncUtils.newFailedFuture(ex); | return AsyncUtils.newFailedFuture(ex); | ||||
} | } | ||||
} | } | ||||
public CompletableFuture<List<ApiDefinitionEntity>> fetchApis(String app, String ip, int port) { | |||||
if (StringUtil.isBlank(ip) || port <= 0) { | |||||
return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); | |||||
} | |||||
try { | |||||
return executeCommand(ip, port, FETCH_GATEWAY_API_PATH, false) | |||||
.thenApply(r -> { | |||||
List<ApiDefinitionEntity> entities = JSON.parseArray(r, ApiDefinitionEntity.class); | |||||
if (entities != null) { | |||||
for (ApiDefinitionEntity entity : entities) { | |||||
entity.setApp(app); | |||||
entity.setIp(ip); | |||||
entity.setPort(port); | |||||
} | |||||
} | |||||
return entities; | |||||
}); | |||||
} catch (Exception ex) { | |||||
logger.warn("Error when fetching gateway apis", ex); | |||||
return AsyncUtils.newFailedFuture(ex); | |||||
} | |||||
} | |||||
public boolean modifyApis(String app, String ip, int port, List<ApiDefinitionEntity> apis) { | |||||
if (apis == null) { | |||||
return true; | |||||
} | |||||
try { | |||||
AssertUtil.notEmpty(app, "Bad app name"); | |||||
AssertUtil.notEmpty(ip, "Bad machine IP"); | |||||
AssertUtil.isTrue(port > 0, "Bad machine port"); | |||||
String data = JSON.toJSONString( | |||||
apis.stream().map(r -> r.toApiDefinition()).collect(Collectors.toList())); | |||||
Map<String, String> params = new HashMap<>(2); | |||||
params.put("data", data); | |||||
String result = executeCommand(app, ip, port, MODIFY_GATEWAY_API_PATH, params, true).get(); | |||||
logger.info("Modify gateway apis: {}", result); | |||||
return true; | |||||
} catch (Exception e) { | |||||
logger.warn("Error when modifying gateway apis", e); | |||||
return false; | |||||
} | |||||
} | |||||
public CompletableFuture<List<GatewayFlowRuleEntity>> fetchGatewayFlowRules(String app, String ip, int port) { | |||||
if (StringUtil.isBlank(ip) || port <= 0) { | |||||
return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); | |||||
} | |||||
try { | |||||
return executeCommand(ip, port, FETCH_GATEWAY_FLOW_RULE_PATH, false) | |||||
.thenApply(r -> { | |||||
List<GatewayFlowRule> gatewayFlowRules = JSON.parseArray(r, GatewayFlowRule.class); | |||||
List<GatewayFlowRuleEntity> entities = gatewayFlowRules.stream().map(rule -> GatewayFlowRuleEntity.fromGatewayFlowRule(app, ip, port, rule)).collect(Collectors.toList()); | |||||
return entities; | |||||
}); | |||||
} catch (Exception ex) { | |||||
logger.warn("Error when fetching gateway flow rules", ex); | |||||
return AsyncUtils.newFailedFuture(ex); | |||||
} | |||||
} | |||||
public boolean modifyGatewayFlowRules(String app, String ip, int port, List<GatewayFlowRuleEntity> rules) { | |||||
if (rules == null) { | |||||
return true; | |||||
} | |||||
try { | |||||
AssertUtil.notEmpty(app, "Bad app name"); | |||||
AssertUtil.notEmpty(ip, "Bad machine IP"); | |||||
AssertUtil.isTrue(port > 0, "Bad machine port"); | |||||
String data = JSON.toJSONString( | |||||
rules.stream().map(r -> r.toGatewayFlowRule()).collect(Collectors.toList())); | |||||
Map<String, String> params = new HashMap<>(2); | |||||
params.put("data", data); | |||||
String result = executeCommand(app, ip, port, MODIFY_GATEWAY_FLOW_RULE_PATH, params, true).get(); | |||||
logger.info("Modify gateway flow rules: {}", result); | |||||
return true; | |||||
} catch (Exception e) { | |||||
logger.warn("Error when modifying gateway apis", e); | |||||
return false; | |||||
} | |||||
} | |||||
} | } |
@@ -63,17 +63,7 @@ public class AppController { | |||||
return Result.ofSuccess(null); | return Result.ofSuccess(null); | ||||
} | } | ||||
List<MachineInfo> list = new ArrayList<>(appInfo.getMachines()); | List<MachineInfo> list = new ArrayList<>(appInfo.getMachines()); | ||||
Collections.sort(list, (o1, o2) -> { | |||||
int t = o1.getApp().compareTo(o2.getApp()); | |||||
if (t != 0) { | |||||
return t; | |||||
} | |||||
t = o1.getIp().compareTo(o2.getIp()); | |||||
if (t != 0) { | |||||
return t; | |||||
} | |||||
return o1.getPort().compareTo(o2.getPort()); | |||||
}); | |||||
Collections.sort(list, Comparator.comparing(MachineInfo::getApp).thenComparing(MachineInfo::getIp).thenComparingInt(MachineInfo::getPort)); | |||||
return Result.ofSuccess(MachineInfoVo.fromMachineInfoList(list)); | return Result.ofSuccess(MachineInfoVo.fromMachineInfoList(list)); | ||||
} | } | ||||
@@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.http.MediaType; | import org.springframework.http.MediaType; | ||||
import org.springframework.stereotype.Controller; | import org.springframework.stereotype.Controller; | ||||
import org.springframework.web.bind.annotation.RequestMapping; | import org.springframework.web.bind.annotation.RequestMapping; | ||||
import org.springframework.web.bind.annotation.RequestParam; | |||||
import org.springframework.web.bind.annotation.ResponseBody; | import org.springframework.web.bind.annotation.ResponseBody; | ||||
@Controller | @Controller | ||||
@@ -40,7 +41,7 @@ public class MachineRegistryController { | |||||
@ResponseBody | @ResponseBody | ||||
@RequestMapping("/machine") | @RequestMapping("/machine") | ||||
public Result<?> receiveHeartBeat(String app, Long version, String v, String hostname, String ip, Integer port) { | |||||
public Result<?> receiveHeartBeat(String app, @RequestParam(value = "app_type", required = false, defaultValue = "0") Integer appType, Long version, String v, String hostname, String ip, Integer port) { | |||||
if (app == null) { | if (app == null) { | ||||
app = MachineDiscovery.UNKNOWN_APP_NAME; | app = MachineDiscovery.UNKNOWN_APP_NAME; | ||||
} | } | ||||
@@ -59,6 +60,7 @@ public class MachineRegistryController { | |||||
try { | try { | ||||
MachineInfo machineInfo = new MachineInfo(); | MachineInfo machineInfo = new MachineInfo(); | ||||
machineInfo.setApp(app); | machineInfo.setApp(app); | ||||
machineInfo.setAppType(appType); | |||||
machineInfo.setHostname(hostname); | machineInfo.setHostname(hostname); | ||||
machineInfo.setIp(ip); | machineInfo.setIp(ip); | ||||
machineInfo.setPort(port); | machineInfo.setPort(port); | ||||
@@ -0,0 +1,270 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.controller.gateway; | |||||
import com.alibaba.csp.sentinel.dashboard.auth.AuthService; | |||||
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; | |||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity; | |||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiPredicateItemEntity; | |||||
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.Result; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.AddApiReqVo; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.ApiPredicateItemVo; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.UpdateApiReqVo; | |||||
import com.alibaba.csp.sentinel.dashboard.repository.gateway.InMemApiDefinitionStore; | |||||
import com.alibaba.csp.sentinel.util.StringUtil; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.util.CollectionUtils; | |||||
import org.springframework.web.bind.annotation.*; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import java.util.*; | |||||
import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*; | |||||
/** | |||||
* Gateway api Controller for manage gateway api definitions. | |||||
* | |||||
* @author cdfive | |||||
* @since 1.7.0 | |||||
*/ | |||||
@RestController | |||||
@RequestMapping(value = "/gateway/api") | |||||
public class GatewayApiController { | |||||
private final Logger logger = LoggerFactory.getLogger(GatewayApiController.class); | |||||
@Autowired | |||||
private InMemApiDefinitionStore repository; | |||||
@Autowired | |||||
private SentinelApiClient sentinelApiClient; | |||||
@Autowired | |||||
private AuthService<HttpServletRequest> authService; | |||||
@GetMapping("/list.json") | |||||
public Result<List<ApiDefinitionEntity>> queryApis(HttpServletRequest request, String app, String ip, Integer port) { | |||||
AuthService.AuthUser authUser = authService.getAuthUser(request); | |||||
authUser.authTarget(app, AuthService.PrivilegeType.READ_RULE); | |||||
if (StringUtil.isEmpty(app)) { | |||||
return Result.ofFail(-1, "app can't be null or empty"); | |||||
} | |||||
if (StringUtil.isEmpty(ip)) { | |||||
return Result.ofFail(-1, "ip can't be null or empty"); | |||||
} | |||||
if (port == null) { | |||||
return Result.ofFail(-1, "port can't be null"); | |||||
} | |||||
try { | |||||
List<ApiDefinitionEntity> apis = sentinelApiClient.fetchApis(app, ip, port).get(); | |||||
repository.saveAll(apis); | |||||
return Result.ofSuccess(apis); | |||||
} catch (Throwable throwable) { | |||||
logger.error("queryApis error:", throwable); | |||||
return Result.ofThrowable(-1, throwable); | |||||
} | |||||
} | |||||
@PostMapping("/new.json") | |||||
public Result<ApiDefinitionEntity> addApi(HttpServletRequest request, @RequestBody AddApiReqVo reqVo) { | |||||
AuthService.AuthUser authUser = authService.getAuthUser(request); | |||||
String app = reqVo.getApp(); | |||||
if (StringUtil.isBlank(app)) { | |||||
return Result.ofFail(-1, "app can't be null or empty"); | |||||
} | |||||
authUser.authTarget(app, AuthService.PrivilegeType.WRITE_RULE); | |||||
ApiDefinitionEntity entity = new ApiDefinitionEntity(); | |||||
entity.setApp(app.trim()); | |||||
String ip = reqVo.getIp(); | |||||
if (StringUtil.isBlank(ip)) { | |||||
return Result.ofFail(-1, "ip can't be null or empty"); | |||||
} | |||||
entity.setIp(ip.trim()); | |||||
Integer port = reqVo.getPort(); | |||||
if (port == null) { | |||||
return Result.ofFail(-1, "port can't be null"); | |||||
} | |||||
entity.setPort(port); | |||||
// API名称 | |||||
String apiName = reqVo.getApiName(); | |||||
if (StringUtil.isBlank(apiName)) { | |||||
return Result.ofFail(-1, "apiName can't be null or empty"); | |||||
} | |||||
entity.setApiName(apiName.trim()); | |||||
// 匹配规则列表 | |||||
List<ApiPredicateItemVo> predicateItems = reqVo.getPredicateItems(); | |||||
if (CollectionUtils.isEmpty(predicateItems)) { | |||||
return Result.ofFail(-1, "predicateItems can't empty"); | |||||
} | |||||
List<ApiPredicateItemEntity> predicateItemEntities = new ArrayList<>(); | |||||
for (ApiPredicateItemVo predicateItem : predicateItems) { | |||||
ApiPredicateItemEntity predicateItemEntity = new ApiPredicateItemEntity(); | |||||
// 匹配模式 | |||||
Integer matchStrategy = predicateItem.getMatchStrategy(); | |||||
if (!Arrays.asList(URL_MATCH_STRATEGY_EXACT, URL_MATCH_STRATEGY_PREFIX, URL_MATCH_STRATEGY_REGEX).contains(matchStrategy)) { | |||||
return Result.ofFail(-1, "invalid matchStrategy: " + matchStrategy); | |||||
} | |||||
predicateItemEntity.setMatchStrategy(matchStrategy); | |||||
// 匹配串 | |||||
String pattern = predicateItem.getPattern(); | |||||
if (StringUtil.isBlank(pattern)) { | |||||
return Result.ofFail(-1, "pattern can't be null or empty"); | |||||
} | |||||
predicateItemEntity.setPattern(pattern); | |||||
predicateItemEntities.add(predicateItemEntity); | |||||
} | |||||
entity.setPredicateItems(new LinkedHashSet<>(predicateItemEntities)); | |||||
// 检查API名称不能重复 | |||||
List<ApiDefinitionEntity> allApis = repository.findAllByMachine(MachineInfo.of(app.trim(), ip.trim(), port)); | |||||
if (allApis.stream().map(o -> o.getApiName()).anyMatch(o -> o.equals(apiName.trim()))) { | |||||
return Result.ofFail(-1, "apiName exists: " + apiName); | |||||
} | |||||
Date date = new Date(); | |||||
entity.setGmtCreate(date); | |||||
entity.setGmtModified(date); | |||||
try { | |||||
entity = repository.save(entity); | |||||
} catch (Throwable throwable) { | |||||
logger.error("add gateway api error:", throwable); | |||||
return Result.ofThrowable(-1, throwable); | |||||
} | |||||
if (!publishApis(app, ip, port)) { | |||||
logger.warn("publish gateway apis fail after add"); | |||||
} | |||||
return Result.ofSuccess(entity); | |||||
} | |||||
@PostMapping("/save.json") | |||||
public Result<ApiDefinitionEntity> updateApi(HttpServletRequest request, @RequestBody UpdateApiReqVo reqVo) { | |||||
AuthService.AuthUser authUser = authService.getAuthUser(request); | |||||
String app = reqVo.getApp(); | |||||
if (StringUtil.isBlank(app)) { | |||||
return Result.ofFail(-1, "app can't be null or empty"); | |||||
} | |||||
authUser.authTarget(app, AuthService.PrivilegeType.WRITE_RULE); | |||||
Long id = reqVo.getId(); | |||||
if (id == null) { | |||||
return Result.ofFail(-1, "id can't be null"); | |||||
} | |||||
ApiDefinitionEntity entity = repository.findById(id); | |||||
if (entity == null) { | |||||
return Result.ofFail(-1, "api does not exist, id=" + id); | |||||
} | |||||
// 匹配规则列表 | |||||
List<ApiPredicateItemVo> predicateItems = reqVo.getPredicateItems(); | |||||
if (CollectionUtils.isEmpty(predicateItems)) { | |||||
return Result.ofFail(-1, "predicateItems can't empty"); | |||||
} | |||||
List<ApiPredicateItemEntity> predicateItemEntities = new ArrayList<>(); | |||||
for (ApiPredicateItemVo predicateItem : predicateItems) { | |||||
ApiPredicateItemEntity predicateItemEntity = new ApiPredicateItemEntity(); | |||||
// 匹配模式 | |||||
int matchStrategy = predicateItem.getMatchStrategy(); | |||||
if (!Arrays.asList(URL_MATCH_STRATEGY_EXACT, URL_MATCH_STRATEGY_PREFIX, URL_MATCH_STRATEGY_REGEX).contains(matchStrategy)) { | |||||
return Result.ofFail(-1, "Invalid matchStrategy: " + matchStrategy); | |||||
} | |||||
predicateItemEntity.setMatchStrategy(matchStrategy); | |||||
// 匹配串 | |||||
String pattern = predicateItem.getPattern(); | |||||
if (StringUtil.isBlank(pattern)) { | |||||
return Result.ofFail(-1, "pattern can't be null or empty"); | |||||
} | |||||
predicateItemEntity.setPattern(pattern); | |||||
predicateItemEntities.add(predicateItemEntity); | |||||
} | |||||
entity.setPredicateItems(new LinkedHashSet<>(predicateItemEntities)); | |||||
Date date = new Date(); | |||||
entity.setGmtModified(date); | |||||
try { | |||||
entity = repository.save(entity); | |||||
} catch (Throwable throwable) { | |||||
logger.error("update gateway api error:", throwable); | |||||
return Result.ofThrowable(-1, throwable); | |||||
} | |||||
if (!publishApis(app, entity.getIp(), entity.getPort())) { | |||||
logger.warn("publish gateway apis fail after update"); | |||||
} | |||||
return Result.ofSuccess(entity); | |||||
} | |||||
@PostMapping("/delete.json") | |||||
public Result<Long> deleteApi(HttpServletRequest request, Long id) { | |||||
AuthService.AuthUser authUser = authService.getAuthUser(request); | |||||
if (id == null) { | |||||
return Result.ofFail(-1, "id can't be null"); | |||||
} | |||||
ApiDefinitionEntity oldEntity = repository.findById(id); | |||||
if (oldEntity == null) { | |||||
return Result.ofSuccess(null); | |||||
} | |||||
authUser.authTarget(oldEntity.getApp(), AuthService.PrivilegeType.DELETE_RULE); | |||||
try { | |||||
repository.delete(id); | |||||
} catch (Throwable throwable) { | |||||
logger.error("delete gateway api error:", throwable); | |||||
return Result.ofThrowable(-1, throwable); | |||||
} | |||||
if (!publishApis(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { | |||||
logger.warn("publish gateway apis fail after delete"); | |||||
} | |||||
return Result.ofSuccess(id); | |||||
} | |||||
private boolean publishApis(String app, String ip, Integer port) { | |||||
List<ApiDefinitionEntity> apis = repository.findAllByMachine(MachineInfo.of(app, ip, port)); | |||||
return sentinelApiClient.modifyApis(app, ip, port, apis); | |||||
} | |||||
} |
@@ -0,0 +1,443 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.controller.gateway; | |||||
import com.alibaba.csp.sentinel.dashboard.auth.AuthService; | |||||
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; | |||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity; | |||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayParamFlowItemEntity; | |||||
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.Result; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.AddFlowRuleReqVo; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.GatewayParamFlowItemVo; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.UpdateFlowRuleReqVo; | |||||
import com.alibaba.csp.sentinel.dashboard.repository.gateway.InMemGatewayFlowRuleStore; | |||||
import com.alibaba.csp.sentinel.util.StringUtil; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.web.bind.annotation.*; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import java.util.Arrays; | |||||
import java.util.Date; | |||||
import java.util.List; | |||||
import static com.alibaba.csp.sentinel.slots.block.RuleConstant.*; | |||||
import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*; | |||||
import static com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity.*; | |||||
/** | |||||
* Gateway flow rule Controller for manage gateway flow rules. | |||||
* | |||||
* @author cdfive | |||||
* @since 1.7.0 | |||||
*/ | |||||
@RestController | |||||
@RequestMapping(value = "/gateway/flow") | |||||
public class GatewayFlowRuleController { | |||||
private final Logger logger = LoggerFactory.getLogger(GatewayFlowRuleController.class); | |||||
@Autowired | |||||
private InMemGatewayFlowRuleStore repository; | |||||
@Autowired | |||||
private SentinelApiClient sentinelApiClient; | |||||
@Autowired | |||||
private AuthService<HttpServletRequest> authService; | |||||
@GetMapping("/list.json") | |||||
public Result<List<GatewayFlowRuleEntity>> queryFlowRules(HttpServletRequest request, String app, String ip, Integer port) { | |||||
AuthService.AuthUser authUser = authService.getAuthUser(request); | |||||
authUser.authTarget(app, AuthService.PrivilegeType.READ_RULE); | |||||
if (StringUtil.isEmpty(app)) { | |||||
return Result.ofFail(-1, "app can't be null or empty"); | |||||
} | |||||
if (StringUtil.isEmpty(ip)) { | |||||
return Result.ofFail(-1, "ip can't be null or empty"); | |||||
} | |||||
if (port == null) { | |||||
return Result.ofFail(-1, "port can't be null"); | |||||
} | |||||
try { | |||||
List<GatewayFlowRuleEntity> rules = sentinelApiClient.fetchGatewayFlowRules(app, ip, port).get(); | |||||
repository.saveAll(rules); | |||||
return Result.ofSuccess(rules); | |||||
} catch (Throwable throwable) { | |||||
logger.error("query gateway flow rules error:", throwable); | |||||
return Result.ofThrowable(-1, throwable); | |||||
} | |||||
} | |||||
@PostMapping("/new.json") | |||||
public Result<GatewayFlowRuleEntity> addFlowRule(HttpServletRequest request, @RequestBody AddFlowRuleReqVo reqVo) { | |||||
AuthService.AuthUser authUser = authService.getAuthUser(request); | |||||
String app = reqVo.getApp(); | |||||
if (StringUtil.isBlank(app)) { | |||||
return Result.ofFail(-1, "app can't be null or empty"); | |||||
} | |||||
authUser.authTarget(app, AuthService.PrivilegeType.WRITE_RULE); | |||||
GatewayFlowRuleEntity entity = new GatewayFlowRuleEntity(); | |||||
entity.setApp(app.trim()); | |||||
String ip = reqVo.getIp(); | |||||
if (StringUtil.isBlank(ip)) { | |||||
return Result.ofFail(-1, "ip can't be null or empty"); | |||||
} | |||||
entity.setIp(ip.trim()); | |||||
Integer port = reqVo.getPort(); | |||||
if (port == null) { | |||||
return Result.ofFail(-1, "port can't be null"); | |||||
} | |||||
entity.setPort(port); | |||||
// API类型, Route ID或API分组 | |||||
Integer resourceMode = reqVo.getResourceMode(); | |||||
if (resourceMode == null) { | |||||
return Result.ofFail(-1, "resourceMode can't be null"); | |||||
} | |||||
if (!Arrays.asList(RESOURCE_MODE_ROUTE_ID, RESOURCE_MODE_CUSTOM_API_NAME).contains(resourceMode)) { | |||||
return Result.ofFail(-1, "invalid resourceMode: " + resourceMode); | |||||
} | |||||
entity.setResourceMode(resourceMode); | |||||
// API名称 | |||||
String resource = reqVo.getResource(); | |||||
if (StringUtil.isBlank(resource)) { | |||||
return Result.ofFail(-1, "resource can't be null or empty"); | |||||
} | |||||
entity.setResource(resource.trim()); | |||||
// 针对请求属性 | |||||
GatewayParamFlowItemVo paramItem = reqVo.getParamItem(); | |||||
if (paramItem != null) { | |||||
GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity(); | |||||
entity.setParamItem(itemEntity); | |||||
// 参数属性 0-ClientIP 1-Remote Host 2-Header 3-URL参数 4-Cookie | |||||
Integer parseStrategy = paramItem.getParseStrategy(); | |||||
if (!Arrays.asList(PARAM_PARSE_STRATEGY_CLIENT_IP, PARAM_PARSE_STRATEGY_HOST, PARAM_PARSE_STRATEGY_HEADER | |||||
, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) { | |||||
return Result.ofFail(-1, "invalid parseStrategy: " + parseStrategy); | |||||
} | |||||
itemEntity.setParseStrategy(paramItem.getParseStrategy()); | |||||
// 当参数属性为2-Header 3-URL参数 4-Cookie时,参数名称必填 | |||||
if (Arrays.asList(PARAM_PARSE_STRATEGY_HEADER, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) { | |||||
// 参数名称 | |||||
String fieldName = paramItem.getFieldName(); | |||||
if (StringUtil.isBlank(fieldName)) { | |||||
return Result.ofFail(-1, "fieldName can't be null or empty"); | |||||
} | |||||
itemEntity.setFieldName(paramItem.getFieldName()); | |||||
String pattern = paramItem.getPattern(); | |||||
// 如果匹配串不为空,验证匹配模式 | |||||
if (StringUtil.isNotEmpty(pattern)) { | |||||
itemEntity.setPattern(pattern); | |||||
Integer matchStrategy = paramItem.getMatchStrategy(); | |||||
if (!Arrays.asList(PARAM_MATCH_STRATEGY_EXACT, PARAM_MATCH_STRATEGY_CONTAINS, PARAM_MATCH_STRATEGY_REGEX).contains(matchStrategy)) { | |||||
return Result.ofFail(-1, "invalid matchStrategy: " + matchStrategy); | |||||
} | |||||
itemEntity.setMatchStrategy(matchStrategy); | |||||
} | |||||
} | |||||
} | |||||
// 阈值类型 0-线程数 1-QPS | |||||
Integer grade = reqVo.getGrade(); | |||||
if (grade == null) { | |||||
return Result.ofFail(-1, "grade can't be null"); | |||||
} | |||||
if (!Arrays.asList(FLOW_GRADE_THREAD, FLOW_GRADE_QPS).contains(grade)) { | |||||
return Result.ofFail(-1, "invalid grade: " + grade); | |||||
} | |||||
entity.setGrade(grade); | |||||
// QPS阈值 | |||||
Double count = reqVo.getCount(); | |||||
if (count == null) { | |||||
return Result.ofFail(-1, "count can't be null"); | |||||
} | |||||
if (count < 0) { | |||||
return Result.ofFail(-1, "count should be at lease zero"); | |||||
} | |||||
entity.setCount(count); | |||||
// 间隔 | |||||
Long interval = reqVo.getInterval(); | |||||
if (interval == null) { | |||||
return Result.ofFail(-1, "interval can't be null"); | |||||
} | |||||
if (interval <= 0) { | |||||
return Result.ofFail(-1, "interval should be greater than zero"); | |||||
} | |||||
entity.setInterval(interval); | |||||
// 间隔单位 | |||||
Integer intervalUnit = reqVo.getIntervalUnit(); | |||||
if (intervalUnit == null) { | |||||
return Result.ofFail(-1, "intervalUnit can't be null"); | |||||
} | |||||
if (!Arrays.asList(INTERVAL_UNIT_SECOND, INTERVAL_UNIT_MINUTE, INTERVAL_UNIT_HOUR, INTERVAL_UNIT_DAY).contains(intervalUnit)) { | |||||
return Result.ofFail(-1, "Invalid intervalUnit: " + intervalUnit); | |||||
} | |||||
entity.setIntervalUnit(intervalUnit); | |||||
// 流控方式 0-快速失败 2-匀速排队 | |||||
Integer controlBehavior = reqVo.getControlBehavior(); | |||||
if (controlBehavior == null) { | |||||
return Result.ofFail(-1, "controlBehavior can't be null"); | |||||
} | |||||
if (!Arrays.asList(CONTROL_BEHAVIOR_DEFAULT, CONTROL_BEHAVIOR_RATE_LIMITER).contains(controlBehavior)) { | |||||
return Result.ofFail(-1, "invalid controlBehavior: " + controlBehavior); | |||||
} | |||||
entity.setControlBehavior(controlBehavior); | |||||
if (CONTROL_BEHAVIOR_DEFAULT == controlBehavior) { | |||||
// 0-快速失败, 则Burst size必填 | |||||
Integer burst = reqVo.getBurst(); | |||||
if (burst == null) { | |||||
return Result.ofFail(-1, "burst can't be null"); | |||||
} | |||||
if (burst < 0) { | |||||
return Result.ofFail(-1, "invalid burst: " + burst); | |||||
} | |||||
entity.setBurst(burst); | |||||
} else if (CONTROL_BEHAVIOR_RATE_LIMITER == controlBehavior) { | |||||
// 1-匀速排队, 则超时时间必填 | |||||
Integer maxQueueingTimeoutMs = reqVo.getMaxQueueingTimeoutMs(); | |||||
if (maxQueueingTimeoutMs == null) { | |||||
return Result.ofFail(-1, "maxQueueingTimeoutMs can't be null"); | |||||
} | |||||
if (maxQueueingTimeoutMs < 0) { | |||||
return Result.ofFail(-1, "invalid maxQueueingTimeoutMs: " + maxQueueingTimeoutMs); | |||||
} | |||||
entity.setMaxQueueingTimeoutMs(maxQueueingTimeoutMs); | |||||
} | |||||
Date date = new Date(); | |||||
entity.setGmtCreate(date); | |||||
entity.setGmtModified(date); | |||||
try { | |||||
entity = repository.save(entity); | |||||
} catch (Throwable throwable) { | |||||
logger.error("add gateway flow rule error:", throwable); | |||||
return Result.ofThrowable(-1, throwable); | |||||
} | |||||
if (!publishRules(app, ip, port)) { | |||||
logger.warn("publish gateway flow rules fail after add"); | |||||
} | |||||
return Result.ofSuccess(entity); | |||||
} | |||||
@PostMapping("/save.json") | |||||
public Result<GatewayFlowRuleEntity> updateFlowRule(HttpServletRequest request, @RequestBody UpdateFlowRuleReqVo reqVo) { | |||||
AuthService.AuthUser authUser = authService.getAuthUser(request); | |||||
String app = reqVo.getApp(); | |||||
if (StringUtil.isBlank(app)) { | |||||
return Result.ofFail(-1, "app can't be null or empty"); | |||||
} | |||||
authUser.authTarget(app, AuthService.PrivilegeType.WRITE_RULE); | |||||
Long id = reqVo.getId(); | |||||
if (id == null) { | |||||
return Result.ofFail(-1, "id can't be null"); | |||||
} | |||||
GatewayFlowRuleEntity entity = repository.findById(id); | |||||
if (entity == null) { | |||||
return Result.ofFail(-1, "gateway flow rule does not exist, id=" + id); | |||||
} | |||||
// 针对请求属性 | |||||
GatewayParamFlowItemVo paramItem = reqVo.getParamItem(); | |||||
if (paramItem != null) { | |||||
GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity(); | |||||
entity.setParamItem(itemEntity); | |||||
// 参数属性 0-ClientIP 1-Remote Host 2-Header 3-URL参数 4-Cookie | |||||
Integer parseStrategy = paramItem.getParseStrategy(); | |||||
if (!Arrays.asList(PARAM_PARSE_STRATEGY_CLIENT_IP, PARAM_PARSE_STRATEGY_HOST, PARAM_PARSE_STRATEGY_HEADER | |||||
, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) { | |||||
return Result.ofFail(-1, "invalid parseStrategy: " + parseStrategy); | |||||
} | |||||
itemEntity.setParseStrategy(paramItem.getParseStrategy()); | |||||
// 当参数属性为2-Header 3-URL参数 4-Cookie时,参数名称必填 | |||||
if (Arrays.asList(PARAM_PARSE_STRATEGY_HEADER, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) { | |||||
// 参数名称 | |||||
String fieldName = paramItem.getFieldName(); | |||||
if (StringUtil.isBlank(fieldName)) { | |||||
return Result.ofFail(-1, "fieldName can't be null or empty"); | |||||
} | |||||
itemEntity.setFieldName(paramItem.getFieldName()); | |||||
String pattern = paramItem.getPattern(); | |||||
// 如果匹配串不为空,验证匹配模式 | |||||
if (StringUtil.isNotEmpty(pattern)) { | |||||
itemEntity.setPattern(pattern); | |||||
Integer matchStrategy = paramItem.getMatchStrategy(); | |||||
if (!Arrays.asList(PARAM_MATCH_STRATEGY_EXACT, PARAM_MATCH_STRATEGY_CONTAINS, PARAM_MATCH_STRATEGY_REGEX).contains(matchStrategy)) { | |||||
return Result.ofFail(-1, "invalid matchStrategy: " + matchStrategy); | |||||
} | |||||
itemEntity.setMatchStrategy(matchStrategy); | |||||
} | |||||
} | |||||
} else { | |||||
entity.setParamItem(null); | |||||
} | |||||
// 阈值类型 0-线程数 1-QPS | |||||
Integer grade = reqVo.getGrade(); | |||||
if (grade == null) { | |||||
return Result.ofFail(-1, "grade can't be null"); | |||||
} | |||||
if (!Arrays.asList(FLOW_GRADE_THREAD, FLOW_GRADE_QPS).contains(grade)) { | |||||
return Result.ofFail(-1, "invalid grade: " + grade); | |||||
} | |||||
entity.setGrade(grade); | |||||
// QPS阈值 | |||||
Double count = reqVo.getCount(); | |||||
if (count == null) { | |||||
return Result.ofFail(-1, "count can't be null"); | |||||
} | |||||
if (count < 0) { | |||||
return Result.ofFail(-1, "count should be at lease zero"); | |||||
} | |||||
entity.setCount(count); | |||||
// 间隔 | |||||
Long interval = reqVo.getInterval(); | |||||
if (interval == null) { | |||||
return Result.ofFail(-1, "interval can't be null"); | |||||
} | |||||
if (interval <= 0) { | |||||
return Result.ofFail(-1, "interval should be greater than zero"); | |||||
} | |||||
entity.setInterval(interval); | |||||
// 间隔单位 | |||||
Integer intervalUnit = reqVo.getIntervalUnit(); | |||||
if (intervalUnit == null) { | |||||
return Result.ofFail(-1, "intervalUnit can't be null"); | |||||
} | |||||
if (!Arrays.asList(INTERVAL_UNIT_SECOND, INTERVAL_UNIT_MINUTE, INTERVAL_UNIT_HOUR, INTERVAL_UNIT_DAY).contains(intervalUnit)) { | |||||
return Result.ofFail(-1, "Invalid intervalUnit: " + intervalUnit); | |||||
} | |||||
entity.setIntervalUnit(intervalUnit); | |||||
// 流控方式 0-快速失败 2-匀速排队 | |||||
Integer controlBehavior = reqVo.getControlBehavior(); | |||||
if (controlBehavior == null) { | |||||
return Result.ofFail(-1, "controlBehavior can't be null"); | |||||
} | |||||
if (!Arrays.asList(CONTROL_BEHAVIOR_DEFAULT, CONTROL_BEHAVIOR_RATE_LIMITER).contains(controlBehavior)) { | |||||
return Result.ofFail(-1, "invalid controlBehavior: " + controlBehavior); | |||||
} | |||||
entity.setControlBehavior(controlBehavior); | |||||
if (CONTROL_BEHAVIOR_DEFAULT == controlBehavior) { | |||||
// 0-快速失败, 则Burst size必填 | |||||
Integer burst = reqVo.getBurst(); | |||||
if (burst == null) { | |||||
return Result.ofFail(-1, "burst can't be null"); | |||||
} | |||||
if (burst < 0) { | |||||
return Result.ofFail(-1, "invalid burst: " + burst); | |||||
} | |||||
entity.setBurst(burst); | |||||
} else if (CONTROL_BEHAVIOR_RATE_LIMITER == controlBehavior) { | |||||
// 2-匀速排队, 则超时时间必填 | |||||
Integer maxQueueingTimeoutMs = reqVo.getMaxQueueingTimeoutMs(); | |||||
if (maxQueueingTimeoutMs == null) { | |||||
return Result.ofFail(-1, "maxQueueingTimeoutMs can't be null"); | |||||
} | |||||
if (maxQueueingTimeoutMs < 0) { | |||||
return Result.ofFail(-1, "invalid maxQueueingTimeoutMs: " + maxQueueingTimeoutMs); | |||||
} | |||||
entity.setMaxQueueingTimeoutMs(maxQueueingTimeoutMs); | |||||
} | |||||
Date date = new Date(); | |||||
entity.setGmtModified(date); | |||||
try { | |||||
entity = repository.save(entity); | |||||
} catch (Throwable throwable) { | |||||
logger.error("update gateway flow rule error:", throwable); | |||||
return Result.ofThrowable(-1, throwable); | |||||
} | |||||
if (!publishRules(app, entity.getIp(), entity.getPort())) { | |||||
logger.warn("publish gateway flow rules fail after update"); | |||||
} | |||||
return Result.ofSuccess(entity); | |||||
} | |||||
@PostMapping("/delete.json") | |||||
public Result<Long> deleteFlowRule(HttpServletRequest request, Long id) { | |||||
AuthService.AuthUser authUser = authService.getAuthUser(request); | |||||
if (id == null) { | |||||
return Result.ofFail(-1, "id can't be null"); | |||||
} | |||||
GatewayFlowRuleEntity oldEntity = repository.findById(id); | |||||
if (oldEntity == null) { | |||||
return Result.ofSuccess(null); | |||||
} | |||||
authUser.authTarget(oldEntity.getApp(), AuthService.PrivilegeType.DELETE_RULE); | |||||
try { | |||||
repository.delete(id); | |||||
} catch (Throwable throwable) { | |||||
logger.error("delete gateway flow rule error:", throwable); | |||||
return Result.ofThrowable(-1, throwable); | |||||
} | |||||
if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { | |||||
logger.warn("publish gateway flow rules fail after delete"); | |||||
} | |||||
return Result.ofSuccess(id); | |||||
} | |||||
private boolean publishRules(String app, String ip, Integer port) { | |||||
List<GatewayFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); | |||||
return sentinelApiClient.modifyGatewayFlowRules(app, ip, port, rules); | |||||
} | |||||
} |
@@ -28,6 +28,7 @@ public class ApplicationEntity { | |||||
private Date gmtCreate; | private Date gmtCreate; | ||||
private Date gmtModified; | private Date gmtModified; | ||||
private String app; | private String app; | ||||
private Integer appType; | |||||
private String activeConsole; | private String activeConsole; | ||||
private Date lastFetch; | private Date lastFetch; | ||||
@@ -63,6 +64,14 @@ public class ApplicationEntity { | |||||
this.app = app; | this.app = app; | ||||
} | } | ||||
public Integer getAppType() { | |||||
return appType; | |||||
} | |||||
public void setAppType(Integer appType) { | |||||
this.appType = appType; | |||||
} | |||||
public String getActiveConsole() { | public String getActiveConsole() { | ||||
return activeConsole; | return activeConsole; | ||||
} | } | ||||
@@ -80,7 +89,7 @@ public class ApplicationEntity { | |||||
} | } | ||||
public AppInfo toAppInfo() { | public AppInfo toAppInfo() { | ||||
return new AppInfo(app); | |||||
return new AppInfo(app, appType); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -0,0 +1,208 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway; | |||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition; | |||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem; | |||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem; | |||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity; | |||||
import com.alibaba.csp.sentinel.slots.block.Rule; | |||||
import java.util.Date; | |||||
import java.util.LinkedHashSet; | |||||
import java.util.Objects; | |||||
import java.util.Set; | |||||
/** | |||||
* Entity for {@link ApiDefinition}. | |||||
* | |||||
* @author cdfive | |||||
* @since 1.7.0 | |||||
*/ | |||||
public class ApiDefinitionEntity implements RuleEntity { | |||||
private Long id; | |||||
private String app; | |||||
private String ip; | |||||
private Integer port; | |||||
private Date gmtCreate; | |||||
private Date gmtModified; | |||||
private String apiName; | |||||
private Set<ApiPredicateItemEntity> predicateItems; | |||||
public static ApiDefinitionEntity fromApiDefinition(String app, String ip, Integer port, ApiDefinition apiDefinition) { | |||||
ApiDefinitionEntity entity = new ApiDefinitionEntity(); | |||||
entity.setApp(app); | |||||
entity.setIp(ip); | |||||
entity.setPort(port); | |||||
entity.setApiName(apiDefinition.getApiName()); | |||||
Set<ApiPredicateItemEntity> predicateItems = new LinkedHashSet<>(); | |||||
entity.setPredicateItems(predicateItems); | |||||
Set<ApiPredicateItem> apiPredicateItems = apiDefinition.getPredicateItems(); | |||||
if (apiPredicateItems != null) { | |||||
for (ApiPredicateItem apiPredicateItem : apiPredicateItems) { | |||||
ApiPredicateItemEntity itemEntity = new ApiPredicateItemEntity(); | |||||
predicateItems.add(itemEntity); | |||||
ApiPathPredicateItem pathPredicateItem = (ApiPathPredicateItem) apiPredicateItem; | |||||
itemEntity.setPattern(pathPredicateItem.getPattern()); | |||||
itemEntity.setMatchStrategy(pathPredicateItem.getMatchStrategy()); | |||||
} | |||||
} | |||||
return entity; | |||||
} | |||||
public ApiDefinition toApiDefinition() { | |||||
ApiDefinition apiDefinition = new ApiDefinition(); | |||||
apiDefinition.setApiName(apiName); | |||||
Set<ApiPredicateItem> apiPredicateItems = new LinkedHashSet<>(); | |||||
apiDefinition.setPredicateItems(apiPredicateItems); | |||||
if (predicateItems != null) { | |||||
for (ApiPredicateItemEntity predicateItem : predicateItems) { | |||||
ApiPathPredicateItem apiPredicateItem = new ApiPathPredicateItem(); | |||||
apiPredicateItems.add(apiPredicateItem); | |||||
apiPredicateItem.setMatchStrategy(predicateItem.getMatchStrategy()); | |||||
apiPredicateItem.setPattern(predicateItem.getPattern()); | |||||
} | |||||
} | |||||
return apiDefinition; | |||||
} | |||||
public ApiDefinitionEntity() { | |||||
} | |||||
public ApiDefinitionEntity(String apiName, Set<ApiPredicateItemEntity> predicateItems) { | |||||
this.apiName = apiName; | |||||
this.predicateItems = predicateItems; | |||||
} | |||||
public String getApiName() { | |||||
return apiName; | |||||
} | |||||
public void setApiName(String apiName) { | |||||
this.apiName = apiName; | |||||
} | |||||
public Set<ApiPredicateItemEntity> getPredicateItems() { | |||||
return predicateItems; | |||||
} | |||||
public void setPredicateItems(Set<ApiPredicateItemEntity> predicateItems) { | |||||
this.predicateItems = predicateItems; | |||||
} | |||||
@Override | |||||
public Long getId() { | |||||
return id; | |||||
} | |||||
@Override | |||||
public void setId(Long id) { | |||||
this.id = id; | |||||
} | |||||
@Override | |||||
public String getApp() { | |||||
return app; | |||||
} | |||||
public void setApp(String app) { | |||||
this.app = app; | |||||
} | |||||
@Override | |||||
public String getIp() { | |||||
return ip; | |||||
} | |||||
public void setIp(String ip) { | |||||
this.ip = ip; | |||||
} | |||||
@Override | |||||
public Integer getPort() { | |||||
return port; | |||||
} | |||||
public void setPort(Integer port) { | |||||
this.port = port; | |||||
} | |||||
@Override | |||||
public Date getGmtCreate() { | |||||
return gmtCreate; | |||||
} | |||||
public void setGmtCreate(Date gmtCreate) { | |||||
this.gmtCreate = gmtCreate; | |||||
} | |||||
public Date getGmtModified() { | |||||
return gmtModified; | |||||
} | |||||
public void setGmtModified(Date gmtModified) { | |||||
this.gmtModified = gmtModified; | |||||
} | |||||
@Override | |||||
public Rule toRule() { | |||||
return null; | |||||
} | |||||
@Override | |||||
public boolean equals(Object o) { | |||||
if (this == o) { return true; } | |||||
if (o == null || getClass() != o.getClass()) { return false; } | |||||
ApiDefinitionEntity entity = (ApiDefinitionEntity) o; | |||||
return Objects.equals(id, entity.id) && | |||||
Objects.equals(app, entity.app) && | |||||
Objects.equals(ip, entity.ip) && | |||||
Objects.equals(port, entity.port) && | |||||
Objects.equals(gmtCreate, entity.gmtCreate) && | |||||
Objects.equals(gmtModified, entity.gmtModified) && | |||||
Objects.equals(apiName, entity.apiName) && | |||||
Objects.equals(predicateItems, entity.predicateItems); | |||||
} | |||||
@Override | |||||
public int hashCode() { | |||||
return Objects.hash(id, app, ip, port, gmtCreate, gmtModified, apiName, predicateItems); | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "ApiDefinitionEntity{" + | |||||
"id=" + id + | |||||
", app='" + app + '\'' + | |||||
", ip='" + ip + '\'' + | |||||
", port=" + port + | |||||
", gmtCreate=" + gmtCreate + | |||||
", gmtModified=" + gmtModified + | |||||
", apiName='" + apiName + '\'' + | |||||
", predicateItems=" + predicateItems + | |||||
'}'; | |||||
} | |||||
} |
@@ -0,0 +1,79 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway; | |||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem; | |||||
import java.util.Objects; | |||||
/** | |||||
* Entity for {@link ApiPredicateItem}. | |||||
* | |||||
* @author cdfive | |||||
* @since 1.7.0 | |||||
*/ | |||||
public class ApiPredicateItemEntity { | |||||
private String pattern; | |||||
private Integer matchStrategy; | |||||
public ApiPredicateItemEntity() { | |||||
} | |||||
public ApiPredicateItemEntity(String pattern, int matchStrategy) { | |||||
this.pattern = pattern; | |||||
this.matchStrategy = matchStrategy; | |||||
} | |||||
public String getPattern() { | |||||
return pattern; | |||||
} | |||||
public void setPattern(String pattern) { | |||||
this.pattern = pattern; | |||||
} | |||||
public Integer getMatchStrategy() { | |||||
return matchStrategy; | |||||
} | |||||
public void setMatchStrategy(Integer matchStrategy) { | |||||
this.matchStrategy = matchStrategy; | |||||
} | |||||
@Override | |||||
public boolean equals(Object o) { | |||||
if (this == o) { return true; } | |||||
if (o == null || getClass() != o.getClass()) { return false; } | |||||
ApiPredicateItemEntity that = (ApiPredicateItemEntity) o; | |||||
return Objects.equals(pattern, that.pattern) && | |||||
Objects.equals(matchStrategy, that.matchStrategy); | |||||
} | |||||
@Override | |||||
public int hashCode() { | |||||
return Objects.hash(pattern, matchStrategy); | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "ApiPredicateItemEntity{" + | |||||
"pattern='" + pattern + '\'' + | |||||
", matchStrategy=" + matchStrategy + | |||||
'}'; | |||||
} | |||||
} |
@@ -0,0 +1,354 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway; | |||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; | |||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem; | |||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity; | |||||
import com.alibaba.csp.sentinel.slots.block.Rule; | |||||
import java.util.Date; | |||||
import java.util.Objects; | |||||
/** | |||||
* Entity for {@link GatewayFlowRule}. | |||||
* | |||||
* @author cdfive | |||||
* @since 1.7.0 | |||||
*/ | |||||
public class GatewayFlowRuleEntity implements RuleEntity { | |||||
/**间隔单位*/ | |||||
/**0-秒*/ | |||||
public static final int INTERVAL_UNIT_SECOND = 0; | |||||
/**1-分*/ | |||||
public static final int INTERVAL_UNIT_MINUTE = 1; | |||||
/**2-时*/ | |||||
public static final int INTERVAL_UNIT_HOUR = 2; | |||||
/**3-天*/ | |||||
public static final int INTERVAL_UNIT_DAY = 3; | |||||
private Long id; | |||||
private String app; | |||||
private String ip; | |||||
private Integer port; | |||||
private Date gmtCreate; | |||||
private Date gmtModified; | |||||
private String resource; | |||||
private Integer resourceMode; | |||||
private Integer grade; | |||||
private Double count; | |||||
private Long interval; | |||||
private Integer intervalUnit; | |||||
private Integer controlBehavior; | |||||
private Integer burst; | |||||
private Integer maxQueueingTimeoutMs; | |||||
private GatewayParamFlowItemEntity paramItem; | |||||
public static Long calIntervalSec(Long interval, Integer intervalUnit) { | |||||
switch (intervalUnit) { | |||||
case INTERVAL_UNIT_SECOND: | |||||
return interval; | |||||
case INTERVAL_UNIT_MINUTE: | |||||
return interval * 60; | |||||
case INTERVAL_UNIT_HOUR: | |||||
return interval * 60 * 60; | |||||
case INTERVAL_UNIT_DAY: | |||||
return interval * 60 * 60 * 24; | |||||
default: | |||||
break; | |||||
} | |||||
throw new IllegalArgumentException("Invalid intervalUnit: " + intervalUnit); | |||||
} | |||||
public static Object[] parseIntervalSec(Long intervalSec) { | |||||
if (intervalSec % (60 * 60 * 24) == 0) { | |||||
return new Object[] {intervalSec / (60 * 60 * 24), INTERVAL_UNIT_DAY}; | |||||
} | |||||
if (intervalSec % (60 * 60 ) == 0) { | |||||
return new Object[] {intervalSec / (60 * 60), INTERVAL_UNIT_HOUR}; | |||||
} | |||||
if (intervalSec % 60 == 0) { | |||||
return new Object[] {intervalSec / 60, INTERVAL_UNIT_MINUTE}; | |||||
} | |||||
return new Object[] {intervalSec, INTERVAL_UNIT_SECOND}; | |||||
} | |||||
public GatewayFlowRule toGatewayFlowRule() { | |||||
GatewayFlowRule rule = new GatewayFlowRule(); | |||||
rule.setResource(resource); | |||||
rule.setResourceMode(resourceMode); | |||||
rule.setGrade(grade); | |||||
rule.setCount(count); | |||||
rule.setIntervalSec(calIntervalSec(interval, intervalUnit)); | |||||
rule.setControlBehavior(controlBehavior); | |||||
if (burst != null) { | |||||
rule.setBurst(burst); | |||||
} | |||||
if (maxQueueingTimeoutMs != null) { | |||||
rule.setMaxQueueingTimeoutMs(maxQueueingTimeoutMs); | |||||
} | |||||
if (paramItem != null) { | |||||
GatewayParamFlowItem ruleItem = new GatewayParamFlowItem(); | |||||
rule.setParamItem(ruleItem); | |||||
ruleItem.setParseStrategy(paramItem.getParseStrategy()); | |||||
ruleItem.setFieldName(paramItem.getFieldName()); | |||||
ruleItem.setPattern(paramItem.getPattern()); | |||||
if (paramItem.getMatchStrategy() != null) { | |||||
ruleItem.setMatchStrategy(paramItem.getMatchStrategy()); | |||||
} | |||||
} | |||||
return rule; | |||||
} | |||||
public static GatewayFlowRuleEntity fromGatewayFlowRule(String app, String ip, Integer port, GatewayFlowRule rule) { | |||||
GatewayFlowRuleEntity entity = new GatewayFlowRuleEntity(); | |||||
entity.setApp(app); | |||||
entity.setIp(ip); | |||||
entity.setPort(port); | |||||
entity.setResource(rule.getResource()); | |||||
entity.setResourceMode(rule.getResourceMode()); | |||||
entity.setGrade(rule.getGrade()); | |||||
entity.setCount(rule.getCount()); | |||||
Object[] intervalSecResult = parseIntervalSec(rule.getIntervalSec()); | |||||
entity.setInterval((Long) intervalSecResult[0]); | |||||
entity.setIntervalUnit((Integer) intervalSecResult[1]); | |||||
entity.setControlBehavior(rule.getControlBehavior()); | |||||
entity.setBurst(rule.getBurst()); | |||||
entity.setMaxQueueingTimeoutMs(rule.getMaxQueueingTimeoutMs()); | |||||
GatewayParamFlowItem paramItem = rule.getParamItem(); | |||||
if (paramItem != null) { | |||||
GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity(); | |||||
entity.setParamItem(itemEntity); | |||||
itemEntity.setParseStrategy(paramItem.getParseStrategy()); | |||||
itemEntity.setFieldName(paramItem.getFieldName()); | |||||
itemEntity.setPattern(paramItem.getPattern()); | |||||
itemEntity.setMatchStrategy(paramItem.getMatchStrategy()); | |||||
} | |||||
return entity; | |||||
} | |||||
@Override | |||||
public Long getId() { | |||||
return id; | |||||
} | |||||
@Override | |||||
public void setId(Long id) { | |||||
this.id = id; | |||||
} | |||||
@Override | |||||
public String getApp() { | |||||
return app; | |||||
} | |||||
public void setApp(String app) { | |||||
this.app = app; | |||||
} | |||||
@Override | |||||
public String getIp() { | |||||
return ip; | |||||
} | |||||
public void setIp(String ip) { | |||||
this.ip = ip; | |||||
} | |||||
@Override | |||||
public Integer getPort() { | |||||
return port; | |||||
} | |||||
public void setPort(Integer port) { | |||||
this.port = port; | |||||
} | |||||
@Override | |||||
public Date getGmtCreate() { | |||||
return gmtCreate; | |||||
} | |||||
public void setGmtCreate(Date gmtCreate) { | |||||
this.gmtCreate = gmtCreate; | |||||
} | |||||
@Override | |||||
public Rule toRule() { | |||||
return null; | |||||
} | |||||
public Date getGmtModified() { | |||||
return gmtModified; | |||||
} | |||||
public void setGmtModified(Date gmtModified) { | |||||
this.gmtModified = gmtModified; | |||||
} | |||||
public GatewayParamFlowItemEntity getParamItem() { | |||||
return paramItem; | |||||
} | |||||
public void setParamItem(GatewayParamFlowItemEntity paramItem) { | |||||
this.paramItem = paramItem; | |||||
} | |||||
public String getResource() { | |||||
return resource; | |||||
} | |||||
public void setResource(String resource) { | |||||
this.resource = resource; | |||||
} | |||||
public Integer getResourceMode() { | |||||
return resourceMode; | |||||
} | |||||
public void setResourceMode(Integer resourceMode) { | |||||
this.resourceMode = resourceMode; | |||||
} | |||||
public Integer getGrade() { | |||||
return grade; | |||||
} | |||||
public void setGrade(Integer grade) { | |||||
this.grade = grade; | |||||
} | |||||
public Double getCount() { | |||||
return count; | |||||
} | |||||
public void setCount(Double count) { | |||||
this.count = count; | |||||
} | |||||
public Long getInterval() { | |||||
return interval; | |||||
} | |||||
public void setInterval(Long interval) { | |||||
this.interval = interval; | |||||
} | |||||
public Integer getIntervalUnit() { | |||||
return intervalUnit; | |||||
} | |||||
public void setIntervalUnit(Integer intervalUnit) { | |||||
this.intervalUnit = intervalUnit; | |||||
} | |||||
public Integer getControlBehavior() { | |||||
return controlBehavior; | |||||
} | |||||
public void setControlBehavior(Integer controlBehavior) { | |||||
this.controlBehavior = controlBehavior; | |||||
} | |||||
public Integer getBurst() { | |||||
return burst; | |||||
} | |||||
public void setBurst(Integer burst) { | |||||
this.burst = burst; | |||||
} | |||||
public Integer getMaxQueueingTimeoutMs() { | |||||
return maxQueueingTimeoutMs; | |||||
} | |||||
public void setMaxQueueingTimeoutMs(Integer maxQueueingTimeoutMs) { | |||||
this.maxQueueingTimeoutMs = maxQueueingTimeoutMs; | |||||
} | |||||
@Override | |||||
public boolean equals(Object o) { | |||||
if (this == o) { return true; } | |||||
if (o == null || getClass() != o.getClass()) { return false; } | |||||
GatewayFlowRuleEntity that = (GatewayFlowRuleEntity) o; | |||||
return Objects.equals(id, that.id) && | |||||
Objects.equals(app, that.app) && | |||||
Objects.equals(ip, that.ip) && | |||||
Objects.equals(port, that.port) && | |||||
Objects.equals(gmtCreate, that.gmtCreate) && | |||||
Objects.equals(gmtModified, that.gmtModified) && | |||||
Objects.equals(resource, that.resource) && | |||||
Objects.equals(resourceMode, that.resourceMode) && | |||||
Objects.equals(grade, that.grade) && | |||||
Objects.equals(count, that.count) && | |||||
Objects.equals(interval, that.interval) && | |||||
Objects.equals(intervalUnit, that.intervalUnit) && | |||||
Objects.equals(controlBehavior, that.controlBehavior) && | |||||
Objects.equals(burst, that.burst) && | |||||
Objects.equals(maxQueueingTimeoutMs, that.maxQueueingTimeoutMs) && | |||||
Objects.equals(paramItem, that.paramItem); | |||||
} | |||||
@Override | |||||
public int hashCode() { | |||||
return Objects.hash(id, app, ip, port, gmtCreate, gmtModified, resource, resourceMode, grade, count, interval, intervalUnit, controlBehavior, burst, maxQueueingTimeoutMs, paramItem); | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "GatewayFlowRuleEntity{" + | |||||
"id=" + id + | |||||
", app='" + app + '\'' + | |||||
", ip='" + ip + '\'' + | |||||
", port=" + port + | |||||
", gmtCreate=" + gmtCreate + | |||||
", gmtModified=" + gmtModified + | |||||
", resource='" + resource + '\'' + | |||||
", resourceMode=" + resourceMode + | |||||
", grade=" + grade + | |||||
", count=" + count + | |||||
", interval=" + interval + | |||||
", intervalUnit=" + intervalUnit + | |||||
", controlBehavior=" + controlBehavior + | |||||
", burst=" + burst + | |||||
", maxQueueingTimeoutMs=" + maxQueueingTimeoutMs + | |||||
", paramItem=" + paramItem + | |||||
'}'; | |||||
} | |||||
} |
@@ -0,0 +1,95 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway; | |||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem; | |||||
import java.util.Objects; | |||||
/** | |||||
* Entity for {@link GatewayParamFlowItem}. | |||||
* | |||||
* @author cdfive | |||||
* @since 1.7.0 | |||||
*/ | |||||
public class GatewayParamFlowItemEntity { | |||||
private Integer parseStrategy; | |||||
private String fieldName; | |||||
private String pattern; | |||||
private Integer matchStrategy; | |||||
public Integer getParseStrategy() { | |||||
return parseStrategy; | |||||
} | |||||
public void setParseStrategy(Integer parseStrategy) { | |||||
this.parseStrategy = parseStrategy; | |||||
} | |||||
public String getFieldName() { | |||||
return fieldName; | |||||
} | |||||
public void setFieldName(String fieldName) { | |||||
this.fieldName = fieldName; | |||||
} | |||||
public String getPattern() { | |||||
return pattern; | |||||
} | |||||
public void setPattern(String pattern) { | |||||
this.pattern = pattern; | |||||
} | |||||
public Integer getMatchStrategy() { | |||||
return matchStrategy; | |||||
} | |||||
public void setMatchStrategy(Integer matchStrategy) { | |||||
this.matchStrategy = matchStrategy; | |||||
} | |||||
@Override | |||||
public boolean equals(Object o) { | |||||
if (this == o) { return true; } | |||||
if (o == null || getClass() != o.getClass()) { return false; } | |||||
GatewayParamFlowItemEntity that = (GatewayParamFlowItemEntity) o; | |||||
return Objects.equals(parseStrategy, that.parseStrategy) && | |||||
Objects.equals(fieldName, that.fieldName) && | |||||
Objects.equals(pattern, that.pattern) && | |||||
Objects.equals(matchStrategy, that.matchStrategy); | |||||
} | |||||
@Override | |||||
public int hashCode() { | |||||
return Objects.hash(parseStrategy, fieldName, pattern, matchStrategy); | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "GatewayParamFlowItemEntity{" + | |||||
"parseStrategy=" + parseStrategy + | |||||
", fieldName='" + fieldName + '\'' + | |||||
", pattern='" + pattern + '\'' + | |||||
", matchStrategy=" + matchStrategy + | |||||
'}'; | |||||
} | |||||
} |
@@ -28,6 +28,8 @@ public class AppInfo { | |||||
private String app = ""; | private String app = ""; | ||||
private Integer appType = 0; | |||||
private Set<MachineInfo> machines = ConcurrentHashMap.newKeySet(); | private Set<MachineInfo> machines = ConcurrentHashMap.newKeySet(); | ||||
public AppInfo() {} | public AppInfo() {} | ||||
@@ -36,6 +38,11 @@ public class AppInfo { | |||||
this.app = app; | this.app = app; | ||||
} | } | ||||
public AppInfo(String app, Integer appType) { | |||||
this.app = app; | |||||
this.appType = appType; | |||||
} | |||||
public String getApp() { | public String getApp() { | ||||
return app; | return app; | ||||
} | } | ||||
@@ -44,6 +51,14 @@ public class AppInfo { | |||||
this.app = app; | this.app = app; | ||||
} | } | ||||
public Integer getAppType() { | |||||
return appType; | |||||
} | |||||
public void setAppType(Integer appType) { | |||||
this.appType = appType; | |||||
} | |||||
/** | /** | ||||
* Get the current machines. | * Get the current machines. | ||||
* | * | ||||
@@ -23,6 +23,7 @@ import com.alibaba.csp.sentinel.util.StringUtil; | |||||
public class MachineInfo implements Comparable<MachineInfo> { | public class MachineInfo implements Comparable<MachineInfo> { | ||||
private String app = ""; | private String app = ""; | ||||
private Integer appType = 0; | |||||
private String hostname = ""; | private String hostname = ""; | ||||
private String ip = ""; | private String ip = ""; | ||||
private Integer port = -1; | private Integer port = -1; | ||||
@@ -62,6 +63,14 @@ public class MachineInfo implements Comparable<MachineInfo> { | |||||
this.app = app; | this.app = app; | ||||
} | } | ||||
public Integer getAppType() { | |||||
return appType; | |||||
} | |||||
public void setAppType(Integer appType) { | |||||
this.appType = appType; | |||||
} | |||||
public String getHostname() { | public String getHostname() { | ||||
return hostname; | return hostname; | ||||
} | } | ||||
@@ -139,6 +148,7 @@ public class MachineInfo implements Comparable<MachineInfo> { | |||||
public String toString() { | public String toString() { | ||||
return new StringBuilder("MachineInfo {") | return new StringBuilder("MachineInfo {") | ||||
.append("app='").append(app).append('\'') | .append("app='").append(app).append('\'') | ||||
.append(",appType='").append(appType).append('\'') | |||||
.append(", hostname='").append(hostname).append('\'') | .append(", hostname='").append(hostname).append('\'') | ||||
.append(", ip='").append(ip).append('\'') | .append(", ip='").append(ip).append('\'') | ||||
.append(", port=").append(port) | .append(", port=").append(port) | ||||
@@ -37,7 +37,7 @@ public class SimpleMachineDiscovery implements MachineDiscovery { | |||||
@Override | @Override | ||||
public long addMachine(MachineInfo machineInfo) { | public long addMachine(MachineInfo machineInfo) { | ||||
AssertUtil.notNull(machineInfo, "machineInfo cannot be null"); | AssertUtil.notNull(machineInfo, "machineInfo cannot be null"); | ||||
AppInfo appInfo = apps.computeIfAbsent(machineInfo.getApp(), AppInfo::new); | |||||
AppInfo appInfo = apps.computeIfAbsent(machineInfo.getApp(), o -> new AppInfo(machineInfo.getApp(), machineInfo.getAppType())); | |||||
appInfo.addMachine(machineInfo); | appInfo.addMachine(machineInfo); | ||||
return 1; | return 1; | ||||
} | } | ||||
@@ -0,0 +1,78 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api; | |||||
import java.util.List; | |||||
/** | |||||
* Value Object for add gateway api. | |||||
* | |||||
* @author cdfive | |||||
* @since 1.7.0 | |||||
*/ | |||||
public class AddApiReqVo { | |||||
private String app; | |||||
private String ip; | |||||
private Integer port; | |||||
private String apiName; | |||||
private List<ApiPredicateItemVo> predicateItems; | |||||
public String getApp() { | |||||
return app; | |||||
} | |||||
public void setApp(String app) { | |||||
this.app = app; | |||||
} | |||||
public String getIp() { | |||||
return ip; | |||||
} | |||||
public void setIp(String ip) { | |||||
this.ip = ip; | |||||
} | |||||
public Integer getPort() { | |||||
return port; | |||||
} | |||||
public void setPort(Integer port) { | |||||
this.port = port; | |||||
} | |||||
public String getApiName() { | |||||
return apiName; | |||||
} | |||||
public void setApiName(String apiName) { | |||||
this.apiName = apiName; | |||||
} | |||||
public List<ApiPredicateItemVo> getPredicateItems() { | |||||
return predicateItems; | |||||
} | |||||
public void setPredicateItems(List<ApiPredicateItemVo> predicateItems) { | |||||
this.predicateItems = predicateItems; | |||||
} | |||||
} | |||||
@@ -0,0 +1,45 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api; | |||||
/** | |||||
* Value Object for add or update gateway api. | |||||
* | |||||
* @author cdfive | |||||
* @since 1.7.0 | |||||
*/ | |||||
public class ApiPredicateItemVo { | |||||
private String pattern; | |||||
private Integer matchStrategy; | |||||
public String getPattern() { | |||||
return pattern; | |||||
} | |||||
public void setPattern(String pattern) { | |||||
this.pattern = pattern; | |||||
} | |||||
public Integer getMatchStrategy() { | |||||
return matchStrategy; | |||||
} | |||||
public void setMatchStrategy(Integer matchStrategy) { | |||||
this.matchStrategy = matchStrategy; | |||||
} | |||||
} |
@@ -0,0 +1,57 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api; | |||||
import java.util.List; | |||||
/** | |||||
* Value Object for update gateway api. | |||||
* | |||||
* @author cdfive | |||||
* @since 1.7.0 | |||||
*/ | |||||
public class UpdateApiReqVo { | |||||
private Long id; | |||||
private String app; | |||||
private List<ApiPredicateItemVo> predicateItems; | |||||
public Long getId() { | |||||
return id; | |||||
} | |||||
public void setId(Long id) { | |||||
this.id = id; | |||||
} | |||||
public String getApp() { | |||||
return app; | |||||
} | |||||
public void setApp(String app) { | |||||
this.app = app; | |||||
} | |||||
public List<ApiPredicateItemVo> getPredicateItems() { | |||||
return predicateItems; | |||||
} | |||||
public void setPredicateItems(List<ApiPredicateItemVo> predicateItems) { | |||||
this.predicateItems = predicateItems; | |||||
} | |||||
} |
@@ -0,0 +1,155 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule; | |||||
/** | |||||
* Value Object for add gateway flow rule. | |||||
* | |||||
* @author cdfive | |||||
* @since 1.7.0 | |||||
*/ | |||||
public class AddFlowRuleReqVo { | |||||
private String app; | |||||
private String ip; | |||||
private Integer port; | |||||
private String resource; | |||||
private Integer resourceMode; | |||||
private Integer grade; | |||||
private Double count; | |||||
private Long interval; | |||||
private Integer intervalUnit; | |||||
private Integer controlBehavior; | |||||
private Integer burst; | |||||
private Integer maxQueueingTimeoutMs; | |||||
private GatewayParamFlowItemVo paramItem; | |||||
public String getApp() { | |||||
return app; | |||||
} | |||||
public void setApp(String app) { | |||||
this.app = app; | |||||
} | |||||
public String getIp() { | |||||
return ip; | |||||
} | |||||
public void setIp(String ip) { | |||||
this.ip = ip; | |||||
} | |||||
public Integer getPort() { | |||||
return port; | |||||
} | |||||
public void setPort(Integer port) { | |||||
this.port = port; | |||||
} | |||||
public String getResource() { | |||||
return resource; | |||||
} | |||||
public void setResource(String resource) { | |||||
this.resource = resource; | |||||
} | |||||
public Integer getResourceMode() { | |||||
return resourceMode; | |||||
} | |||||
public void setResourceMode(Integer resourceMode) { | |||||
this.resourceMode = resourceMode; | |||||
} | |||||
public Integer getGrade() { | |||||
return grade; | |||||
} | |||||
public void setGrade(Integer grade) { | |||||
this.grade = grade; | |||||
} | |||||
public Double getCount() { | |||||
return count; | |||||
} | |||||
public void setCount(Double count) { | |||||
this.count = count; | |||||
} | |||||
public Long getInterval() { | |||||
return interval; | |||||
} | |||||
public void setInterval(Long interval) { | |||||
this.interval = interval; | |||||
} | |||||
public Integer getIntervalUnit() { | |||||
return intervalUnit; | |||||
} | |||||
public void setIntervalUnit(Integer intervalUnit) { | |||||
this.intervalUnit = intervalUnit; | |||||
} | |||||
public Integer getControlBehavior() { | |||||
return controlBehavior; | |||||
} | |||||
public void setControlBehavior(Integer controlBehavior) { | |||||
this.controlBehavior = controlBehavior; | |||||
} | |||||
public Integer getBurst() { | |||||
return burst; | |||||
} | |||||
public void setBurst(Integer burst) { | |||||
this.burst = burst; | |||||
} | |||||
public Integer getMaxQueueingTimeoutMs() { | |||||
return maxQueueingTimeoutMs; | |||||
} | |||||
public void setMaxQueueingTimeoutMs(Integer maxQueueingTimeoutMs) { | |||||
this.maxQueueingTimeoutMs = maxQueueingTimeoutMs; | |||||
} | |||||
public GatewayParamFlowItemVo getParamItem() { | |||||
return paramItem; | |||||
} | |||||
public void setParamItem(GatewayParamFlowItemVo paramItem) { | |||||
this.paramItem = paramItem; | |||||
} | |||||
} |
@@ -0,0 +1,65 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule; | |||||
/** | |||||
* Value Object for add or update gateway flow rule. | |||||
* | |||||
* @author cdfive | |||||
* @since 1.7.0 | |||||
*/ | |||||
public class GatewayParamFlowItemVo { | |||||
private Integer parseStrategy; | |||||
private String fieldName; | |||||
private String pattern; | |||||
private Integer matchStrategy; | |||||
public Integer getParseStrategy() { | |||||
return parseStrategy; | |||||
} | |||||
public void setParseStrategy(Integer parseStrategy) { | |||||
this.parseStrategy = parseStrategy; | |||||
} | |||||
public String getFieldName() { | |||||
return fieldName; | |||||
} | |||||
public void setFieldName(String fieldName) { | |||||
this.fieldName = fieldName; | |||||
} | |||||
public String getPattern() { | |||||
return pattern; | |||||
} | |||||
public void setPattern(String pattern) { | |||||
this.pattern = pattern; | |||||
} | |||||
public Integer getMatchStrategy() { | |||||
return matchStrategy; | |||||
} | |||||
public void setMatchStrategy(Integer matchStrategy) { | |||||
this.matchStrategy = matchStrategy; | |||||
} | |||||
} |
@@ -0,0 +1,125 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule; | |||||
/** | |||||
* Value Object for update gateway flow rule. | |||||
* | |||||
* @author cdfive | |||||
* @since 1.7.0 | |||||
*/ | |||||
public class UpdateFlowRuleReqVo { | |||||
private Long id; | |||||
private String app; | |||||
private Integer grade; | |||||
private Double count; | |||||
private Long interval; | |||||
private Integer intervalUnit; | |||||
private Integer controlBehavior; | |||||
private Integer burst; | |||||
private Integer maxQueueingTimeoutMs; | |||||
private GatewayParamFlowItemVo paramItem; | |||||
public Long getId() { | |||||
return id; | |||||
} | |||||
public void setId(Long id) { | |||||
this.id = id; | |||||
} | |||||
public String getApp() { | |||||
return app; | |||||
} | |||||
public void setApp(String app) { | |||||
this.app = app; | |||||
} | |||||
public Integer getGrade() { | |||||
return grade; | |||||
} | |||||
public void setGrade(Integer grade) { | |||||
this.grade = grade; | |||||
} | |||||
public Double getCount() { | |||||
return count; | |||||
} | |||||
public void setCount(Double count) { | |||||
this.count = count; | |||||
} | |||||
public Long getInterval() { | |||||
return interval; | |||||
} | |||||
public void setInterval(Long interval) { | |||||
this.interval = interval; | |||||
} | |||||
public Integer getIntervalUnit() { | |||||
return intervalUnit; | |||||
} | |||||
public void setIntervalUnit(Integer intervalUnit) { | |||||
this.intervalUnit = intervalUnit; | |||||
} | |||||
public Integer getControlBehavior() { | |||||
return controlBehavior; | |||||
} | |||||
public void setControlBehavior(Integer controlBehavior) { | |||||
this.controlBehavior = controlBehavior; | |||||
} | |||||
public Integer getBurst() { | |||||
return burst; | |||||
} | |||||
public void setBurst(Integer burst) { | |||||
this.burst = burst; | |||||
} | |||||
public Integer getMaxQueueingTimeoutMs() { | |||||
return maxQueueingTimeoutMs; | |||||
} | |||||
public void setMaxQueueingTimeoutMs(Integer maxQueueingTimeoutMs) { | |||||
this.maxQueueingTimeoutMs = maxQueueingTimeoutMs; | |||||
} | |||||
public GatewayParamFlowItemVo getParamItem() { | |||||
return paramItem; | |||||
} | |||||
public void setParamItem(GatewayParamFlowItemVo paramItem) { | |||||
this.paramItem = paramItem; | |||||
} | |||||
} |
@@ -0,0 +1,39 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.repository.gateway; | |||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity; | |||||
import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter; | |||||
import org.springframework.stereotype.Component; | |||||
import java.util.concurrent.atomic.AtomicLong; | |||||
/** | |||||
* Store {@link ApiDefinitionEntity} in memory. | |||||
* | |||||
* @author cdfive | |||||
* @since 1.7.0 | |||||
*/ | |||||
@Component | |||||
public class InMemApiDefinitionStore extends InMemoryRuleRepositoryAdapter<ApiDefinitionEntity> { | |||||
private static AtomicLong ids = new AtomicLong(0); | |||||
@Override | |||||
protected long nextId() { | |||||
return ids.incrementAndGet(); | |||||
} | |||||
} |
@@ -0,0 +1,39 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.repository.gateway; | |||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity; | |||||
import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter; | |||||
import org.springframework.stereotype.Component; | |||||
import java.util.concurrent.atomic.AtomicLong; | |||||
/** | |||||
* Store {@link GatewayFlowRuleEntity} in memory. | |||||
* | |||||
* @author cdfive | |||||
* @since 1.7.0 | |||||
*/ | |||||
@Component | |||||
public class InMemGatewayFlowRuleStore extends InMemoryRuleRepositoryAdapter<GatewayFlowRuleEntity> { | |||||
private static AtomicLong ids = new AtomicLong(0); | |||||
@Override | |||||
protected long nextId() { | |||||
return ids.incrementAndGet(); | |||||
} | |||||
} |
@@ -110,6 +110,12 @@ public abstract class InMemoryRuleRepositoryAdapter<T extends RuleEntity> implem | |||||
return new ArrayList<>(entities.values()); | return new ArrayList<>(entities.values()); | ||||
} | } | ||||
public void clearAll() { | |||||
allRules.clear(); | |||||
machineRules.clear(); | |||||
appRules.clear(); | |||||
} | |||||
protected T preProcess(T entity) { | protected T preProcess(T entity) { | ||||
return entity; | return entity; | ||||
} | } | ||||
@@ -26,9 +26,9 @@ angular | |||||
.factory('AuthInterceptor', ['$window', '$state', function ($window, $state) { | .factory('AuthInterceptor', ['$window', '$state', function ($window, $state) { | ||||
var authInterceptor = { | var authInterceptor = { | ||||
'responseError' : function(response) { | 'responseError' : function(response) { | ||||
if (response.status == 401) { | |||||
if (response.status === 401) { | |||||
// If not auth, clear session in localStorage and jump to the login page | // If not auth, clear session in localStorage and jump to the login page | ||||
$window.localStorage.removeItem("session_sentinel_admin"); | |||||
$window.localStorage.removeItem('session_sentinel_admin'); | |||||
$state.go('login'); | $state.go('login'); | ||||
} | } | ||||
@@ -123,21 +123,21 @@ angular | |||||
} | } | ||||
}) | }) | ||||
.state('dashboard.flow', { | |||||
templateUrl: 'app/views/flow_v2.html', | |||||
url: '/v2/flow/:app', | |||||
controller: 'FlowControllerV2', | |||||
resolve: { | |||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||||
return $ocLazyLoad.load({ | |||||
name: 'sentinelDashboardApp', | |||||
files: [ | |||||
'app/scripts/controllers/flow_v2.js', | |||||
] | |||||
}); | |||||
}] | |||||
} | |||||
}) | |||||
.state('dashboard.flow', { | |||||
templateUrl: 'app/views/flow_v2.html', | |||||
url: '/v2/flow/:app', | |||||
controller: 'FlowControllerV2', | |||||
resolve: { | |||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||||
return $ocLazyLoad.load({ | |||||
name: 'sentinelDashboardApp', | |||||
files: [ | |||||
'app/scripts/controllers/flow_v2.js', | |||||
] | |||||
}); | |||||
}] | |||||
} | |||||
}) | |||||
.state('dashboard.paramFlow', { | .state('dashboard.paramFlow', { | ||||
templateUrl: 'app/views/param_flow.html', | templateUrl: 'app/views/param_flow.html', | ||||
@@ -155,69 +155,69 @@ angular | |||||
} | } | ||||
}) | }) | ||||
.state('dashboard.clusterAppAssignManage', { | |||||
templateUrl: 'app/views/cluster_app_assign_manage.html', | |||||
url: '/cluster/assign_manage/:app', | |||||
controller: 'SentinelClusterAppAssignManageController', | |||||
resolve: { | |||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||||
return $ocLazyLoad.load({ | |||||
name: 'sentinelDashboardApp', | |||||
files: [ | |||||
'app/scripts/controllers/cluster_app_assign_manage.js', | |||||
] | |||||
}); | |||||
}] | |||||
} | |||||
}) | |||||
.state('dashboard.clusterAppAssignManage', { | |||||
templateUrl: 'app/views/cluster_app_assign_manage.html', | |||||
url: '/cluster/assign_manage/:app', | |||||
controller: 'SentinelClusterAppAssignManageController', | |||||
resolve: { | |||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||||
return $ocLazyLoad.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 ($ocLazyLoad) { | |||||
return $ocLazyLoad.load({ | |||||
name: 'sentinelDashboardApp', | |||||
files: [ | |||||
'app/scripts/controllers/cluster_app_server_list.js', | |||||
] | |||||
}); | |||||
}] | |||||
} | |||||
}) | |||||
.state('dashboard.clusterAppServerList', { | |||||
templateUrl: 'app/views/cluster_app_server_list.html', | |||||
url: '/cluster/server/:app', | |||||
controller: 'SentinelClusterAppServerListController', | |||||
resolve: { | |||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||||
return $ocLazyLoad.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 ($ocLazyLoad) { | |||||
return $ocLazyLoad.load({ | |||||
name: 'sentinelDashboardApp', | |||||
files: [ | |||||
'app/scripts/controllers/cluster_app_token_client_list.js', | |||||
] | |||||
}); | |||||
}] | |||||
} | |||||
}) | |||||
.state('dashboard.clusterAppClientList', { | |||||
templateUrl: 'app/views/cluster_app_client_list.html', | |||||
url: '/cluster/client/:app', | |||||
controller: 'SentinelClusterAppTokenClientListController', | |||||
resolve: { | |||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||||
return $ocLazyLoad.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 ($ocLazyLoad) { | |||||
return $ocLazyLoad.load({ | |||||
name: 'sentinelDashboardApp', | |||||
files: [ | |||||
'app/scripts/controllers/cluster_single.js', | |||||
] | |||||
}); | |||||
}] | |||||
} | |||||
}) | |||||
.state('dashboard.clusterSingle', { | |||||
templateUrl: 'app/views/cluster_single_config.html', | |||||
url: '/cluster/single/:app', | |||||
controller: 'SentinelClusterSingleController', | |||||
resolve: { | |||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||||
return $ocLazyLoad.load({ | |||||
name: 'sentinelDashboardApp', | |||||
files: [ | |||||
'app/scripts/controllers/cluster_single.js', | |||||
] | |||||
}); | |||||
}] | |||||
} | |||||
}) | |||||
.state('dashboard.authority', { | .state('dashboard.authority', { | ||||
templateUrl: 'app/views/authority.html', | templateUrl: 'app/views/authority.html', | ||||
@@ -298,6 +298,23 @@ angular | |||||
}] | }] | ||||
} | } | ||||
}) | }) | ||||
.state('dashboard.gatewayIdentity', { | |||||
templateUrl: 'app/views/gateway/identity.html', | |||||
url: '/gateway/identity/:app', | |||||
controller: 'GatewayIdentityCtl', | |||||
resolve: { | |||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||||
return $ocLazyLoad.load({ | |||||
name: 'sentinelDashboardApp', | |||||
files: [ | |||||
'app/scripts/controllers/gateway/identity.js', | |||||
] | |||||
}); | |||||
}] | |||||
} | |||||
}) | |||||
.state('dashboard.metric', { | .state('dashboard.metric', { | ||||
templateUrl: 'app/views/metric.html', | templateUrl: 'app/views/metric.html', | ||||
url: '/metric/:app', | url: '/metric/:app', | ||||
@@ -312,5 +329,37 @@ angular | |||||
}); | }); | ||||
}] | }] | ||||
} | } | ||||
}) | |||||
.state('dashboard.gatewayApi', { | |||||
templateUrl: 'app/views/gateway/api.html', | |||||
url: '/gateway/api/:app', | |||||
controller: 'GatewayApiCtl', | |||||
resolve: { | |||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||||
return $ocLazyLoad.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 ($ocLazyLoad) { | |||||
return $ocLazyLoad.load({ | |||||
name: 'sentinelDashboardApp', | |||||
files: [ | |||||
'app/scripts/controllers/gateway/flow.js', | |||||
] | |||||
}); | |||||
}] | |||||
} | |||||
}); | }); | ||||
}]); | }]); |
@@ -0,0 +1,245 @@ | |||||
var app = angular.module('sentinelDashboardApp'); | |||||
app.controller('GatewayApiCtl', ['$scope', '$stateParams', 'GatewayApiService', 'ngDialog', 'MachineService', | |||||
function ($scope, $stateParams, GatewayApiService, ngDialog, MachineService) { | |||||
$scope.app = $stateParams.app; | |||||
$scope.apisPageConfig = { | |||||
pageSize: 10, | |||||
currentPageIndex: 1, | |||||
totalPage: 1, | |||||
totalCount: 0, | |||||
}; | |||||
$scope.macsInputConfig = { | |||||
searchField: ['text', 'value'], | |||||
persist: true, | |||||
create: false, | |||||
maxItems: 1, | |||||
render: { | |||||
item: function (data, escape) { | |||||
return '<div>' + escape(data.text) + '</div>'; | |||||
} | |||||
}, | |||||
onChange: function (value, oldValue) { | |||||
$scope.macInputModel = value; | |||||
} | |||||
}; | |||||
getApis(); | |||||
function getApis() { | |||||
if (!$scope.macInputModel) { | |||||
return; | |||||
} | |||||
var mac = $scope.macInputModel.split(':'); | |||||
GatewayApiService.queryApis($scope.app, mac[0], mac[1]).success( | |||||
function (data) { | |||||
if (data.code == 0 && data.data) { | |||||
// To merge rows for api who has more than one predicateItems, here we build data manually | |||||
$scope.apis = []; | |||||
data.data.forEach(function(api) { | |||||
api["predicateItems"].forEach(function (item, index) { | |||||
var newItem = {}; | |||||
newItem["id"] = api["id"]; | |||||
newItem["app"] = api["app"]; | |||||
newItem["ip"] = api["ip"]; | |||||
newItem["port"] = api["port"]; | |||||
newItem["apiName"] = api["apiName"]; | |||||
newItem["pattern"] = item["pattern"]; | |||||
newItem["matchStrategy"] = item["matchStrategy"]; | |||||
// The itemSize indicates how many rows to merge, by using rowspan="{{api.itemSize}}" in <td> tag | |||||
newItem["itemSize"] = api["predicateItems"].length; | |||||
// Mark the flag of first item to zero, indicates the start row to merge | |||||
newItem["firstFlag"] = index == 0 ? 0 : 1; | |||||
// Still hold the data of predicateItems, in order to bind data in edit dialog html | |||||
newItem["predicateItems"] = api["predicateItems"]; | |||||
$scope.apis.push(newItem); | |||||
}); | |||||
}); | |||||
$scope.apisPageConfig.totalCount = data.data.length; | |||||
} else { | |||||
$scope.apis = []; | |||||
$scope.apisPageConfig.totalCount = 0; | |||||
} | |||||
}); | |||||
}; | |||||
$scope.getApis = getApis; | |||||
var gatewayApiDialog; | |||||
$scope.editApi = function (api) { | |||||
$scope.currentApi = angular.copy(api); | |||||
$scope.gatewayApiDialog = { | |||||
title: '编辑自定义API', | |||||
type: 'edit', | |||||
confirmBtnText: '保存' | |||||
}; | |||||
gatewayApiDialog = ngDialog.open({ | |||||
template: '/app/views/dialog/gateway/api-dialog.html', | |||||
width: 840, | |||||
overlay: true, | |||||
scope: $scope | |||||
}); | |||||
}; | |||||
$scope.addNewApi = function () { | |||||
var mac = $scope.macInputModel.split(':'); | |||||
$scope.currentApi = { | |||||
grade: 0, | |||||
app: $scope.app, | |||||
ip: mac[0], | |||||
port: mac[1], | |||||
predicateItems: [{matchStrategy: 0, pattern: ''}] | |||||
}; | |||||
$scope.gatewayApiDialog = { | |||||
title: '新增自定义API', | |||||
type: 'add', | |||||
confirmBtnText: '新增' | |||||
}; | |||||
gatewayApiDialog = ngDialog.open({ | |||||
template: '/app/views/dialog/gateway/api-dialog.html', | |||||
width: 840, | |||||
overlay: true, | |||||
scope: $scope | |||||
}); | |||||
}; | |||||
$scope.saveApi = function () { | |||||
var apiNames = []; | |||||
if ($scope.gatewayApiDialog.type === 'add') { | |||||
apiNames = $scope.apis.map(function (item, index, array) { | |||||
return item["apiName"]; | |||||
}).filter(function (item, index, array) { | |||||
return array.indexOf(item) === index; | |||||
}); | |||||
} | |||||
if (!GatewayApiService.checkApiValid($scope.currentApi, apiNames)) { | |||||
return; | |||||
} | |||||
if ($scope.gatewayApiDialog.type === 'add') { | |||||
addNewApi($scope.currentApi); | |||||
} else if ($scope.gatewayApiDialog.type === 'edit') { | |||||
saveApi($scope.currentApi, true); | |||||
} | |||||
}; | |||||
function addNewApi(api) { | |||||
GatewayApiService.newApi(api).success(function (data) { | |||||
if (data.code == 0) { | |||||
getApis(); | |||||
gatewayApiDialog.close(); | |||||
} else { | |||||
alert('新增自定义API失败!' + data.msg); | |||||
} | |||||
}); | |||||
}; | |||||
function saveApi(api, edit) { | |||||
GatewayApiService.saveApi(api).success(function (data) { | |||||
if (data.code == 0) { | |||||
getApis(); | |||||
if (edit) { | |||||
gatewayApiDialog.close(); | |||||
} else { | |||||
confirmDialog.close(); | |||||
} | |||||
} else { | |||||
alert('修改自定义API失败!' + data.msg); | |||||
} | |||||
}); | |||||
}; | |||||
var confirmDialog; | |||||
$scope.deleteApi = function (api) { | |||||
$scope.currentApi = api; | |||||
$scope.confirmDialog = { | |||||
title: '删除自定义API', | |||||
type: 'delete_api', | |||||
attentionTitle: '请确认是否删除如下自定义API', | |||||
attention: 'API名称: ' + api.apiName, | |||||
confirmBtnText: '删除', | |||||
}; | |||||
confirmDialog = ngDialog.open({ | |||||
template: '/app/views/dialog/confirm-dialog.html', | |||||
scope: $scope, | |||||
overlay: true | |||||
}); | |||||
}; | |||||
$scope.confirm = function () { | |||||
if ($scope.confirmDialog.type == 'delete_api') { | |||||
deleteApi($scope.currentApi); | |||||
} else { | |||||
console.error('error'); | |||||
} | |||||
}; | |||||
function deleteApi(api) { | |||||
GatewayApiService.deleteApi(api).success(function (data) { | |||||
if (data.code == 0) { | |||||
getApis(); | |||||
confirmDialog.close(); | |||||
} else { | |||||
alert('删除自定义API失败!' + data.msg); | |||||
} | |||||
}); | |||||
}; | |||||
$scope.addNewMatchPattern = function() { | |||||
var total; | |||||
if ($scope.currentApi.predicateItems == null) { | |||||
$scope.currentApi.predicateItems = []; | |||||
total = 0; | |||||
} else { | |||||
total = $scope.currentApi.predicateItems.length; | |||||
} | |||||
$scope.currentApi.predicateItems.splice(total + 1, 0, {matchStrategy: 0, pattern: ''}); | |||||
}; | |||||
$scope.removeMatchPattern = function($index) { | |||||
if ($scope.currentApi.predicateItems.length <= 1) { | |||||
// Should never happen since no remove button will display when only one predicateItem. | |||||
alert('至少有一个匹配规则'); | |||||
return; | |||||
} | |||||
$scope.currentApi.predicateItems.splice($index, 1); | |||||
}; | |||||
queryAppMachines(); | |||||
function queryAppMachines() { | |||||
MachineService.getAppMachines($scope.app).success( | |||||
function (data) { | |||||
if (data.code == 0) { | |||||
// $scope.machines = data.data; | |||||
if (data.data) { | |||||
$scope.machines = []; | |||||
$scope.macsInputOptions = []; | |||||
data.data.forEach(function (item) { | |||||
if (item.healthy) { | |||||
$scope.macsInputOptions.push({ | |||||
text: item.ip + ':' + item.port, | |||||
value: item.ip + ':' + item.port | |||||
}); | |||||
} | |||||
}); | |||||
} | |||||
if ($scope.macsInputOptions.length > 0) { | |||||
$scope.macInputModel = $scope.macsInputOptions[0].value; | |||||
} | |||||
} else { | |||||
$scope.macsInputOptions = []; | |||||
} | |||||
} | |||||
); | |||||
}; | |||||
$scope.$watch('macInputModel', function () { | |||||
if ($scope.macInputModel) { | |||||
getApis(); | |||||
} | |||||
}); | |||||
}] | |||||
); |
@@ -0,0 +1,251 @@ | |||||
var app = angular.module('sentinelDashboardApp'); | |||||
app.controller('GatewayFlowCtl', ['$scope', '$stateParams', 'GatewayFlowService', 'GatewayApiService', 'ngDialog', 'MachineService', | |||||
function ($scope, $stateParams, GatewayFlowService, GatewayApiService, ngDialog, MachineService) { | |||||
$scope.app = $stateParams.app; | |||||
$scope.rulesPageConfig = { | |||||
pageSize: 10, | |||||
currentPageIndex: 1, | |||||
totalPage: 1, | |||||
totalCount: 0, | |||||
}; | |||||
$scope.macsInputConfig = { | |||||
searchField: ['text', 'value'], | |||||
persist: true, | |||||
create: false, | |||||
maxItems: 1, | |||||
render: { | |||||
item: function (data, escape) { | |||||
return '<div>' + escape(data.text) + '</div>'; | |||||
} | |||||
}, | |||||
onChange: function (value, oldValue) { | |||||
$scope.macInputModel = value; | |||||
} | |||||
}; | |||||
getMachineRules(); | |||||
function getMachineRules() { | |||||
if (!$scope.macInputModel) { | |||||
return; | |||||
} | |||||
var mac = $scope.macInputModel.split(':'); | |||||
GatewayFlowService.queryRules($scope.app, mac[0], mac[1]).success( | |||||
function (data) { | |||||
if (data.code == 0 && data.data) { | |||||
$scope.rules = data.data; | |||||
$scope.rulesPageConfig.totalCount = $scope.rules.length; | |||||
} else { | |||||
$scope.rules = []; | |||||
$scope.rulesPageConfig.totalCount = 0; | |||||
} | |||||
}); | |||||
}; | |||||
$scope.getMachineRules = getMachineRules; | |||||
getApiNames(); | |||||
function getApiNames() { | |||||
if (!$scope.macInputModel) { | |||||
return; | |||||
} | |||||
var mac = $scope.macInputModel.split(':'); | |||||
GatewayApiService.queryApis($scope.app, mac[0], mac[1]).success( | |||||
function (data) { | |||||
if (data.code == 0 && data.data) { | |||||
$scope.apiNames = []; | |||||
data.data.forEach(function (api) { | |||||
$scope.apiNames.push(api["apiName"]); | |||||
}); | |||||
} | |||||
}); | |||||
} | |||||
$scope.intervalUnits = [{val: 0, desc: '秒'}, {val: 1, desc: '分'}, {val: 2, desc: '时'}, {val: 3, desc: '天'}]; | |||||
var gatewayFlowRuleDialog; | |||||
$scope.editRule = function (rule) { | |||||
$scope.currentRule = angular.copy(rule); | |||||
$scope.gatewayFlowRuleDialog = { | |||||
title: '编辑网关流控规则', | |||||
type: 'edit', | |||||
confirmBtnText: '保存' | |||||
}; | |||||
gatewayFlowRuleDialog = ngDialog.open({ | |||||
template: '/app/views/dialog/gateway/flow-rule-dialog.html', | |||||
width: 780, | |||||
overlay: true, | |||||
scope: $scope | |||||
}); | |||||
}; | |||||
$scope.addNewRule = function () { | |||||
var mac = $scope.macInputModel.split(':'); | |||||
$scope.currentRule = { | |||||
grade: 1, | |||||
app: $scope.app, | |||||
ip: mac[0], | |||||
port: mac[1], | |||||
resourceMode: 0, | |||||
interval: 1, | |||||
intervalUnit: 0, | |||||
controlBehavior: 0, | |||||
burst: 0, | |||||
maxQueueingTimeoutMs: 0 | |||||
}; | |||||
$scope.gatewayFlowRuleDialog = { | |||||
title: '新增网关流控规则', | |||||
type: 'add', | |||||
confirmBtnText: '新增' | |||||
}; | |||||
gatewayFlowRuleDialog = ngDialog.open({ | |||||
template: '/app/views/dialog/gateway/flow-rule-dialog.html', | |||||
width: 780, | |||||
overlay: true, | |||||
scope: $scope | |||||
}); | |||||
}; | |||||
$scope.saveRule = function () { | |||||
if (!GatewayFlowService.checkRuleValid($scope.currentRule)) { | |||||
return; | |||||
} | |||||
if ($scope.gatewayFlowRuleDialog.type === 'add') { | |||||
addNewRule($scope.currentRule); | |||||
} else if ($scope.gatewayFlowRuleDialog.type === 'edit') { | |||||
saveRule($scope.currentRule, true); | |||||
} | |||||
}; | |||||
$scope.useRouteID = function() { | |||||
$scope.currentRule.resource = ''; | |||||
}; | |||||
$scope.useCustormAPI = function() { | |||||
$scope.currentRule.resource = ''; | |||||
}; | |||||
$scope.useParamItem = function () { | |||||
$scope.currentRule.paramItem = { | |||||
parseStrategy: 0, | |||||
matchStrategy: 0 | |||||
}; | |||||
}; | |||||
$scope.notUseParamItem = function () { | |||||
$scope.currentRule.paramItem = null; | |||||
}; | |||||
$scope.useParamItemVal = function() { | |||||
$scope.currentRule.paramItem.pattern = ""; | |||||
$scope.currentRule.paramItem.matchStrategy = 0; | |||||
}; | |||||
$scope.notUseParamItemVal = function() { | |||||
$scope.currentRule.paramItem.pattern = null; | |||||
$scope.currentRule.paramItem.matchStrategy = null; | |||||
}; | |||||
function addNewRule(rule) { | |||||
GatewayFlowService.newRule(rule).success(function (data) { | |||||
if (data.code == 0) { | |||||
getMachineRules(); | |||||
gatewayFlowRuleDialog.close(); | |||||
} else { | |||||
alert('新增网关流控规则失败!' + data.msg); | |||||
} | |||||
}); | |||||
}; | |||||
function saveRule(rule, edit) { | |||||
GatewayFlowService.saveRule(rule).success(function (data) { | |||||
if (data.code == 0) { | |||||
getMachineRules(); | |||||
if (edit) { | |||||
gatewayFlowRuleDialog.close(); | |||||
} else { | |||||
confirmDialog.close(); | |||||
} | |||||
} else { | |||||
alert('修改网关流控规则失败!' + data.msg); | |||||
} | |||||
}); | |||||
}; | |||||
var confirmDialog; | |||||
$scope.deleteRule = function (rule) { | |||||
$scope.currentRule = rule; | |||||
$scope.confirmDialog = { | |||||
title: '删除网关流控规则', | |||||
type: 'delete_rule', | |||||
attentionTitle: '请确认是否删除如下规则', | |||||
attention: 'API名称: ' + rule.resource + ', ' + (rule.grade == 1 ? 'QPS阈值' : '线程数') + ': ' + rule.count, | |||||
confirmBtnText: '删除', | |||||
}; | |||||
confirmDialog = ngDialog.open({ | |||||
template: '/app/views/dialog/confirm-dialog.html', | |||||
scope: $scope, | |||||
overlay: true | |||||
}); | |||||
}; | |||||
$scope.confirm = function () { | |||||
if ($scope.confirmDialog.type == 'delete_rule') { | |||||
deleteRule($scope.currentRule); | |||||
} else { | |||||
console.error('error'); | |||||
} | |||||
}; | |||||
function deleteRule(rule) { | |||||
GatewayFlowService.deleteRule(rule).success(function (data) { | |||||
if (data.code == 0) { | |||||
getMachineRules(); | |||||
confirmDialog.close(); | |||||
} else { | |||||
alert('删除网关流控规则失败!' + data.msg); | |||||
} | |||||
}); | |||||
}; | |||||
queryAppMachines(); | |||||
function queryAppMachines() { | |||||
MachineService.getAppMachines($scope.app).success( | |||||
function (data) { | |||||
if (data.code == 0) { | |||||
if (data.data) { | |||||
$scope.machines = []; | |||||
$scope.macsInputOptions = []; | |||||
data.data.forEach(function (item) { | |||||
if (item.healthy) { | |||||
$scope.macsInputOptions.push({ | |||||
text: item.ip + ':' + item.port, | |||||
value: item.ip + ':' + item.port | |||||
}); | |||||
} | |||||
}); | |||||
} | |||||
if ($scope.macsInputOptions.length > 0) { | |||||
$scope.macInputModel = $scope.macsInputOptions[0].value; | |||||
} | |||||
} else { | |||||
$scope.macsInputOptions = []; | |||||
} | |||||
} | |||||
); | |||||
}; | |||||
$scope.$watch('macInputModel', function () { | |||||
if ($scope.macInputModel) { | |||||
getMachineRules(); | |||||
getApiNames(); | |||||
} | |||||
}); | |||||
}] | |||||
); |
@@ -0,0 +1,299 @@ | |||||
var app = angular.module('sentinelDashboardApp'); | |||||
app.controller('GatewayIdentityCtl', ['$scope', '$stateParams', 'IdentityService', | |||||
'ngDialog', 'GatewayFlowService', 'GatewayApiService', 'DegradeService', 'MachineService', | |||||
'$interval', '$location', '$timeout', | |||||
function ($scope, $stateParams, IdentityService, ngDialog, | |||||
GatewayFlowService, GatewayApiService, DegradeService, MachineService, $interval, $location, $timeout) { | |||||
$scope.app = $stateParams.app; | |||||
$scope.currentPage = 1; | |||||
$scope.pageSize = 16; | |||||
$scope.totalPage = 1; | |||||
$scope.totalCount = 0; | |||||
$scope.identities = []; | |||||
$scope.searchKey = ''; | |||||
$scope.macsInputConfig = { | |||||
searchField: ['text', 'value'], | |||||
persist: true, | |||||
create: false, | |||||
maxItems: 1, | |||||
render: { | |||||
item: function (data, escape) { | |||||
return '<div>' + escape(data.text) + '</div>'; | |||||
} | |||||
}, | |||||
onChange: function (value, oldValue) { | |||||
$scope.macInputModel = value; | |||||
} | |||||
}; | |||||
$scope.table = null; | |||||
getApiNames(); | |||||
function getApiNames() { | |||||
if (!$scope.macInputModel) { | |||||
return; | |||||
} | |||||
var mac = $scope.macInputModel.split(':'); | |||||
GatewayApiService.queryApis($scope.app, mac[0], mac[1]).success( | |||||
function (data) { | |||||
if (data.code == 0 && data.data) { | |||||
$scope.apiNames = []; | |||||
data.data.forEach(function (api) { | |||||
$scope.apiNames.push(api["apiName"]); | |||||
}); | |||||
} | |||||
}); | |||||
} | |||||
var gatewayFlowRuleDialog; | |||||
var gatewayFlowRuleDialogScope; | |||||
$scope.addNewGatewayFlowRule = function (resource) { | |||||
if (!$scope.macInputModel) { | |||||
return; | |||||
} | |||||
var mac = $scope.macInputModel.split(':'); | |||||
gatewayFlowRuleDialogScope = $scope.$new(true); | |||||
gatewayFlowRuleDialogScope.apiNames = $scope.apiNames; | |||||
gatewayFlowRuleDialogScope.intervalUnits = [{val: 0, desc: '秒'}, {val: 1, desc: '分'}, {val: 2, desc: '时'}, {val: 3, desc: '天'}]; | |||||
gatewayFlowRuleDialogScope.currentRule = { | |||||
grade: 1, | |||||
app: $scope.app, | |||||
ip: mac[0], | |||||
port: mac[1], | |||||
resourceMode: gatewayFlowRuleDialogScope.apiNames.indexOf(resource) == -1 ? 0 : 1, | |||||
resource: resource, | |||||
interval: 1, | |||||
intervalUnit: 0, | |||||
controlBehavior: 0, | |||||
burst: 0, | |||||
maxQueueingTimeoutMs: 0 | |||||
}; | |||||
gatewayFlowRuleDialogScope.gatewayFlowRuleDialog = { | |||||
title: '新增网关流控规则', | |||||
type: 'add', | |||||
confirmBtnText: '新增', | |||||
saveAndContinueBtnText: '新增并继续添加', | |||||
showAdvanceButton: true | |||||
}; | |||||
gatewayFlowRuleDialogScope.useRouteID = function() { | |||||
gatewayFlowRuleDialogScope.currentRule.resource = ''; | |||||
}; | |||||
gatewayFlowRuleDialogScope.useCustormAPI = function() { | |||||
gatewayFlowRuleDialogScope.currentRule.resource = ''; | |||||
}; | |||||
gatewayFlowRuleDialogScope.useParamItem = function () { | |||||
gatewayFlowRuleDialogScope.currentRule.paramItem = { | |||||
parseStrategy: 0, | |||||
matchStrategy: 0 | |||||
}; | |||||
}; | |||||
gatewayFlowRuleDialogScope.notUseParamItem = function () { | |||||
gatewayFlowRuleDialogScope.currentRule.paramItem = null; | |||||
}; | |||||
gatewayFlowRuleDialogScope.useParamItemVal = function() { | |||||
gatewayFlowRuleDialogScope.currentRule.paramItem.pattern = ""; | |||||
}; | |||||
gatewayFlowRuleDialogScope.notUseParamItemVal = function() { | |||||
gatewayFlowRuleDialogScope.currentRule.paramItem.pattern = null; | |||||
}; | |||||
gatewayFlowRuleDialogScope.saveRule = saveGatewayFlowRule; | |||||
gatewayFlowRuleDialogScope.saveRuleAndContinue = saveGatewayFlowRuleAndContinue; | |||||
gatewayFlowRuleDialogScope.onOpenAdvanceClick = function () { | |||||
gatewayFlowRuleDialogScope.gatewayFlowRuleDialog.showAdvanceButton = false; | |||||
}; | |||||
gatewayFlowRuleDialogScope.onCloseAdvanceClick = function () { | |||||
gatewayFlowRuleDialogScope.gatewayFlowRuleDialog.showAdvanceButton = true; | |||||
}; | |||||
gatewayFlowRuleDialog = ngDialog.open({ | |||||
template: '/app/views/dialog/gateway/flow-rule-dialog.html', | |||||
width: 780, | |||||
overlay: true, | |||||
scope: gatewayFlowRuleDialogScope | |||||
}); | |||||
}; | |||||
function saveGatewayFlowRule() { | |||||
if (!GatewayFlowService.checkRuleValid(gatewayFlowRuleDialogScope.currentRule)) { | |||||
return; | |||||
} | |||||
GatewayFlowService.newRule(gatewayFlowRuleDialogScope.currentRule).success(function (data) { | |||||
if (data.code === 0) { | |||||
gatewayFlowRuleDialog.close(); | |||||
let url = '/dashboard/gateway/flow/' + $scope.app; | |||||
$location.path(url); | |||||
} else { | |||||
alert('失败!'); | |||||
} | |||||
}).error((data, header, config, status) => { | |||||
alert('未知错误'); | |||||
}); | |||||
} | |||||
function saveGatewayFlowRuleAndContinue() { | |||||
if (!GatewayFlowService.checkRuleValid(gatewayFlowRuleDialogScope.currentRule)) { | |||||
return; | |||||
} | |||||
GatewayFlowService.newRule(gatewayFlowRuleDialogScope.currentRule).success(function (data) { | |||||
if (data.code == 0) { | |||||
gatewayFlowRuleDialog.close(); | |||||
} else { | |||||
alert('失败!'); | |||||
} | |||||
}); | |||||
} | |||||
var degradeRuleDialog; | |||||
$scope.addNewDegradeRule = function (resource) { | |||||
if (!$scope.macInputModel) { | |||||
return; | |||||
} | |||||
var mac = $scope.macInputModel.split(':'); | |||||
degradeRuleDialogScope = $scope.$new(true); | |||||
degradeRuleDialogScope.currentRule = { | |||||
enable: false, | |||||
grade: 0, | |||||
strategy: 0, | |||||
resource: resource, | |||||
limitApp: 'default', | |||||
app: $scope.app, | |||||
ip: mac[0], | |||||
port: mac[1] | |||||
}; | |||||
degradeRuleDialogScope.degradeRuleDialog = { | |||||
title: '新增降级规则', | |||||
type: 'add', | |||||
confirmBtnText: '新增', | |||||
saveAndContinueBtnText: '新增并继续添加' | |||||
}; | |||||
degradeRuleDialogScope.saveRule = saveDegradeRule; | |||||
degradeRuleDialogScope.saveRuleAndContinue = saveDegradeRuleAndContinue; | |||||
degradeRuleDialog = ngDialog.open({ | |||||
template: '/app/views/dialog/degrade-rule-dialog.html', | |||||
width: 680, | |||||
overlay: true, | |||||
scope: degradeRuleDialogScope | |||||
}); | |||||
}; | |||||
function saveDegradeRule() { | |||||
if (!DegradeService.checkRuleValid(degradeRuleDialogScope.currentRule)) { | |||||
return; | |||||
} | |||||
DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) { | |||||
if (data.code == 0) { | |||||
degradeRuleDialog.close(); | |||||
var url = '/dashboard/degrade/' + $scope.app; | |||||
$location.path(url); | |||||
} else { | |||||
alert('失败!'); | |||||
} | |||||
}); | |||||
} | |||||
function saveDegradeRuleAndContinue() { | |||||
if (!DegradeService.checkRuleValid(degradeRuleDialogScope.currentRule)) { | |||||
return; | |||||
} | |||||
DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) { | |||||
if (data.code == 0) { | |||||
degradeRuleDialog.close(); | |||||
} else { | |||||
alert('失败!'); | |||||
} | |||||
}); | |||||
} | |||||
var searchHandler; | |||||
$scope.searchChange = function (searchKey) { | |||||
$timeout.cancel(searchHandler); | |||||
searchHandler = $timeout(function () { | |||||
$scope.searchKey = searchKey; | |||||
reInitIdentityDatas(); | |||||
}, 600); | |||||
}; | |||||
function queryAppMachines() { | |||||
MachineService.getAppMachines($scope.app).success( | |||||
function (data) { | |||||
if (data.code === 0) { | |||||
if (data.data) { | |||||
$scope.machines = []; | |||||
$scope.macsInputOptions = []; | |||||
data.data.forEach(function (item) { | |||||
if (item.healthy) { | |||||
$scope.macsInputOptions.push({ | |||||
text: item.ip + ':' + item.port, | |||||
value: item.ip + ':' + item.port | |||||
}); | |||||
} | |||||
}); | |||||
} | |||||
if ($scope.macsInputOptions.length > 0) { | |||||
$scope.macInputModel = $scope.macsInputOptions[0].value; | |||||
} | |||||
} else { | |||||
$scope.macsInputOptions = []; | |||||
} | |||||
} | |||||
); | |||||
} | |||||
// Fetch all machines by current app name. | |||||
queryAppMachines(); | |||||
$scope.$watch('macInputModel', function () { | |||||
if ($scope.macInputModel) { | |||||
reInitIdentityDatas(); | |||||
} | |||||
}); | |||||
$scope.$on('$destroy', function () { | |||||
$interval.cancel(intervalId); | |||||
}); | |||||
var intervalId; | |||||
function reInitIdentityDatas() { | |||||
getApiNames(); | |||||
queryIdentities(); | |||||
}; | |||||
function queryIdentities() { | |||||
var mac = $scope.macInputModel.split(':'); | |||||
if (mac == null || mac.length < 2) { | |||||
return; | |||||
} | |||||
IdentityService.fetchClusterNodeOfMachine(mac[0], mac[1], $scope.searchKey).success( | |||||
function (data) { | |||||
if (data.code == 0 && data.data) { | |||||
$scope.identities = data.data; | |||||
$scope.totalCount = $scope.identities.length; | |||||
} else { | |||||
$scope.identities = []; | |||||
$scope.totalCount = 0; | |||||
} | |||||
} | |||||
); | |||||
}; | |||||
$scope.queryIdentities = queryIdentities; | |||||
}]); |
@@ -1,8 +1,8 @@ | |||||
var app = angular.module('sentinelDashboardApp'); | var app = angular.module('sentinelDashboardApp'); | ||||
app.controller('LoginCtl', ['$scope', '$state', '$window', 'AuthService', | app.controller('LoginCtl', ['$scope', '$state', '$window', 'AuthService', | ||||
function ($scope, $state, $window, LoginService) { | |||||
// If auth, jump to the index page directly | |||||
function ($scope, $state, $window, AuthService) { | |||||
// If auth passed, jump to the index page directly | |||||
if ($window.localStorage.getItem('session_sentinel_admin')) { | if ($window.localStorage.getItem('session_sentinel_admin')) { | ||||
$state.go('dashboard'); | $state.go('dashboard'); | ||||
} | } | ||||
@@ -20,7 +20,7 @@ app.controller('LoginCtl', ['$scope', '$state', '$window', 'AuthService', | |||||
var param = {"username": $scope.username, "password": $scope.password}; | var param = {"username": $scope.username, "password": $scope.password}; | ||||
LoginService.login(param).success(function (data) { | |||||
AuthService.login(param).success(function (data) { | |||||
if (data.code == 0) { | if (data.code == 0) { | ||||
$window.localStorage.setItem('session_sentinel_admin', { | $window.localStorage.setItem('session_sentinel_admin', { | ||||
username: data.data | username: data.data | ||||
@@ -28,24 +28,41 @@ | |||||
<a ui-sref="dashboard.metric({app: entry.app})"> | <a ui-sref="dashboard.metric({app: entry.app})"> | ||||
<i class="fa fa-bar-chart"></i> 实时监控</a> | <i class="fa fa-bar-chart"></i> 实时监控</a> | ||||
</li> | </li> | ||||
<li ui-sref-active="active"> | |||||
<li ui-sref-active="active" ng-if="entry.appType==0"> | |||||
<a ui-sref="dashboard.identity({app: entry.app})"> | <a ui-sref="dashboard.identity({app: entry.app})"> | ||||
<i class="glyphicon glyphicon-list-alt"></i> 簇点链路</a> | <i class="glyphicon glyphicon-list-alt"></i> 簇点链路</a> | ||||
</li> | </li> | ||||
<li ui-sref-active="active"> | |||||
<a ui-sref="dashboard.flowV1({app: entry.app})"> | |||||
<i class="glyphicon glyphicon-filter"></i> 流控规则</a> | |||||
<li ui-sref-active="active" ng-if="entry.appType==1"> | |||||
<a ui-sref="dashboard.gatewayIdentity({app: entry.app})"> | |||||
<i class="glyphicon glyphicon-filter"></i> 请求链路</a> | |||||
</li> | </li> | ||||
<!--<li ui-sref-active="active">--> | |||||
<!--<li ui-sref-active="active" ng-if="entry.appType==0">--> | |||||
<!--<a ui-sref="dashboard.flow({app: entry.app})">--> | <!--<a ui-sref="dashboard.flow({app: entry.app})">--> | ||||
<!--<i class="glyphicon glyphicon-filter"></i> 流控规则 V1</a>--> | <!--<i class="glyphicon glyphicon-filter"></i> 流控规则 V1</a>--> | ||||
<!--</li>--> | <!--</li>--> | ||||
<li ui-sref-active="active" ng-if="entry.appType==1"> | |||||
<a ui-sref="dashboard.gatewayApi({app: entry.app})"> | |||||
<i class="glyphicon glyphicon-tags"></i> API管理</a> | |||||
</li> | |||||
<li ui-sref-active="active" ng-if="entry.appType==1"> | |||||
<a ui-sref="dashboard.gatewayFlow({app: entry.app})"> | |||||
<i class="glyphicon glyphicon-filter"></i> 流控规则</a> | |||||
</li> | |||||
<li ui-sref-active="active" ng-if="entry.appType==0"> | |||||
<a ui-sref="dashboard.flowV1({app: entry.app})"> | |||||
<i class="glyphicon glyphicon-filter"></i> 流控规则</a> | |||||
</li> | |||||
<li ui-sref-active="active"> | <li ui-sref-active="active"> | ||||
<a ui-sref="dashboard.degrade({app: entry.app})"> | <a ui-sref="dashboard.degrade({app: entry.app})"> | ||||
<i class="glyphicon glyphicon-flash"></i> 降级规则</a> | <i class="glyphicon glyphicon-flash"></i> 降级规则</a> | ||||
</li> | </li> | ||||
<li ui-sref-active="active"> | |||||
<li ui-sref-active="active" ng-if="entry.appType==0"> | |||||
<a ui-sref="dashboard.paramFlow({app: entry.app})"> | <a ui-sref="dashboard.paramFlow({app: entry.app})"> | ||||
<i class="glyphicon glyphicon-fire"></i> 热点规则</a> | <i class="glyphicon glyphicon-fire"></i> 热点规则</a> | ||||
</li> | </li> | ||||
@@ -53,19 +70,19 @@ | |||||
<a ui-sref="dashboard.system({app: entry.app})"> | <a ui-sref="dashboard.system({app: entry.app})"> | ||||
<i class="glyphicon glyphicon-lock"></i> 系统规则</a> | <i class="glyphicon glyphicon-lock"></i> 系统规则</a> | ||||
</li> | </li> | ||||
<li ui-sref-active="active"> | |||||
<li ui-sref-active="active" ng-if="entry.appType==0"> | |||||
<a ui-sref="dashboard.authority({app: entry.app})"> | <a ui-sref="dashboard.authority({app: entry.app})"> | ||||
<i class="glyphicon glyphicon-check"></i> 授权规则</a> | <i class="glyphicon glyphicon-check"></i> 授权规则</a> | ||||
</li> | </li> | ||||
<li ui-sref-active="active"> | |||||
<li ui-sref-active="active" ng-if="entry.appType==0"> | |||||
<a ui-sref="dashboard.clusterAppServerList({app: entry.app})"> | <a ui-sref="dashboard.clusterAppServerList({app: entry.app})"> | ||||
<i class="glyphicon glyphicon-cloud"></i> 集群流控</a> | <i class="glyphicon glyphicon-cloud"></i> 集群流控</a> | ||||
</li> | </li> | ||||
<li ui-sref-active="active"> | <li ui-sref-active="active"> | ||||
<a ui-sref="dashboard.machine({app: entry.app})"> | <a ui-sref="dashboard.machine({app: entry.app})"> | ||||
<i class="glyphicon glyphicon-th-list"></i> 机器列表</a> | <i class="glyphicon glyphicon-th-list"></i> 机器列表</a> | ||||
</li> | </li> | ||||
</ul> | </ul> | ||||
<!-- /.nav-second-level --> | <!-- /.nav-second-level --> | ||||
</li> | </li> | ||||
@@ -20,7 +20,8 @@ angular.module('sentinelDashboardApp') | |||||
AppService.getApps().success( | AppService.getApps().success( | ||||
function (data) { | function (data) { | ||||
if (data.code === 0) { | if (data.code === 0) { | ||||
let initHashApp = $location.path().split('/')[3]; | |||||
var path = $location.path().split('/'); | |||||
let initHashApp = path[path.length - 1]; | |||||
$scope.apps = data.data; | $scope.apps = data.data; | ||||
$scope.apps = $scope.apps.map(function (item) { | $scope.apps = $scope.apps.map(function (item) { | ||||
if (item.app === initHashApp) { | if (item.app === initHashApp) { | ||||
@@ -6,13 +6,13 @@ app.service('AuthService', ['$http', function ($http) { | |||||
url: '/auth/login', | url: '/auth/login', | ||||
params: param, | params: param, | ||||
method: 'POST' | method: 'POST' | ||||
}) | |||||
} | |||||
}); | |||||
}; | |||||
this.logout = function () { | this.logout = function () { | ||||
return $http({ | return $http({ | ||||
url: '/auth/logout', | url: '/auth/logout', | ||||
method: 'POST' | method: 'POST' | ||||
}) | |||||
} | |||||
}); | |||||
}; | |||||
}]); | }]); |
@@ -0,0 +1,73 @@ | |||||
var app = angular.module('sentinelDashboardApp'); | |||||
app.service('GatewayApiService', ['$http', function ($http) { | |||||
this.queryApis = function (app, ip, port) { | |||||
var param = { | |||||
app: app, | |||||
ip: ip, | |||||
port: port | |||||
}; | |||||
return $http({ | |||||
url: '/gateway/api/list.json', | |||||
params: param, | |||||
method: 'GET' | |||||
}); | |||||
}; | |||||
this.newApi = function (api) { | |||||
return $http({ | |||||
url: '/gateway/api/new.json', | |||||
data: api, | |||||
method: 'POST' | |||||
}); | |||||
}; | |||||
this.saveApi = function (api) { | |||||
return $http({ | |||||
url: '/gateway/api/save.json', | |||||
data: api, | |||||
method: 'POST' | |||||
}); | |||||
}; | |||||
this.deleteApi = function (api) { | |||||
var param = { | |||||
id: api.id, | |||||
app: api.app | |||||
}; | |||||
return $http({ | |||||
url: '/gateway/api/delete.json', | |||||
params: param, | |||||
method: 'POST' | |||||
}); | |||||
}; | |||||
this.checkApiValid = function (api, apiNames) { | |||||
if (api.apiName === undefined || api.apiName === '') { | |||||
alert('API名称不能为空'); | |||||
return false; | |||||
} | |||||
if (api.predicateItems == null || api.predicateItems.length === 0) { | |||||
// Should never happen since no remove button will display when only one predicateItem. | |||||
alert('至少有一个匹配规则'); | |||||
return false; | |||||
} | |||||
for (var i = 0; i < api.predicateItems.length; i++) { | |||||
var predicateItem = api.predicateItems[i]; | |||||
var pattern = predicateItem.pattern; | |||||
if (pattern === undefined || pattern === '') { | |||||
alert('匹配串不能为空,请检查'); | |||||
return false; | |||||
} | |||||
} | |||||
if (apiNames.indexOf(api.apiName) !== -1) { | |||||
alert('API名称(' + api.apiName + ')已存在'); | |||||
return false; | |||||
} | |||||
return true; | |||||
}; | |||||
}]); |
@@ -0,0 +1,76 @@ | |||||
var app = angular.module('sentinelDashboardApp'); | |||||
app.service('GatewayFlowService', ['$http', function ($http) { | |||||
this.queryRules = function (app, ip, port) { | |||||
var param = { | |||||
app: app, | |||||
ip: ip, | |||||
port: port | |||||
}; | |||||
return $http({ | |||||
url: '/gateway/flow/list.json', | |||||
params: param, | |||||
method: 'GET' | |||||
}); | |||||
}; | |||||
this.newRule = function (rule) { | |||||
return $http({ | |||||
url: '/gateway/flow/new.json', | |||||
data: rule, | |||||
method: 'POST' | |||||
}); | |||||
}; | |||||
this.saveRule = function (rule) { | |||||
return $http({ | |||||
url: '/gateway/flow/save.json', | |||||
data: rule, | |||||
method: 'POST' | |||||
}); | |||||
}; | |||||
this.deleteRule = function (rule) { | |||||
var param = { | |||||
id: rule.id, | |||||
app: rule.app | |||||
}; | |||||
return $http({ | |||||
url: '/gateway/flow/delete.json', | |||||
params: param, | |||||
method: 'POST' | |||||
}); | |||||
}; | |||||
this.checkRuleValid = function (rule) { | |||||
if (rule.resource === undefined || rule.resource === '') { | |||||
alert('API名称不能为空'); | |||||
return false; | |||||
} | |||||
if (rule.paramItem != null) { | |||||
if (rule.paramItem.parseStrategy == 2 || | |||||
rule.paramItem.parseStrategy == 3 || | |||||
rule.paramItem.parseStrategy == 4) { | |||||
if (rule.paramItem.fieldName === undefined || rule.paramItem.fieldName === '') { | |||||
alert('当参数属性为Header、URL参数、Cookie时,参数名称不能为空'); | |||||
return false; | |||||
} | |||||
if (rule.paramItem.pattern === '') { | |||||
alert('匹配串不能为空'); | |||||
return false; | |||||
} | |||||
} | |||||
} | |||||
if (rule.count === undefined || rule.count < 0) { | |||||
alert((rule.grade === 1 ? 'QPS阈值' : '线程数') + '必须大于等于 0'); | |||||
return false; | |||||
} | |||||
return true; | |||||
}; | |||||
}]); |
@@ -1,23 +1,25 @@ | |||||
var app = angular.module('sentinelDashboardApp'); | var app = angular.module('sentinelDashboardApp'); | ||||
app.service('MachineService', ['$http', '$httpParamSerializerJQLike', function ($http, $httpParamSerializerJQLike) { | |||||
this.getAppMachines = function (app) { | |||||
return $http({ | |||||
url: 'app/' + app + '/machines.json', | |||||
method: 'GET' | |||||
}); | |||||
}; | |||||
this.removeAppMachine = function (app, ip, port) { | |||||
return $http({ | |||||
url: 'app/' + app + '/machine/remove.json', | |||||
method: 'POST', | |||||
headers: { | |||||
"Content-type": 'application/x-www-form-urlencoded; charset=UTF-8' | |||||
}, | |||||
data: $httpParamSerializerJQLike({ | |||||
ip: ip, | |||||
port: port | |||||
}) | |||||
}); | |||||
}; | |||||
}]); | |||||
app.service('MachineService', ['$http', '$httpParamSerializerJQLike', | |||||
function ($http, $httpParamSerializerJQLike) { | |||||
this.getAppMachines = function (app) { | |||||
return $http({ | |||||
url: 'app/' + app + '/machines.json', | |||||
method: 'GET' | |||||
}); | |||||
}; | |||||
this.removeAppMachine = function (app, ip, port) { | |||||
return $http({ | |||||
url: 'app/' + app + '/machine/remove.json', | |||||
method: 'POST', | |||||
headers: { | |||||
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8' | |||||
}, | |||||
data: $httpParamSerializerJQLike({ | |||||
ip: ip, | |||||
port: port | |||||
}) | |||||
}); | |||||
}; | |||||
}] | |||||
); |
@@ -0,0 +1,52 @@ | |||||
<div> | |||||
<span class="brand" style="font-weight:bold;">{{gatewayApiDialog.title}}</span> | |||||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;"> | |||||
<div class="panel-body"> | |||||
<div class="row"> | |||||
<form role="form" class="form-horizontal"> | |||||
<div class="form-group"> | |||||
<label class="col-sm-2 control-label">API名称</label> | |||||
<div class="col-sm-9"> | |||||
<input type="text" ng-if="gatewayApiDialog.type == 'edit'" class="form-control" placeholder="请输入" ng-model='currentApi.apiName' | |||||
disabled="" /> | |||||
<input type="text" ng-if="gatewayApiDialog.type == 'add'" class="form-control highlight-border" placeholder="请输入" ng-model='currentApi.apiName' /> | |||||
</div> | |||||
</div> | |||||
<div class="form-group" ng-repeat="predicateItem in currentApi.predicateItems track by $index"> | |||||
<label class="col-sm-2 control-label">匹配模式</label> | |||||
<div class="col-sm-4 control-label"> | |||||
<div class="form-control highlight-border" align="center"> | |||||
<input type="radio" value="0" checked ng-model="predicateItem.matchStrategy" title="精确" /> 精确 | |||||
<input type="radio" value="1" ng-model="predicateItem.matchStrategy" title="前缀" /> 前缀  | |||||
<input type="radio" value="2" ng-model="predicateItem.matchStrategy" title="正则" /> 正则  | |||||
</div> | |||||
</div> | |||||
<label class="col-sm-2 control-label">匹配串</label> | |||||
<div class="col-sm-3 control-label"> | |||||
<input type='text' ng-model="predicateItem.pattern" class="form-control highlight-border" placeholder="请输入" /> | |||||
</div> | |||||
<div class="col-sm-1 control-label" align="center"> | |||||
<button class="btn btn-default-inverse" ng-click="removeMatchPattern($index)" align="center" ng-if="currentApi.predicateItems.length>1">X</button> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<label class="col-sm-11 control-label" style="text-align: center;"> | |||||
<button class="btn btn-default-inverse" ng-click="addNewMatchPattern()" align="center"> | |||||
<i class="fa fa-plus"></i>新增匹配规则 | |||||
</button> | |||||
</label> | |||||
</div> | |||||
</form> | |||||
</div> | |||||
<div class="separator"></div> | |||||
<div clss="row" style="margin-top: 20px;"> | |||||
<button class="btn btn-outline-danger" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="closeThisDialog()">取消</button> | |||||
<button class="btn btn-outline-success" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="saveApi()">{{gatewayApiDialog.confirmBtnText}}</button> | |||||
<button ng-if="gatewayApiDialog.saveAndContinueBtnText" class="btn btn-default" style="float:right; height: 30px;font-size: 12px;" | |||||
ng-click="saveApiAndContinue()">{{gatewayApiDialog.saveAndContinueBtnText}}</button> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> |
@@ -0,0 +1,172 @@ | |||||
<div> | |||||
<span class="brand" style="font-weight:bold;">{{gatewayFlowRuleDialog.title}}</span> | |||||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;"> | |||||
<div class="panel-body"> | |||||
<div class="row"> | |||||
<form role="form" class="form-horizontal"> | |||||
<div class="form-group"> | |||||
<label class="col-sm-2 control-label">API类型</label> | |||||
<div class="col-sm-9"> | |||||
<div class="form-control highlight-border" align="center"> | |||||
<input type="radio" ng-if="gatewayFlowRuleDialog.type == 'edit'" value="0" checked ng-model='currentRule.resourceMode' | |||||
disabled="" title="Route ID" /><span ng-if="gatewayFlowRuleDialog.type == 'edit'"> Route ID </span> | |||||
<input type="radio" ng-if="gatewayFlowRuleDialog.type == 'add'" value="0" checked ng-model='currentRule.resourceMode' | |||||
title="Route ID" ng-click="useRouteID()"/><span ng-if="gatewayFlowRuleDialog.type == 'add'"> Route ID </span> | |||||
<input type="radio" ng-if="gatewayFlowRuleDialog.type == 'edit'" value="1" ng-model='currentRule.resourceMode' | |||||
disabled="" title="API分组" /><span ng-if="gatewayFlowRuleDialog.type == 'edit'"> API分组  </span> | |||||
<input type="radio" ng-if="gatewayFlowRuleDialog.type == 'add'" value="1" ng-model='currentRule.resourceMode' | |||||
title="API分组" ng-click="useCustormAPI()"/><span ng-if="gatewayFlowRuleDialog.type == 'add'"> API分组  </span> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<label class="col-sm-2 control-label">API名称</label> | |||||
<div class="col-sm-9"> | |||||
<input type="text" ng-if="currentRule.resourceMode == 0 && gatewayFlowRuleDialog.type == 'edit'" class="form-control" placeholder="请输入Route ID" ng-model='currentRule.resource' | |||||
disabled="" /> | |||||
<input type="text" ng-if="currentRule.resourceMode == 0 && gatewayFlowRuleDialog.type == 'add'" class="form-control highlight-border" placeholder="请输入Route ID" ng-model='currentRule.resource' /> | |||||
<select ng-if="currentRule.resourceMode == 1 && gatewayFlowRuleDialog.type == 'edit'" ng-model="currentRule.resource" ng-init="selectedName = currentRule.resource" | |||||
disabled="" ng-options="name for name in apiNames" class="form-control"> | |||||
</select> | |||||
<select ng-if="currentRule.resourceMode == 1 && gatewayFlowRuleDialog.type == 'add'" ng-model="currentRule.resource" ng-init="currentRule.resource" | |||||
ng-options="name for name in apiNames" class="form-control"> | |||||
</select> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<label class="col-sm-2 control-label">针对请求属性</label> | |||||
<div class="col-sm-2"> | |||||
<label class="checkbox-inline"> | |||||
<input type="checkbox" ng-if="currentRule.paramItem != null" checked ng-click="notUseParamItem()" /> | |||||
<input type="checkbox" ng-if="currentRule.paramItem == null" ng-click="useParamItem()" /> | |||||
</label> | |||||
</div> | |||||
</div> | |||||
<div class="form-group" ng-if="currentRule.paramItem != null"> | |||||
<label class="col-sm-2 control-label">参数属性</label> | |||||
<div class="col-sm-9"> | |||||
<div class="form-control highlight-border" align="center"> | |||||
<input type="radio" name="parseStrategy" value="0" checked ng-model='currentRule.paramItem.parseStrategy' title="Client IP" /> Client IP | |||||
<input type="radio" name="parseStrategy" value="1" ng-model='currentRule.paramItem.parseStrategy' title="Remote Host" /> Remote Host | |||||
<input type="radio" name="parseStrategy" value="2" ng-model='currentRule.paramItem.parseStrategy' title="Header" /> Header | |||||
<input type="radio" name="parseStrategy" value="3" ng-model='currentRule.paramItem.parseStrategy' title="URL参数" /> URL参数 | |||||
<input type="radio" name="parseStrategy" value="4" ng-model='currentRule.paramItem.parseStrategy' title="Cookie" /> Cookie | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="form-group" ng-if="currentRule.paramItem != null && (currentRule.paramItem.parseStrategy == 2 || currentRule.paramItem.parseStrategy == 3 || currentRule.paramItem.parseStrategy == 4)"> | |||||
<label class="col-sm-2 control-label"> | |||||
<span ng-if="currentRule.paramItem.parseStrategy==2">Header名称</span> | |||||
<span ng-if="currentRule.paramItem.parseStrategy==3">URL参数名称</span> | |||||
<span ng-if="currentRule.paramItem.parseStrategy==4">Cookie名称</span> | |||||
</label> | |||||
<div class="col-sm-9"> | |||||
<input type="text" name="fieldName" class="form-control highlight-border" placeholder="请输入" ng-model='currentRule.paramItem.fieldName' /> | |||||
</div> | |||||
</div> | |||||
<div class="form-group" ng-if="currentRule.paramItem != null && (currentRule.paramItem.parseStrategy == 2 || currentRule.paramItem.parseStrategy == 3 || currentRule.paramItem.parseStrategy == 4)"> | |||||
<label class="col-sm-2 control-label">属性值匹配</label> | |||||
<div class="col-sm-2"> | |||||
<label class="checkbox-inline"> | |||||
<input type="checkbox" ng-if="currentRule.paramItem.pattern != null" checked ng-click="notUseParamItemVal()"/> | |||||
<input type="checkbox" ng-if="currentRule.paramItem.pattern == null" ng-click="useParamItemVal()"/> | |||||
</label> | |||||
</div> | |||||
</div> | |||||
<div class="form-group" ng-if="currentRule.paramItem.pattern != null"> | |||||
<label class="col-sm-2 control-label">匹配模式</label> | |||||
<div class="col-sm-4 control-label"> | |||||
<div class="form-control highlight-border" align="center"> | |||||
<input type="radio" value="0" checked ng-model="currentRule.paramItem.matchStrategy" title="精确" /> 精确 | |||||
<input type="radio" value="3" ng-model="currentRule.paramItem.matchStrategy" title="子串" /> 子串 | |||||
<input type="radio" value="2" ng-model="currentRule.paramItem.matchStrategy" title="正则" /> 正则 | |||||
</div> | |||||
</div> | |||||
<label class="col-sm-2 control-label">匹配串</label> | |||||
<div class="col-sm-3 control-label"> | |||||
<input type='text' ng-model="currentRule.paramItem.pattern" class="form-control highlight-border" placeholder="匹配串" /> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<label class="col-sm-2 control-label">阈值类型</label> | |||||
<div class="col-sm-9"> | |||||
<div class="form-control highlight-border" align="center"> | |||||
<input type="radio" name="grade" value="1" checked ng-model="currentRule.grade" title="QPS" /> QPS | |||||
<input type="radio" name="grade" value="0" ng-model="currentRule.grade" title="线程数" /> 线程数 | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div> | |||||
<label class="col-sm-2 control-label"> | |||||
<span ng-if="currentRule.grade==1">QPS阈值</span> | |||||
<span ng-if="currentRule.grade==0">线程数</span> | |||||
</label> | |||||
<div class="col-sm-3"> | |||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.count' placeholder="阈值" /> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="form-group" ng-if="currentRule.grade==1"> | |||||
<div> | |||||
<label class="col-sm-2 control-label">间隔</label> | |||||
<div class="col-sm-3"> | |||||
<input type='number' id="txtInterval" min="1" class="form-control highlight-border" ng-model='currentRule.interval' placeholder="统计窗口时间长度" /> | |||||
</div> | |||||
<div class="col-sm-2"> | |||||
<select ng-model="currentRule.intervalUnit" ng-init="currentRule.intervalUnit" | |||||
ng-options="intervalUnit.val as intervalUnit.desc for intervalUnit in intervalUnits" class="form-control" ng-click="changeIntervalUnit()"> | |||||
</select> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="form-group" ng-if="currentRule.grade==1"> | |||||
<label class="col-sm-2 control-label">流控方式</label> | |||||
<div class="col-sm-9"> | |||||
<div class="form-control highlight-border" align="center"> | |||||
<input type="radio" name="controlBehavior" value="0" checked ng-model='currentRule.controlBehavior' title="快速失败" /> 快速失败 | |||||
<input type="radio" name="controlBehavior" value="2" ng-model='currentRule.controlBehavior' title="匀速排队" /> 匀速排队  | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="form-group" ng-if="currentRule.grade==1 && currentRule.controlBehavior==0"> | |||||
<div> | |||||
<label class="col-sm-2 control-label">Burst size</label> | |||||
<div class="col-sm-3"> | |||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.burst' placeholder="突发请求额外允许数" /> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="form-group" ng-if="currentRule.grade==1 && currentRule.controlBehavior==2"> | |||||
<div> | |||||
<label class="col-sm-2 control-label">超时时间</label> | |||||
<div class="col-sm-3"> | |||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.maxQueueingTimeoutMs' placeholder="排队等待时间(ms)" /> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
</div> | |||||
<div class="separator"></div> | |||||
<div clss="row" style="margin-top: 20px;"> | |||||
<button class="btn btn-outline-danger" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="closeThisDialog()">取消</button> | |||||
<button class="btn btn-outline-success" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="saveRule()">{{gatewayFlowRuleDialog.confirmBtnText}}</button> | |||||
<button ng-if="gatewayFlowRuleDialog.saveAndContinueBtnText" class="btn btn-default" style="float:right; height: 30px;font-size: 12px;" | |||||
ng-click="saveRuleAndContinue()">{{gatewayFlowRuleDialog.saveAndContinueBtnText}}</button> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> |
@@ -0,0 +1,87 @@ | |||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;"> | |||||
<div class="col-md-6" style="margin-bottom: 10px;"> | |||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span> | |||||
</div> | |||||
<div class="col-md-6"> | |||||
<button class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ng-disabled="!macInputModel" ng-click="addNewApi()"> | |||||
<i class="fa fa-plus"></i> 新增API</button> | |||||
</div> | |||||
</div> | |||||
<div class="separator"></div> | |||||
<div class="container-fluid"> | |||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;"> | |||||
<div class="col-md-12"> | |||||
<div class="card"> | |||||
<div class="inputs-header"> | |||||
<span class="brand" style="font-size: 13px;">API管理</span> | |||||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="getApis()">刷新</button> | |||||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey"> | |||||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;"> | |||||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel" | |||||
placeholder="机器"></selectize> | |||||
</div> | |||||
</div> | |||||
<!--.tools-header --> | |||||
<div class="card-body" style="padding: 0px 0px;"> | |||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;"> | |||||
<thead> | |||||
<tr style="background: #F3F5F7;"> | |||||
<td> | |||||
API名称 | |||||
</td> | |||||
<td> | |||||
匹配模式 | |||||
</td> | |||||
<td> | |||||
匹配串 | |||||
</td> | |||||
<td> | |||||
操作 | |||||
</td> | |||||
</tr> | |||||
</thead> | |||||
<tbody> | |||||
<tr dir-paginate="api in apis | filter: searchKey | itemsPerPage: apisPageConfig.pageSize" current-page="apisPageConfig.currentPageIndex" | |||||
pagination-id="entriesPagination"> | |||||
<td ng-if="api.firstFlag==0" rowspan="{{api.itemSize}}" style="vertical-align: middle;">{{api.apiName}}</td> | |||||
<td> | |||||
<span ng-if="api.matchStrategy == 0">精确</span> | |||||
<span ng-if="api.matchStrategy == 1">前缀</span> | |||||
<span ng-if="api.matchStrategy == 2">正则</span> | |||||
</td> | |||||
<td>{{api.pattern}}</td> | |||||
<td ng-if="api.firstFlag==0" rowspan="{{api.itemSize}}" style="vertical-align: middle;"> | |||||
<button class="btn btn-xs btn-default" type="button" ng-click="editApi(api)" style="font-size: 12px; height:25px;">编辑</button> | |||||
<button class="btn btn-xs btn-default" type="button" ng-click="deleteApi(api)" style="font-size: 12px; height:25px;">删除</button> | |||||
</td> | |||||
</tr> | |||||
</tbody> | |||||
</table> | |||||
</div> | |||||
<!-- .card-body --> | |||||
<div class="pagination-footer"> | |||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination" | |||||
on-page-change=""> | |||||
</dir-pagination-controls> | |||||
<div class="tools" style=""> | |||||
<span>共 {{apisPageConfig.totalCount}} 条记录, </span> | |||||
<span> | |||||
每页 | |||||
<input class="form-control" ng-model="apisPageConfig.pageSize"> 条记录 | |||||
</span> | |||||
<!--<span>第 {{apisPageConfig.currentPageIndex}} / {{apisPageConfig.totalPage}} 页</span>--> | |||||
</div> | |||||
<!-- .tools --> | |||||
</div> | |||||
<!-- pagination-footer --> | |||||
</div> | |||||
<!-- .card --> | |||||
</div> | |||||
<!-- .col-md-12 --> | |||||
</div> | |||||
<!-- --> | |||||
</div> | |||||
<!-- .container-fluid --> |
@@ -0,0 +1,94 @@ | |||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;"> | |||||
<div class="col-md-6" style="margin-bottom: 10px;"> | |||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span> | |||||
</div> | |||||
<div class="col-md-6"> | |||||
<button class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ng-disabled="!macInputModel" ng-click="addNewRule()"> | |||||
<i class="fa fa-plus"></i> 新增流控规则</button> | |||||
</div> | |||||
</div> | |||||
<div class="separator"></div> | |||||
<div class="container-fluid"> | |||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;"> | |||||
<div class="col-md-12"> | |||||
<div class="card"> | |||||
<div class="inputs-header"> | |||||
<span class="brand" style="font-size: 13px;">网关流控规则</span> | |||||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="getMachineRules()">刷新</button> | |||||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey"> | |||||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;"> | |||||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel" | |||||
placeholder="机器"></selectize> | |||||
</div> | |||||
</div> | |||||
<!--.tools-header --> | |||||
<div class="card-body" style="padding: 0px 0px;"> | |||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;"> | |||||
<thead> | |||||
<tr style="background: #F3F5F7;"> | |||||
<td> | |||||
API名称 | |||||
</td> | |||||
<td> | |||||
API类型 | |||||
</td> | |||||
<td> | |||||
阈值类型 | |||||
</td> | |||||
<td> | |||||
单机阈值 | |||||
</td> | |||||
<td> | |||||
操作 | |||||
</td> | |||||
</tr> | |||||
</thead> | |||||
<tbody> | |||||
<tr dir-paginate="rule in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize " current-page="rulesPageConfig.currentPageIndex" | |||||
pagination-id="entriesPagination"> | |||||
<td>{{rule.resource}}</td> | |||||
<td> | |||||
<span ng-if="rule.resourceMode == 0">Route ID</span> | |||||
<span ng-if="rule.resourceMode == 1">API分组</span> | |||||
</td> | |||||
<td> | |||||
<span ng-if="rule.grade == 1">QPS</span> | |||||
<span ng-if="rule.grade == 0">线程数</span> | |||||
</td> | |||||
<td>{{rule.count}}</td> | |||||
<td> | |||||
<button class="btn btn-xs btn-default" type="button" ng-click="editRule(rule)" style="font-size: 12px; height:25px;">编辑</button> | |||||
<button class="btn btn-xs btn-default" type="button" ng-click="deleteRule(rule)" style="font-size: 12px; height:25px;">删除</button> | |||||
</td> | |||||
</tr> | |||||
</tbody> | |||||
</table> | |||||
</div> | |||||
<!-- .card-body --> | |||||
<div class="pagination-footer"> | |||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination" | |||||
on-page-change=""> | |||||
</dir-pagination-controls> | |||||
<div class="tools" style=""> | |||||
<span>共 {{rulesPageConfig.totalCount}} 条记录, </span> | |||||
<span> | |||||
每页 | |||||
<input class="form-control" ng-model="rulesPageConfig.pageSize"> 条记录 | |||||
</span> | |||||
<!--<span>第 {{apisPageConfig.currentPageIndex}} / {{apisPageConfig.totalPage}} 页</span>--> | |||||
</div> | |||||
<!-- .tools --> | |||||
</div> | |||||
<!-- pagination-footer --> | |||||
</div> | |||||
<!-- .card --> | |||||
</div> | |||||
<!-- .col-md-12 --> | |||||
</div> | |||||
<!-- --> | |||||
</div> | |||||
<!-- .container-fluid --> |
@@ -0,0 +1,98 @@ | |||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;"> | |||||
<div class="col-md-6" style="margin-bottom: 10px;"> | |||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span> | |||||
</div> | |||||
</div> | |||||
<div class="separator"></div> | |||||
<div class="container-fluid"> | |||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;"> | |||||
<div class="col-md-12"> | |||||
<div class="card"> | |||||
<div class="inputs-header"> | |||||
<span class="brand" style="font-size: 13px;">请求链路</span> | |||||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="queryIdentities()">刷新</button> | |||||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey" ng-change="searchChange(searchKey)"> | |||||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;"> | |||||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel" | |||||
placeholder="机器"></selectize> | |||||
</div> | |||||
</div> | |||||
<!--.tools-header --> | |||||
<div class="card-body" style="padding: 0px 0px;"> | |||||
<table rz-table id="identities" class="table" style="border-left: none; border-right:none;margin-top: 10px;"> | |||||
<thead> | |||||
<thead> | |||||
<tr style="background: #F3F5F7;"> | |||||
<td style="width: 34%;"> | |||||
资源名 | |||||
</td> | |||||
<td style="width: 6%;"> | |||||
资源类型 | |||||
</td> | |||||
<td style="width: 7%;">通过QPS</td> | |||||
<td style="width: 7%;">拒绝QPS</td> | |||||
<td style="width: 5%;">线程数</td> | |||||
<td style="width: 6%;">平均RT</td> | |||||
<td style="width: 6%;">分钟通过</td> | |||||
<td style="width: 6%;">分钟拒绝</td> | |||||
<td style="width: 23%">操作</td> | |||||
</tr> | |||||
<tr></tr> | |||||
</thead> | |||||
<tbody> | |||||
<tr dir-paginate="resource in identities | itemsPerPage: pageSize" current-page="currentPage" pagination-id="entriesPagination" | |||||
data-tt-id="{{resource.ttId}}" data-tt-parent-id="{{resource.parentTtId}}" data-tt-visible="{{resource.visible}}"> | |||||
<td style="white-space: normal; text-align: left;"> | |||||
<span style="word-wrap:break-word;word-break:break-all;">{{resource.resource}}</span> | |||||
</td> | |||||
<td> | |||||
<span ng-if="apiNames.indexOf(resource.resource) == -1">Route ID</span> | |||||
<span ng-if="apiNames.indexOf(resource.resource) != -1">自定义API</span> | |||||
</td> | |||||
<td>{{resource.passQps}}</td> | |||||
<td>{{resource.blockQps}}</td> | |||||
<td>{{resource.threadNum}}</td> | |||||
<td>{{resource.averageRt}}</td> | |||||
<td>{{resource.oneMinutePass}}</td> | |||||
<td ng-if="$index==pageSize-1 || (currentPage>=identities.length/pageSize && $index==identities.length%pageSize-1)" ng-init="initTreeTable()"> | |||||
{{resource.oneMinuteBlock}}</td> | |||||
<td ng-if="!($index==pageSize-1 || (currentPage>=identities.length/pageSize && $index==identities.length%pageSize-1))"> {{resource.oneMinuteBlock}}</td> | |||||
<td> | |||||
<div class="control-group"> | |||||
<button class="btn btn-xs btn-default" type="button" ng-click="addNewGatewayFlowRule(resource.resource)" style="font-size: 12px; height:25px;"> | |||||
<i class="fa fa-plus"></i> 流控</button> | |||||
<button class="btn btn-xs btn-default" type="button" ng-click="addNewDegradeRule(resource.resource)" style="font-size: 12px; height:25px;"> | |||||
<i class="fa fa-plus"></i> 降级</button> | |||||
</div> | |||||
</td> | |||||
</tr> | |||||
</tbody> | |||||
</table> | |||||
</div> | |||||
<!-- .card-body --> | |||||
<div class="pagination-footer"> | |||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination" | |||||
on-page-change=""> | |||||
</dir-pagination-controls> | |||||
<div class="tools"> | |||||
<span>共 {{totalCount}} 条记录, </span> | |||||
<span> | |||||
每页 | |||||
<input class="form-control" ng-model="pageSize"> 条记录 | |||||
</span> | |||||
<!--<span>第 {{currentPage}} / {{totalPage}} 页</span>--> | |||||
</div> | |||||
<!-- .tools --> | |||||
</div> | |||||
<!-- pagination-footer --> | |||||
</div> | |||||
<!-- .card --> | |||||
</div> | |||||
<!-- .col-md-12 --> | |||||
</div> | |||||
<!-- --> | |||||
</div> | |||||
<!-- .container-fluid --> |
@@ -55,6 +55,8 @@ const JS_APP = [ | |||||
'app/scripts/services/param_flow_service.js', | 'app/scripts/services/param_flow_service.js', | ||||
'app/scripts/services/authority_service.js', | 'app/scripts/services/authority_service.js', | ||||
'app/scripts/services/cluster_state_service.js', | 'app/scripts/services/cluster_state_service.js', | ||||
'app/scripts/services/gateway/api_service.js', | |||||
'app/scripts/services/gateway/flow_service.js', | |||||
]; | ]; | ||||
gulp.task('lib', function () { | gulp.task('lib', function () { | ||||
@@ -0,0 +1,327 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.controller.gateway; | |||||
import com.alibaba.csp.sentinel.dashboard.auth.FakeAuthServiceImpl; | |||||
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; | |||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity; | |||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiPredicateItemEntity; | |||||
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; | |||||
import com.alibaba.csp.sentinel.dashboard.discovery.SimpleMachineDiscovery; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.Result; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.AddApiReqVo; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.ApiPredicateItemVo; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.UpdateApiReqVo; | |||||
import com.alibaba.csp.sentinel.dashboard.repository.gateway.InMemApiDefinitionStore; | |||||
import com.alibaba.fastjson.JSON; | |||||
import com.alibaba.fastjson.JSONObject; | |||||
import com.alibaba.fastjson.TypeReference; | |||||
import org.apache.commons.lang3.time.DateUtils; | |||||
import org.junit.Before; | |||||
import org.junit.Test; | |||||
import org.junit.runner.RunWith; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; | |||||
import org.springframework.boot.test.mock.mockito.MockBean; | |||||
import org.springframework.context.annotation.Import; | |||||
import org.springframework.http.MediaType; | |||||
import org.springframework.test.context.junit4.SpringRunner; | |||||
import org.springframework.test.web.servlet.MockMvc; | |||||
import org.springframework.test.web.servlet.MvcResult; | |||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; | |||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; | |||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers; | |||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers; | |||||
import java.util.*; | |||||
import java.util.concurrent.CompletableFuture; | |||||
import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*; | |||||
import static org.junit.Assert.*; | |||||
import static org.mockito.BDDMockito.*; | |||||
/** | |||||
* Test cases for {@link GatewayApiController}. | |||||
* | |||||
* @author cdfive | |||||
*/ | |||||
@RunWith(SpringRunner.class) | |||||
@WebMvcTest(GatewayApiController.class) | |||||
@Import({FakeAuthServiceImpl.class, InMemApiDefinitionStore.class, AppManagement.class, SimpleMachineDiscovery.class}) | |||||
public class GatewayApiControllerTest { | |||||
private static final String TEST_APP = "test_app"; | |||||
private static final String TEST_IP = "localhost"; | |||||
private static final Integer TEST_PORT = 8719; | |||||
@Autowired | |||||
private MockMvc mockMvc; | |||||
@Autowired | |||||
private InMemApiDefinitionStore repository; | |||||
@MockBean | |||||
private SentinelApiClient sentinelApiClient; | |||||
@Before | |||||
public void before() { | |||||
repository.clearAll(); | |||||
} | |||||
@Test | |||||
public void testQueryApis() throws Exception { | |||||
String path = "/gateway/api/list.json"; | |||||
List<ApiDefinitionEntity> entities = new ArrayList<>(); | |||||
// Mock two entities | |||||
ApiDefinitionEntity entity = new ApiDefinitionEntity(); | |||||
entity.setId(1L); | |||||
entity.setApp(TEST_APP); | |||||
entity.setIp(TEST_IP); | |||||
entity.setPort(TEST_PORT); | |||||
entity.setApiName("foo"); | |||||
Date date = new Date(); | |||||
entity.setGmtCreate(date); | |||||
entity.setGmtModified(date); | |||||
Set<ApiPredicateItemEntity> itemEntities = new LinkedHashSet<>(); | |||||
entity.setPredicateItems(itemEntities); | |||||
ApiPredicateItemEntity itemEntity = new ApiPredicateItemEntity(); | |||||
itemEntity.setPattern("/aaa"); | |||||
itemEntity.setMatchStrategy(URL_MATCH_STRATEGY_EXACT); | |||||
itemEntities.add(itemEntity); | |||||
entities.add(entity); | |||||
ApiDefinitionEntity entity2 = new ApiDefinitionEntity(); | |||||
entity2.setId(2L); | |||||
entity2.setApp(TEST_APP); | |||||
entity2.setIp(TEST_IP); | |||||
entity2.setPort(TEST_PORT); | |||||
entity2.setApiName("biz"); | |||||
entity.setGmtCreate(date); | |||||
entity.setGmtModified(date); | |||||
Set<ApiPredicateItemEntity> itemEntities2 = new LinkedHashSet<>(); | |||||
entity2.setPredicateItems(itemEntities2); | |||||
ApiPredicateItemEntity itemEntity2 = new ApiPredicateItemEntity(); | |||||
itemEntity2.setPattern("/bbb"); | |||||
itemEntity2.setMatchStrategy(URL_MATCH_STRATEGY_PREFIX); | |||||
itemEntities2.add(itemEntity2); | |||||
entities.add(entity2); | |||||
CompletableFuture<List<ApiDefinitionEntity>> completableFuture = mock(CompletableFuture.class); | |||||
given(completableFuture.get()).willReturn(entities); | |||||
given(sentinelApiClient.fetchApis(TEST_APP, TEST_IP, TEST_PORT)).willReturn(completableFuture); | |||||
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(path); | |||||
requestBuilder.param("app", TEST_APP); | |||||
requestBuilder.param("ip", TEST_IP); | |||||
requestBuilder.param("port", String.valueOf(TEST_PORT)); | |||||
// Do controller logic | |||||
MvcResult mvcResult = mockMvc.perform(requestBuilder) | |||||
.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn(); | |||||
// Verify the fetchApis method has been called | |||||
verify(sentinelApiClient).fetchApis(TEST_APP, TEST_IP, TEST_PORT); | |||||
// Verify if two same entities are got | |||||
Result<List<ApiDefinitionEntity>> result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference<Result<List<ApiDefinitionEntity>>>(){}); | |||||
assertTrue(result.isSuccess()); | |||||
List<ApiDefinitionEntity> data = result.getData(); | |||||
assertEquals(2, data.size()); | |||||
assertEquals(entities, data); | |||||
// Verify the entities are add into memory repository | |||||
List<ApiDefinitionEntity> entitiesInMem = repository.findAllByApp(TEST_APP); | |||||
assertEquals(2, entitiesInMem.size()); | |||||
assertEquals(entities, entitiesInMem); | |||||
} | |||||
@Test | |||||
public void testAddApi() throws Exception { | |||||
String path = "/gateway/api/new.json"; | |||||
AddApiReqVo reqVo = new AddApiReqVo(); | |||||
reqVo.setApp(TEST_APP); | |||||
reqVo.setIp(TEST_IP); | |||||
reqVo.setPort(TEST_PORT); | |||||
reqVo.setApiName("customized_api"); | |||||
List<ApiPredicateItemVo> itemVos = new ArrayList<>(); | |||||
ApiPredicateItemVo itemVo = new ApiPredicateItemVo(); | |||||
itemVo.setMatchStrategy(URL_MATCH_STRATEGY_EXACT); | |||||
itemVo.setPattern("/product"); | |||||
itemVos.add(itemVo); | |||||
reqVo.setPredicateItems(itemVos); | |||||
given(sentinelApiClient.modifyApis(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any())).willReturn(true); | |||||
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(path); | |||||
requestBuilder.content(JSON.toJSONString(reqVo)).contentType(MediaType.APPLICATION_JSON); | |||||
// Do controller logic | |||||
MvcResult mvcResult = mockMvc.perform(requestBuilder) | |||||
.andExpect(MockMvcResultMatchers.status().isOk()) | |||||
.andDo(MockMvcResultHandlers.print()).andReturn(); | |||||
// Verify the modifyApis method has been called | |||||
verify(sentinelApiClient).modifyApis(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any()); | |||||
Result<ApiDefinitionEntity> result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference<Result<ApiDefinitionEntity>>() {}); | |||||
assertTrue(result.isSuccess()); | |||||
// Verify the result | |||||
ApiDefinitionEntity entity = result.getData(); | |||||
assertNotNull(entity); | |||||
assertEquals(TEST_APP, entity.getApp()); | |||||
assertEquals(TEST_IP, entity.getIp()); | |||||
assertEquals(TEST_PORT, entity.getPort()); | |||||
assertEquals("customized_api", entity.getApiName()); | |||||
assertNotNull(entity.getId()); | |||||
assertNotNull(entity.getGmtCreate()); | |||||
assertNotNull(entity.getGmtModified()); | |||||
Set<ApiPredicateItemEntity> predicateItemEntities = entity.getPredicateItems(); | |||||
assertEquals(1, predicateItemEntities.size()); | |||||
ApiPredicateItemEntity predicateItemEntity = predicateItemEntities.iterator().next(); | |||||
assertEquals(URL_MATCH_STRATEGY_EXACT, predicateItemEntity.getMatchStrategy().intValue()); | |||||
assertEquals("/product", predicateItemEntity.getPattern()); | |||||
// Verify the entity which is add in memory repository | |||||
List<ApiDefinitionEntity> entitiesInMem = repository.findAllByApp(TEST_APP); | |||||
assertEquals(1, entitiesInMem.size()); | |||||
assertEquals(entity, entitiesInMem.get(0)); | |||||
} | |||||
@Test | |||||
public void testUpdateApi() throws Exception { | |||||
String path = "/gateway/api/save.json"; | |||||
// Add one entity to memory repository for update | |||||
ApiDefinitionEntity addEntity = new ApiDefinitionEntity(); | |||||
addEntity.setApp(TEST_APP); | |||||
addEntity.setIp(TEST_IP); | |||||
addEntity.setPort(TEST_PORT); | |||||
addEntity.setApiName("bbb"); | |||||
Date date = new Date(); | |||||
// To make the gmtModified different when do update | |||||
date = DateUtils.addSeconds(date, -1); | |||||
addEntity.setGmtCreate(date); | |||||
addEntity.setGmtModified(date); | |||||
Set<ApiPredicateItemEntity> addRedicateItemEntities = new HashSet<>(); | |||||
addEntity.setPredicateItems(addRedicateItemEntities); | |||||
ApiPredicateItemEntity addPredicateItemEntity = new ApiPredicateItemEntity(); | |||||
addPredicateItemEntity.setMatchStrategy(URL_MATCH_STRATEGY_EXACT); | |||||
addPredicateItemEntity.setPattern("/order"); | |||||
addEntity = repository.save(addEntity); | |||||
UpdateApiReqVo reqVo = new UpdateApiReqVo(); | |||||
reqVo.setId(addEntity.getId()); | |||||
reqVo.setApp(TEST_APP); | |||||
List<ApiPredicateItemVo> itemVos = new ArrayList<>(); | |||||
ApiPredicateItemVo itemVo = new ApiPredicateItemVo(); | |||||
itemVo.setMatchStrategy(URL_MATCH_STRATEGY_PREFIX); | |||||
itemVo.setPattern("/my_order"); | |||||
itemVos.add(itemVo); | |||||
reqVo.setPredicateItems(itemVos); | |||||
given(sentinelApiClient.modifyApis(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any())).willReturn(true); | |||||
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(path); | |||||
requestBuilder.content(JSON.toJSONString(reqVo)).contentType(MediaType.APPLICATION_JSON); | |||||
// Do controller logic | |||||
MvcResult mvcResult = mockMvc.perform(requestBuilder) | |||||
.andExpect(MockMvcResultMatchers.status().isOk()) | |||||
.andDo(MockMvcResultHandlers.print()).andReturn(); | |||||
// Verify the modifyApis method has been called | |||||
verify(sentinelApiClient).modifyApis(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any()); | |||||
Result<ApiDefinitionEntity> result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference<Result<ApiDefinitionEntity>>() {}); | |||||
assertTrue(result.isSuccess()); | |||||
ApiDefinitionEntity entity = result.getData(); | |||||
assertNotNull(entity); | |||||
assertEquals("bbb", entity.getApiName()); | |||||
assertEquals(date, entity.getGmtCreate()); | |||||
// To make sure gmtModified has been set and it's different from gmtCreate | |||||
assertNotNull(entity.getGmtModified()); | |||||
assertNotEquals(entity.getGmtCreate(), entity.getGmtModified()); | |||||
Set<ApiPredicateItemEntity> predicateItemEntities = entity.getPredicateItems(); | |||||
assertEquals(1, predicateItemEntities.size()); | |||||
ApiPredicateItemEntity predicateItemEntity = predicateItemEntities.iterator().next(); | |||||
assertEquals(URL_MATCH_STRATEGY_PREFIX, predicateItemEntity.getMatchStrategy().intValue()); | |||||
assertEquals("/my_order", predicateItemEntity.getPattern()); | |||||
// Verify the entity which is update in memory repository | |||||
List<ApiDefinitionEntity> entitiesInMem = repository.findAllByApp(TEST_APP); | |||||
assertEquals(1, entitiesInMem.size()); | |||||
assertEquals(entity, entitiesInMem.get(0)); | |||||
} | |||||
@Test | |||||
public void testDeleteApi() throws Exception { | |||||
String path = "/gateway/api/delete.json"; | |||||
// Add one entity into memory repository for delete | |||||
ApiDefinitionEntity addEntity = new ApiDefinitionEntity(); | |||||
addEntity.setApp(TEST_APP); | |||||
addEntity.setIp(TEST_IP); | |||||
addEntity.setPort(TEST_PORT); | |||||
addEntity.setApiName("ccc"); | |||||
Date date = new Date(); | |||||
addEntity.setGmtCreate(date); | |||||
addEntity.setGmtModified(date); | |||||
Set<ApiPredicateItemEntity> addRedicateItemEntities = new HashSet<>(); | |||||
addEntity.setPredicateItems(addRedicateItemEntities); | |||||
ApiPredicateItemEntity addPredicateItemEntity = new ApiPredicateItemEntity(); | |||||
addPredicateItemEntity.setMatchStrategy(URL_MATCH_STRATEGY_EXACT); | |||||
addPredicateItemEntity.setPattern("/user/add"); | |||||
addEntity = repository.save(addEntity); | |||||
given(sentinelApiClient.modifyApis(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any())).willReturn(true); | |||||
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(path); | |||||
requestBuilder.param("id", String.valueOf(addEntity.getId())); | |||||
// Do controller logic | |||||
MvcResult mvcResult = mockMvc.perform(requestBuilder) | |||||
.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn(); | |||||
// Verify the modifyApis method has been called | |||||
verify(sentinelApiClient).modifyApis(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any()); | |||||
// Verify the result | |||||
Result<Long> result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference<Result<Long>>() {}); | |||||
assertTrue(result.isSuccess()); | |||||
assertEquals(addEntity.getId(), result.getData()); | |||||
// Now no entities in memory | |||||
List<ApiDefinitionEntity> entitiesInMem = repository.findAllByApp(TEST_APP); | |||||
assertEquals(0, entitiesInMem.size()); | |||||
} | |||||
} |
@@ -0,0 +1,355 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.alibaba.csp.sentinel.dashboard.controller.gateway; | |||||
import com.alibaba.csp.sentinel.dashboard.auth.FakeAuthServiceImpl; | |||||
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; | |||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity; | |||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayParamFlowItemEntity; | |||||
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; | |||||
import com.alibaba.csp.sentinel.dashboard.discovery.SimpleMachineDiscovery; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.Result; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.AddFlowRuleReqVo; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.GatewayParamFlowItemVo; | |||||
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.UpdateFlowRuleReqVo; | |||||
import com.alibaba.csp.sentinel.dashboard.repository.gateway.InMemGatewayFlowRuleStore; | |||||
import com.alibaba.fastjson.JSON; | |||||
import com.alibaba.fastjson.JSONObject; | |||||
import com.alibaba.fastjson.TypeReference; | |||||
import org.apache.commons.lang3.time.DateUtils; | |||||
import org.junit.Before; | |||||
import org.junit.Test; | |||||
import org.junit.runner.RunWith; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; | |||||
import org.springframework.boot.test.mock.mockito.MockBean; | |||||
import org.springframework.context.annotation.Import; | |||||
import org.springframework.http.MediaType; | |||||
import org.springframework.test.context.junit4.SpringRunner; | |||||
import org.springframework.test.web.servlet.MockMvc; | |||||
import org.springframework.test.web.servlet.MvcResult; | |||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; | |||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; | |||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers; | |||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers; | |||||
import java.util.ArrayList; | |||||
import java.util.Date; | |||||
import java.util.List; | |||||
import java.util.concurrent.CompletableFuture; | |||||
import static com.alibaba.csp.sentinel.slots.block.RuleConstant.*; | |||||
import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*; | |||||
import static org.junit.Assert.*; | |||||
import static org.mockito.BDDMockito.*; | |||||
/** | |||||
* Test cases for {@link GatewayFlowRuleController}. | |||||
* | |||||
* @author cdfive | |||||
*/ | |||||
@RunWith(SpringRunner.class) | |||||
@WebMvcTest(GatewayFlowRuleController.class) | |||||
@Import({FakeAuthServiceImpl.class, InMemGatewayFlowRuleStore.class, AppManagement.class, SimpleMachineDiscovery.class}) | |||||
public class GatewayFlowRuleControllerTest { | |||||
private static final String TEST_APP = "test_app"; | |||||
private static final String TEST_IP = "localhost"; | |||||
private static final Integer TEST_PORT = 8719; | |||||
@Autowired | |||||
private MockMvc mockMvc; | |||||
@Autowired | |||||
private InMemGatewayFlowRuleStore repository; | |||||
@MockBean | |||||
private SentinelApiClient sentinelApiClient; | |||||
@Before | |||||
public void before() { | |||||
repository.clearAll(); | |||||
} | |||||
@Test | |||||
public void testQueryFlowRules() throws Exception { | |||||
String path = "/gateway/flow/list.json"; | |||||
List<GatewayFlowRuleEntity> entities = new ArrayList<>(); | |||||
// Mock two entities | |||||
GatewayFlowRuleEntity entity = new GatewayFlowRuleEntity(); | |||||
entity.setId(1L); | |||||
entity.setApp(TEST_APP); | |||||
entity.setIp(TEST_IP); | |||||
entity.setPort(TEST_PORT); | |||||
entity.setResource("httpbin_route"); | |||||
entity.setResourceMode(RESOURCE_MODE_ROUTE_ID); | |||||
entity.setGrade(FLOW_GRADE_QPS); | |||||
entity.setCount(5D); | |||||
entity.setInterval(30L); | |||||
entity.setIntervalUnit(GatewayFlowRuleEntity.INTERVAL_UNIT_SECOND); | |||||
entity.setControlBehavior(CONTROL_BEHAVIOR_DEFAULT); | |||||
entity.setBurst(0); | |||||
entity.setMaxQueueingTimeoutMs(0); | |||||
GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity(); | |||||
entity.setParamItem(itemEntity); | |||||
itemEntity.setParseStrategy(PARAM_PARSE_STRATEGY_CLIENT_IP); | |||||
entities.add(entity); | |||||
GatewayFlowRuleEntity entity2 = new GatewayFlowRuleEntity(); | |||||
entity2.setId(2L); | |||||
entity2.setApp(TEST_APP); | |||||
entity2.setIp(TEST_IP); | |||||
entity2.setPort(TEST_PORT); | |||||
entity2.setResource("some_customized_api"); | |||||
entity2.setResourceMode(RESOURCE_MODE_CUSTOM_API_NAME); | |||||
entity2.setCount(30D); | |||||
entity2.setInterval(2L); | |||||
entity2.setIntervalUnit(GatewayFlowRuleEntity.INTERVAL_UNIT_MINUTE); | |||||
entity2.setControlBehavior(CONTROL_BEHAVIOR_DEFAULT); | |||||
entity2.setBurst(0); | |||||
entity2.setMaxQueueingTimeoutMs(0); | |||||
GatewayParamFlowItemEntity itemEntity2 = new GatewayParamFlowItemEntity(); | |||||
entity2.setParamItem(itemEntity2); | |||||
itemEntity2.setParseStrategy(PARAM_PARSE_STRATEGY_CLIENT_IP); | |||||
entities.add(entity2); | |||||
CompletableFuture<List<GatewayFlowRuleEntity>> completableFuture = mock(CompletableFuture.class); | |||||
given(completableFuture.get()).willReturn(entities); | |||||
given(sentinelApiClient.fetchGatewayFlowRules(TEST_APP, TEST_IP, TEST_PORT)).willReturn(completableFuture); | |||||
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(path); | |||||
requestBuilder.param("app", TEST_APP); | |||||
requestBuilder.param("ip", TEST_IP); | |||||
requestBuilder.param("port", String.valueOf(TEST_PORT)); | |||||
// Do controller logic | |||||
MvcResult mvcResult = mockMvc.perform(requestBuilder) | |||||
.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn(); | |||||
// Verify the fetchGatewayFlowRules method has been called | |||||
verify(sentinelApiClient).fetchGatewayFlowRules(TEST_APP, TEST_IP, TEST_PORT); | |||||
// Verify if two same entities are got | |||||
Result<List<GatewayFlowRuleEntity>> result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference<Result<List<GatewayFlowRuleEntity>>>(){}); | |||||
assertTrue(result.isSuccess()); | |||||
List<GatewayFlowRuleEntity> data = result.getData(); | |||||
assertEquals(2, data.size()); | |||||
assertEquals(entities, data); | |||||
// Verify the entities are add into memory repository | |||||
List<GatewayFlowRuleEntity> entitiesInMem = repository.findAllByApp(TEST_APP); | |||||
assertEquals(2, entitiesInMem.size()); | |||||
assertEquals(entities, entitiesInMem); | |||||
} | |||||
@Test | |||||
public void testAddFlowRule() throws Exception { | |||||
String path = "/gateway/flow/new.json"; | |||||
AddFlowRuleReqVo reqVo = new AddFlowRuleReqVo(); | |||||
reqVo.setApp(TEST_APP); | |||||
reqVo.setIp(TEST_IP); | |||||
reqVo.setPort(TEST_PORT); | |||||
reqVo.setResourceMode(RESOURCE_MODE_ROUTE_ID); | |||||
reqVo.setResource("httpbin_route"); | |||||
reqVo.setGrade(FLOW_GRADE_QPS); | |||||
reqVo.setCount(5D); | |||||
reqVo.setInterval(30L); | |||||
reqVo.setIntervalUnit(GatewayFlowRuleEntity.INTERVAL_UNIT_SECOND); | |||||
reqVo.setControlBehavior(CONTROL_BEHAVIOR_DEFAULT); | |||||
reqVo.setBurst(0); | |||||
reqVo.setMaxQueueingTimeoutMs(0); | |||||
given(sentinelApiClient.modifyGatewayFlowRules(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any())).willReturn(true); | |||||
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(path); | |||||
requestBuilder.content(JSON.toJSONString(reqVo)).contentType(MediaType.APPLICATION_JSON); | |||||
// Do controller logic | |||||
MvcResult mvcResult = mockMvc.perform(requestBuilder) | |||||
.andExpect(MockMvcResultMatchers.status().isOk()) | |||||
.andDo(MockMvcResultHandlers.print()).andReturn(); | |||||
// Verify the modifyGatewayFlowRules method has been called | |||||
verify(sentinelApiClient).modifyGatewayFlowRules(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any()); | |||||
Result<GatewayFlowRuleEntity> result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference<Result<GatewayFlowRuleEntity>>() {}); | |||||
assertTrue(result.isSuccess()); | |||||
// Verify the result | |||||
GatewayFlowRuleEntity entity = result.getData(); | |||||
assertNotNull(entity); | |||||
assertEquals(TEST_APP, entity.getApp()); | |||||
assertEquals(TEST_IP, entity.getIp()); | |||||
assertEquals(TEST_PORT, entity.getPort()); | |||||
assertEquals(RESOURCE_MODE_ROUTE_ID, entity.getResourceMode().intValue()); | |||||
assertEquals("httpbin_route", entity.getResource()); | |||||
assertNotNull(entity.getId()); | |||||
assertNotNull(entity.getGmtCreate()); | |||||
assertNotNull(entity.getGmtModified()); | |||||
// Verify the entity which is add in memory repository | |||||
List<GatewayFlowRuleEntity> entitiesInMem = repository.findAllByApp(TEST_APP); | |||||
assertEquals(1, entitiesInMem.size()); | |||||
assertEquals(entity, entitiesInMem.get(0)); | |||||
} | |||||
@Test | |||||
public void testUpdateFlowRule() throws Exception { | |||||
String path = "/gateway/flow/save.json"; | |||||
// Add one entity into memory repository for update | |||||
GatewayFlowRuleEntity addEntity = new GatewayFlowRuleEntity(); | |||||
addEntity.setId(1L); | |||||
addEntity.setApp(TEST_APP); | |||||
addEntity.setIp(TEST_IP); | |||||
addEntity.setPort(TEST_PORT); | |||||
addEntity.setResource("httpbin_route"); | |||||
addEntity.setResourceMode(RESOURCE_MODE_ROUTE_ID); | |||||
addEntity.setGrade(FLOW_GRADE_QPS); | |||||
addEntity.setCount(5D); | |||||
addEntity.setInterval(30L); | |||||
addEntity.setIntervalUnit(GatewayFlowRuleEntity.INTERVAL_UNIT_SECOND); | |||||
addEntity.setControlBehavior(CONTROL_BEHAVIOR_DEFAULT); | |||||
addEntity.setBurst(0); | |||||
addEntity.setMaxQueueingTimeoutMs(0); | |||||
Date date = new Date(); | |||||
// To make the gmtModified different when do update | |||||
date = DateUtils.addSeconds(date, -1); | |||||
addEntity.setGmtCreate(date); | |||||
addEntity.setGmtModified(date); | |||||
GatewayParamFlowItemEntity addItemEntity = new GatewayParamFlowItemEntity(); | |||||
addEntity.setParamItem(addItemEntity); | |||||
addItemEntity.setParseStrategy(PARAM_PARSE_STRATEGY_CLIENT_IP); | |||||
repository.save(addEntity); | |||||
UpdateFlowRuleReqVo reqVo = new UpdateFlowRuleReqVo(); | |||||
reqVo.setId(addEntity.getId()); | |||||
reqVo.setApp(TEST_APP); | |||||
reqVo.setGrade(FLOW_GRADE_QPS); | |||||
reqVo.setCount(6D); | |||||
reqVo.setInterval(2L); | |||||
reqVo.setIntervalUnit(GatewayFlowRuleEntity.INTERVAL_UNIT_MINUTE); | |||||
reqVo.setControlBehavior(CONTROL_BEHAVIOR_RATE_LIMITER); | |||||
reqVo.setMaxQueueingTimeoutMs(500); | |||||
GatewayParamFlowItemVo itemVo = new GatewayParamFlowItemVo(); | |||||
reqVo.setParamItem(itemVo); | |||||
itemVo.setParseStrategy(PARAM_PARSE_STRATEGY_URL_PARAM); | |||||
itemVo.setFieldName("pa"); | |||||
given(sentinelApiClient.modifyGatewayFlowRules(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any())).willReturn(true); | |||||
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(path); | |||||
requestBuilder.content(JSON.toJSONString(reqVo)).contentType(MediaType.APPLICATION_JSON); | |||||
// Do controller logic | |||||
MvcResult mvcResult = mockMvc.perform(requestBuilder) | |||||
.andExpect(MockMvcResultMatchers.status().isOk()) | |||||
.andDo(MockMvcResultHandlers.print()).andReturn(); | |||||
// Verify the modifyGatewayFlowRules method has been called | |||||
verify(sentinelApiClient).modifyGatewayFlowRules(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any()); | |||||
Result<GatewayFlowRuleEntity> result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference<Result<GatewayFlowRuleEntity>>() { | |||||
}); | |||||
assertTrue(result.isSuccess()); | |||||
GatewayFlowRuleEntity entity = result.getData(); | |||||
assertNotNull(entity); | |||||
assertEquals(RESOURCE_MODE_ROUTE_ID, entity.getResourceMode().intValue()); | |||||
assertEquals("httpbin_route", entity.getResource()); | |||||
assertEquals(6D, entity.getCount().doubleValue(), 0); | |||||
assertEquals(2L, entity.getInterval().longValue()); | |||||
assertEquals(GatewayFlowRuleEntity.INTERVAL_UNIT_MINUTE, entity.getIntervalUnit().intValue()); | |||||
assertEquals(CONTROL_BEHAVIOR_RATE_LIMITER, entity.getControlBehavior().intValue()); | |||||
assertEquals(0, entity.getBurst().intValue()); | |||||
assertEquals(500, entity.getMaxQueueingTimeoutMs().intValue()); | |||||
assertEquals(date, entity.getGmtCreate()); | |||||
// To make sure gmtModified has been set and it's different from gmtCreate | |||||
assertNotNull(entity.getGmtModified()); | |||||
assertNotEquals(entity.getGmtCreate(), entity.getGmtModified()); | |||||
// Verify the entity which is update in memory repository | |||||
GatewayParamFlowItemEntity itemEntity = entity.getParamItem(); | |||||
assertEquals(PARAM_PARSE_STRATEGY_URL_PARAM, itemEntity.getParseStrategy().intValue()); | |||||
assertEquals("pa", itemEntity.getFieldName()); | |||||
} | |||||
@Test | |||||
public void testDeleteFlowRule() throws Exception { | |||||
String path = "/gateway/flow/delete.json"; | |||||
// Add one entity into memory repository for delete | |||||
GatewayFlowRuleEntity addEntity = new GatewayFlowRuleEntity(); | |||||
addEntity.setId(1L); | |||||
addEntity.setApp(TEST_APP); | |||||
addEntity.setIp(TEST_IP); | |||||
addEntity.setPort(TEST_PORT); | |||||
addEntity.setResource("httpbin_route"); | |||||
addEntity.setResourceMode(RESOURCE_MODE_ROUTE_ID); | |||||
addEntity.setGrade(FLOW_GRADE_QPS); | |||||
addEntity.setCount(5D); | |||||
addEntity.setInterval(30L); | |||||
addEntity.setIntervalUnit(GatewayFlowRuleEntity.INTERVAL_UNIT_SECOND); | |||||
addEntity.setControlBehavior(CONTROL_BEHAVIOR_DEFAULT); | |||||
addEntity.setBurst(0); | |||||
addEntity.setMaxQueueingTimeoutMs(0); | |||||
Date date = new Date(); | |||||
date = DateUtils.addSeconds(date, -1); | |||||
addEntity.setGmtCreate(date); | |||||
addEntity.setGmtModified(date); | |||||
GatewayParamFlowItemEntity addItemEntity = new GatewayParamFlowItemEntity(); | |||||
addEntity.setParamItem(addItemEntity); | |||||
addItemEntity.setParseStrategy(PARAM_PARSE_STRATEGY_CLIENT_IP); | |||||
repository.save(addEntity); | |||||
given(sentinelApiClient.modifyGatewayFlowRules(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any())).willReturn(true); | |||||
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(path); | |||||
requestBuilder.param("id", String.valueOf(addEntity.getId())); | |||||
// Do controller logic | |||||
MvcResult mvcResult = mockMvc.perform(requestBuilder) | |||||
.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn(); | |||||
// Verify the modifyGatewayFlowRules method has been called | |||||
verify(sentinelApiClient).modifyGatewayFlowRules(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any()); | |||||
// Verify the result | |||||
Result<Long> result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference<Result<Long>>() {}); | |||||
assertTrue(result.isSuccess()); | |||||
assertEquals(addEntity.getId(), result.getData()); | |||||
// Now no entities in memory | |||||
List<GatewayFlowRuleEntity> entitiesInMem = repository.findAllByApp(TEST_APP); | |||||
assertEquals(0, entitiesInMem.size()); | |||||
} | |||||
} |