This is an automated email from the ASF dual-hosted git repository.
xiaoyu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new ba6d30a [ISSUE #2956] valid plugin api paramater (#3036)
ba6d30a is described below
commit ba6d30a7ef8484fe56af8144124ce520334fc78d
Author: likeguo <[email protected]>
AuthorDate: Mon Mar 14 13:14:08 2022 +0800
[ISSUE #2956] valid plugin api paramater (#3036)
* valid plugiin api paramater
* valid plugiin api paramater
---
.../http/http-debug-platform-controller-api.http | 36 ++++++++
.../src/http/http-debug-plugin-controller-api.http | 99 ++++++++++++++++++++++
.../shenyu/admin/controller/PluginController.java | 45 ++++++----
.../apache/shenyu/admin/mapper/PluginMapper.java | 56 ++++++++----
.../apache/shenyu/admin/model/dto/PluginDTO.java | 50 ++++++-----
.../admin/service/impl/PluginServiceImpl.java | 25 ------
.../admin/service/provider/PluginNameProvider.java | 42 +++++++++
.../admin/validation/annotation/Existed.java | 7 ++
.../validation/validator/ExistedValidator.java | 4 +
.../src/main/resources/mappers/plugin-sqlmap.xml | 8 ++
.../admin/controller/MetaDataControllerTest.java | 1 -
.../admin/controller/PluginControllerTest.java | 86 ++++++++++++-------
.../shenyu/admin/service/PluginServiceTest.java | 40 ++-------
13 files changed, 352 insertions(+), 147 deletions(-)
diff --git a/shenyu-admin/src/http/http-debug-platform-controller-api.http
b/shenyu-admin/src/http/http-debug-platform-controller-api.http
new file mode 100644
index 0000000..6a55c18
--- /dev/null
+++ b/shenyu-admin/src/http/http-debug-platform-controller-api.http
@@ -0,0 +1,36 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+# if you debug api, replace your own token
+
+### login
+GET http://localhost:9095/platform/login?userName=admin&password=123456
+Accept: application/json
+Content-Type: application/json
+X-Access-Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ3MDA1ODUxfQ.PQKajtOBgL8_it1Ctylk1-TdBTxFv_fneX1ZPWY4DeE
+
+### enum
+GET http://localhost:9095/platform/enum
+Accept: application/json
+Content-Type: application/json
+X-Access-Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ3MjUzNzg2fQ.37fddsUZRFVNc2pTlACHEI9oZSj9gnE5hhpK5Yaf-6s
+
+### getUserPermissionByToken
+GET
http://localhost:9095/permission/getUserPermissionByToken?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ3MjUzNzg2fQ.37fddsUZRFVNc2pTlACHEI9oZSj9gnE5hhpK5Yaf-6s
+Accept: application/json
+Content-Type: application/json
+X-Access-Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ3MjUzNzg2fQ.37fddsUZRFVNc2pTlACHEI9oZSj9gnE5hhpK5Yaf-6s
diff --git a/shenyu-admin/src/http/http-debug-plugin-controller-api.http
b/shenyu-admin/src/http/http-debug-plugin-controller-api.http
new file mode 100644
index 0000000..0dcfe8c
--- /dev/null
+++ b/shenyu-admin/src/http/http-debug-plugin-controller-api.http
@@ -0,0 +1,99 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+# if you debug api, replace your own token
+
+### plugin list by page
+GET http://localhost:9095/plugin?currentPage=1&pageSize=10
+Accept: application/json
+Content-Type: application/json
+X-Access-Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ3MjUzNzg2fQ.37fddsUZRFVNc2pTlACHEI9oZSj9gnE5hhpK5Yaf-6s
+
+### plugin list
+GET http://localhost:9095/plugin/all
+Accept: application/json
+Content-Type: application/json
+X-Access-Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ3MjUzNzg2fQ.37fddsUZRFVNc2pTlACHEI9oZSj9gnE5hhpK5Yaf-6s
+
+### plugin get
+GET http://localhost:9095/plugin/100
+Accept: application/json
+Content-Type: application/json
+X-Access-Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ3MjUzNzg2fQ.37fddsUZRFVNc2pTlACHEI9oZSj9gnE5hhpK5Yaf-6s
+
+### create
+POST http://localhost:9095/plugin
+Accept: application/json
+Content-Type: application/json
+X-Access-Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ3MjUzNzg2fQ.37fddsUZRFVNc2pTlACHEI9oZSj9gnE5hhpK5Yaf-6s
+
+{
+
+ "name": "test-create-plugin",
+ "role": "test-create-plugin",
+ "enabled": true,
+ "sort": 100
+}
+
+### update
+PUT http://localhost:9095/plugin/1503021118890123264
+Accept: application/json
+Content-Type: application/json
+X-Access-Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ3MjUzNzg2fQ.37fddsUZRFVNc2pTlACHEI9oZSj9gnE5hhpK5Yaf-6s
+
+{
+ "name": "test-create-plugin",
+ "role": "test-create-plugin",
+ "enabled": true,
+ "sort": 100
+}
+
+### delete
+DELETE http://localhost:9095/plugin/batch
+Accept: application/json
+Content-Type: application/json
+X-Access-Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ3MjUzNzg2fQ.37fddsUZRFVNc2pTlACHEI9oZSj9gnE5hhpK5Yaf-6s
+
+[
+ "1503021118890123264"
+]
+
+### enabled
+POST http://localhost:9095/plugin/enabled
+Accept: application/json
+Content-Type: application/json
+X-Access-Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ3MjUzNzg2fQ.37fddsUZRFVNc2pTlACHEI9oZSj9gnE5hhpK5Yaf-6s
+
+{
+ "id": [
+ "1503021118890123264"
+ ],
+ "enable": true
+}
+
+### sync plugin data
+POST http://localhost:9095/plugin/syncPluginAll
+Accept: application/json
+Content-Type: application/json
+X-Access-Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ3MjUzNzg2fQ.37fddsUZRFVNc2pTlACHEI9oZSj9gnE5hhpK5Yaf-6s
+
+### sync plugin data
+PUT http://localhost:9095/plugin/syncPluginData/1
+Accept: application/json
+Content-Type: application/json
+X-Access-Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ3MjUzNzg2fQ.37fddsUZRFVNc2pTlACHEI9oZSj9gnE5hhpK5Yaf-6s
+
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/PluginController.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/PluginController.java
index cecc105..2955bf5 100644
---
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/PluginController.java
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/PluginController.java
@@ -18,6 +18,7 @@
package org.apache.shenyu.admin.controller;
import org.apache.commons.lang3.StringUtils;
+import org.apache.shenyu.admin.mapper.PluginMapper;
import org.apache.shenyu.admin.model.dto.BatchCommonDTO;
import org.apache.shenyu.admin.model.dto.PluginDTO;
import org.apache.shenyu.admin.model.page.CommonPager;
@@ -28,6 +29,7 @@ import org.apache.shenyu.admin.model.vo.PluginVO;
import org.apache.shenyu.admin.service.PluginService;
import org.apache.shenyu.admin.service.SyncDataService;
import org.apache.shenyu.admin.utils.ShenyuResultMessage;
+import org.apache.shenyu.admin.validation.annotation.Existed;
import org.apache.shenyu.common.dto.PluginData;
import org.apache.shenyu.common.enums.DataEventTypeEnum;
import org.springframework.validation.annotation.Validated;
@@ -43,6 +45,7 @@ import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
import java.util.List;
/**
@@ -52,16 +55,16 @@ import java.util.List;
@RestController
@RequestMapping("/plugin")
public class PluginController {
-
+
private final PluginService pluginService;
-
+
private final SyncDataService syncDataService;
-
+
public PluginController(final PluginService pluginService, final
SyncDataService syncDataService) {
this.pluginService = pluginService;
this.syncDataService = syncDataService;
}
-
+
/**
* query plugins.
*
@@ -72,13 +75,16 @@ public class PluginController {
* @return {@linkplain ShenyuAdminResult}
*/
@GetMapping("")
- public ShenyuAdminResult queryPlugins(final String name, final Integer
enabled, final Integer currentPage, final Integer pageSize) {
+ public ShenyuAdminResult queryPlugins(final String name, final Integer
enabled,
+ @NotNull final Integer currentPage,
+ @NotNull final Integer pageSize) {
CommonPager<PluginVO> commonPager = pluginService.listByPage(new
PluginQuery(name, enabled, new PageParameter(currentPage, pageSize)));
return ShenyuAdminResult.success(ShenyuResultMessage.QUERY_SUCCESS,
commonPager);
}
-
+
/**
* query All plugins.
+ *
* @return {@linkplain ShenyuAdminResult}
*/
@GetMapping("/all")
@@ -86,7 +92,7 @@ public class PluginController {
List<PluginData> pluginDataList = pluginService.listAll();
return ShenyuAdminResult.success(ShenyuResultMessage.QUERY_SUCCESS,
pluginDataList);
}
-
+
/**
* detail plugin.
*
@@ -94,11 +100,13 @@ public class PluginController {
* @return {@linkplain ShenyuAdminResult}
*/
@GetMapping("/{id}")
- public ShenyuAdminResult detailPlugin(@PathVariable("id") final String id)
{
+ public ShenyuAdminResult detailPlugin(@PathVariable("id")
+ @Existed(message = "plugin is not
existed",
+ provider =
PluginMapper.class) final String id) {
PluginVO pluginVO = pluginService.findById(id);
return ShenyuAdminResult.success(ShenyuResultMessage.DETAIL_SUCCESS,
pluginVO);
}
-
+
/**
* create plugin.
*
@@ -113,7 +121,7 @@ public class PluginController {
}
return ShenyuAdminResult.success(ShenyuResultMessage.CREATE_SUCCESS);
}
-
+
/**
* update plugin.
*
@@ -122,7 +130,10 @@ public class PluginController {
* @return {@linkplain ShenyuAdminResult}
*/
@PutMapping("/{id}")
- public ShenyuAdminResult updatePlugin(@PathVariable("id") final String id,
@Valid @RequestBody final PluginDTO pluginDTO) {
+ public ShenyuAdminResult updatePlugin(@PathVariable("id")
+ @Existed(message = "plugin is not
existed",
+ provider =
PluginMapper.class) final String id,
+ @Valid @RequestBody final PluginDTO
pluginDTO) {
pluginDTO.setId(id);
final String result = pluginService.createOrUpdate(pluginDTO);
if (StringUtils.isNoneBlank(result)) {
@@ -130,7 +141,7 @@ public class PluginController {
}
return ShenyuAdminResult.success(ShenyuResultMessage.UPDATE_SUCCESS);
}
-
+
/**
* delete plugins.
*
@@ -145,7 +156,7 @@ public class PluginController {
}
return ShenyuAdminResult.success(ShenyuResultMessage.DELETE_SUCCESS);
}
-
+
/**
* Enable plugins.
*
@@ -160,7 +171,7 @@ public class PluginController {
}
return ShenyuAdminResult.success(ShenyuResultMessage.ENABLE_SUCCESS);
}
-
+
/**
* sync plugins.
*
@@ -175,7 +186,7 @@ public class PluginController {
return ShenyuAdminResult.error(ShenyuResultMessage.SYNC_FAIL);
}
}
-
+
/**
* Sync plugin data.
*
@@ -183,7 +194,9 @@ public class PluginController {
* @return the mono
*/
@PutMapping("/syncPluginData/{id}")
- public ShenyuAdminResult syncPluginData(@PathVariable("id") final String
id) {
+ public ShenyuAdminResult syncPluginData(@PathVariable("id")
+ @Existed(message = "plugin is not
existed",
+ provider =
PluginMapper.class) final String id) {
boolean success = syncDataService.syncPluginData(id);
if (success) {
return ShenyuAdminResult.success(ShenyuResultMessage.SYNC_SUCCESS);
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/PluginMapper.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/PluginMapper.java
index 7ab4092..aa5891c 100644
---
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/PluginMapper.java
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/PluginMapper.java
@@ -21,7 +21,9 @@ import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.shenyu.admin.model.entity.PluginDO;
import org.apache.shenyu.admin.model.query.PluginQuery;
+import org.apache.shenyu.admin.validation.ExistProvider;
+import java.io.Serializable;
import java.util.List;
import java.util.Set;
@@ -29,8 +31,17 @@ import java.util.Set;
* PluginMapper.
*/
@Mapper
-public interface PluginMapper {
-
+public interface PluginMapper extends ExistProvider {
+
+ /**
+ * existed.
+ *
+ * @param id id
+ * @return existed
+ */
+ @Override
+ Boolean existed(@Param("id") Serializable id);
+
/**
* select plugin by id.
*
@@ -38,7 +49,7 @@ public interface PluginMapper {
* @return {@linkplain PluginDO}
*/
PluginDO selectById(String id);
-
+
/**
* select plugin by id.
*
@@ -46,7 +57,7 @@ public interface PluginMapper {
* @return {@linkplain PluginDO}
*/
List<PluginDO> selectByIds(List<String> ids);
-
+
/**
* Select by name plugin do.
*
@@ -54,7 +65,7 @@ public interface PluginMapper {
* @return the plugin do
*/
PluginDO selectByName(String name);
-
+
/**
* Select by names plugin do.
*
@@ -62,7 +73,7 @@ public interface PluginMapper {
* @return the plugins do
*/
List<PluginDO> selectByNames(List<String> names);
-
+
/**
* select plugin by query.
*
@@ -70,21 +81,21 @@ public interface PluginMapper {
* @return {@linkplain List}
*/
List<PluginDO> selectByQuery(PluginQuery pluginQuery);
-
+
/**
* select all.
*
* @return {@linkplain List}
*/
List<PluginDO> selectAll();
-
+
/**
* select all not in resource.
*
* @return {@linkplain List}
*/
List<PluginDO> listAllNotInResource();
-
+
/**
* count plugin by query.
*
@@ -92,7 +103,7 @@ public interface PluginMapper {
* @return {@linkplain Integer}
*/
Integer countByQuery(PluginQuery pluginQuery);
-
+
/**
* insert plugin.
*
@@ -100,7 +111,7 @@ public interface PluginMapper {
* @return rows int
*/
int insert(PluginDO pluginDO);
-
+
/**
* insert selective plugin.
*
@@ -108,7 +119,7 @@ public interface PluginMapper {
* @return rows int
*/
int insertSelective(PluginDO pluginDO);
-
+
/**
* update plugin.
*
@@ -116,7 +127,7 @@ public interface PluginMapper {
* @return rows int
*/
int update(PluginDO pluginDO);
-
+
/**
* Update enable int.
*
@@ -124,15 +135,16 @@ public interface PluginMapper {
* @return the int
*/
int updateEnable(PluginDO pluginDO);
-
+
/**
* enable data by a set of ids.
- * @param idSet a set of ids
+ *
+ * @param idSet a set of ids
* @param enabled status
* @return the count of enabled datas
*/
int updateEnableByIdSet(@Param("idSet") Set<String> idSet,
@Param("enabled") Boolean enabled);
-
+
/**
* update selective plugin.
*
@@ -140,7 +152,7 @@ public interface PluginMapper {
* @return rows int
*/
int updateSelective(PluginDO pluginDO);
-
+
/**
* delete plugin.
*
@@ -148,7 +160,7 @@ public interface PluginMapper {
* @return rows int
*/
int delete(String id);
-
+
/**
* delete plugin.
*
@@ -156,4 +168,12 @@ public interface PluginMapper {
* @return rows int
*/
int deleteByIds(List<String> ids);
+
+ /**
+ * plugin existed.
+ *
+ * @param name name
+ * @return existed
+ */
+ Boolean nameExisted(@Param("name") Serializable name);
}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/PluginDTO.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/PluginDTO.java
index b69510f..5820416 100644
---
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/PluginDTO.java
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/PluginDTO.java
@@ -17,6 +17,11 @@
package org.apache.shenyu.admin.model.dto;
+import org.apache.shenyu.admin.mapper.PluginMapper;
+import org.apache.shenyu.admin.service.provider.PluginNameProvider;
+import org.apache.shenyu.admin.validation.annotation.Existed;
+import org.apache.shenyu.common.constant.AdminConstants;
+
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Objects;
@@ -25,42 +30,45 @@ import java.util.Objects;
* this is plugin from by web front.
*/
public class PluginDTO implements Serializable {
-
+
private static final long serialVersionUID = 789913506331671329L;
-
+
/**
* primary key.
*/
+ @Existed(provider = PluginMapper.class, nullOfIgnore = true, message =
"the plugin is not exited")
private String id;
-
+
/**
* plugin name.
*/
@NotNull
+ @Existed(provider = PluginNameProvider.class, reverse = true, message =
AdminConstants.PLUGIN_NAME_IS_EXIST)
private String name;
-
+
/**
* plugin config.
*/
private String config;
-
+
/**
* plugin role.
*/
@NotNull
private String role;
-
+
/**
* plugin sort.
*/
+ @NotNull
private Integer sort;
-
+
/**
* whether enabled.
*/
@NotNull
private Boolean enabled;
-
+
/**
* Gets the value of id.
*
@@ -69,7 +77,7 @@ public class PluginDTO implements Serializable {
public String getId() {
return id;
}
-
+
/**
* Sets the id.
*
@@ -78,7 +86,7 @@ public class PluginDTO implements Serializable {
public void setId(final String id) {
this.id = id;
}
-
+
/**
* Gets the value of name.
*
@@ -87,7 +95,7 @@ public class PluginDTO implements Serializable {
public String getName() {
return name;
}
-
+
/**
* Sets the name.
*
@@ -96,7 +104,7 @@ public class PluginDTO implements Serializable {
public void setName(final String name) {
this.name = name;
}
-
+
/**
* Gets the value of config.
*
@@ -105,7 +113,7 @@ public class PluginDTO implements Serializable {
public String getConfig() {
return config;
}
-
+
/**
* Sets the config.
*
@@ -114,7 +122,7 @@ public class PluginDTO implements Serializable {
public void setConfig(final String config) {
this.config = config;
}
-
+
/**
* Gets the value of role.
*
@@ -123,7 +131,7 @@ public class PluginDTO implements Serializable {
public String getRole() {
return role;
}
-
+
/**
* Sets the role.
*
@@ -132,7 +140,7 @@ public class PluginDTO implements Serializable {
public void setRole(final String role) {
this.role = role;
}
-
+
/**
* Gets the value of sort.
*
@@ -141,7 +149,7 @@ public class PluginDTO implements Serializable {
public Integer getSort() {
return sort;
}
-
+
/**
* Sets the sort.
*
@@ -150,7 +158,7 @@ public class PluginDTO implements Serializable {
public void setSort(final Integer sort) {
this.sort = sort;
}
-
+
/**
* Gets the value of enabled.
*
@@ -159,7 +167,7 @@ public class PluginDTO implements Serializable {
public Boolean getEnabled() {
return enabled;
}
-
+
/**
* Sets the enabled.
*
@@ -168,7 +176,7 @@ public class PluginDTO implements Serializable {
public void setEnabled(final Boolean enabled) {
this.enabled = enabled;
}
-
+
@Override
public boolean equals(final Object o) {
if (this == o) {
@@ -185,7 +193,7 @@ public class PluginDTO implements Serializable {
&& Objects.equals(sort, pluginDTO.sort)
&& Objects.equals(enabled, pluginDTO.enabled);
}
-
+
@Override
public int hashCode() {
return Objects.hash(id, name, config, role, sort, enabled);
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/PluginServiceImpl.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/PluginServiceImpl.java
index 6a7cff1..8f56c1c 100644
---
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/PluginServiceImpl.java
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/PluginServiceImpl.java
@@ -109,11 +109,6 @@ public class PluginServiceImpl implements PluginService {
@Override
@Transactional(rollbackFor = Exception.class)
public String createOrUpdate(final PluginDTO pluginDTO) {
- final String msg = checkData(pluginDTO);
- if (StringUtils.isNoneBlank(msg)) {
- return msg;
- }
-
PluginDO pluginDO = PluginDO.buildPluginDO(pluginDTO);
DataEventTypeEnum eventType = DataEventTypeEnum.CREATE;
if (StringUtils.isBlank(pluginDTO.getId())) {
@@ -278,26 +273,6 @@ public class PluginServiceImpl implements PluginService {
}
/**
- * check plugin Data integrity.
- *
- * @param pluginDTO {@linkplain PluginDTO}
- * @return result description
- */
- private String checkData(final PluginDTO pluginDTO) {
- final PluginDO exist = pluginMapper.selectByName(pluginDTO.getName());
- if (StringUtils.isBlank(pluginDTO.getId())) {
- if (Objects.nonNull(exist)) {
- return AdminConstants.PLUGIN_NAME_IS_EXIST;
- }
- } else {
- if (Objects.isNull(exist) ||
!exist.getId().equals(pluginDTO.getId())) {
- return AdminConstants.PLUGIN_NAME_NOT_EXIST;
- }
- }
- return StringUtils.EMPTY;
- }
-
- /**
* add plugin and add plugin resource.
*
* @param pluginDTO {@linkplain PluginDTO}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/provider/PluginNameProvider.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/provider/PluginNameProvider.java
new file mode 100644
index 0000000..192682c
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/provider/PluginNameProvider.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.shenyu.admin.service.provider;
+
+import org.apache.shenyu.admin.mapper.PluginMapper;
+import org.apache.shenyu.admin.validation.ExistProvider;
+import org.springframework.stereotype.Component;
+
+import java.io.Serializable;
+
+/**
+ * PluginNameProvider.
+ */
+@Component
+public class PluginNameProvider implements ExistProvider {
+
+ private final PluginMapper pluginMapper;
+
+ public PluginNameProvider(final PluginMapper pluginMapper) {
+ this.pluginMapper = pluginMapper;
+ }
+
+ @Override
+ public Boolean existed(final Serializable key) {
+ return pluginMapper.nameExisted(key);
+ }
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/annotation/Existed.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/annotation/Existed.java
index 2ea340b..685d02a 100644
---
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/annotation/Existed.java
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/annotation/Existed.java
@@ -46,6 +46,13 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
public @interface Existed {
/**
+ * if null,valid is ignore.
+ *
+ * @return not existed
+ */
+ boolean nullOfIgnore() default false;
+
+ /**
* if reverse ,it is not existed.
*
* @return not existed
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/validator/ExistedValidator.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/validator/ExistedValidator.java
index d9466f7..16d869e 100644
---
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/validator/ExistedValidator.java
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/validation/validator/ExistedValidator.java
@@ -54,6 +54,10 @@ public class ExistedValidator implements
ConstraintValidator<Existed, Serializab
if (Objects.isNull(annotation.provider())) {
throw new ResourceNotFoundException("the validation ExistProvider
is not found");
}
+ if (annotation.nullOfIgnore() && Objects.isNull(value)) {
+ // null of ignore
+ return true;
+ }
if (annotation.reverse()) {
return !Boolean.TRUE.equals(getExistProvider().existed(value));
}
diff --git a/shenyu-admin/src/main/resources/mappers/plugin-sqlmap.xml
b/shenyu-admin/src/main/resources/mappers/plugin-sqlmap.xml
index 0ba78ca..a51388b 100644
--- a/shenyu-admin/src/main/resources/mappers/plugin-sqlmap.xml
+++ b/shenyu-admin/src/main/resources/mappers/plugin-sqlmap.xml
@@ -113,6 +113,14 @@
(select title from resource)
</select>
+ <select id="existed" resultType="java.lang.Boolean">
+ select true from plugin where id = #{id} limit 1
+ </select>
+
+ <select id="nameExisted" resultType="java.lang.Boolean">
+ select true from plugin where name = #{name} limit 1
+ </select>
+
<insert id="insert"
parameterType="org.apache.shenyu.admin.model.entity.PluginDO">
insert into plugin (id, date_created, date_updated,
name,config,role, sort, enabled)
diff --git
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/controller/MetaDataControllerTest.java
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/controller/MetaDataControllerTest.java
index 3de01bc..d0d443b 100644
---
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/controller/MetaDataControllerTest.java
+++
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/controller/MetaDataControllerTest.java
@@ -82,7 +82,6 @@ public final class MetaDataControllerTest {
@BeforeEach
public void setUp() {
this.mockMvc = MockMvcBuilders.standaloneSetup(metaDataController)
-// .setControllerAdvice(pathProvider)
.setControllerAdvice(new ExceptionHandlers())
.build();
}
diff --git
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/controller/PluginControllerTest.java
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/controller/PluginControllerTest.java
index 3eb9d32..4dc35e6 100644
---
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/controller/PluginControllerTest.java
+++
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/controller/PluginControllerTest.java
@@ -19,15 +19,18 @@ package org.apache.shenyu.admin.controller;
import org.apache.commons.lang3.StringUtils;
import org.apache.shenyu.admin.exception.ExceptionHandlers;
+import org.apache.shenyu.admin.mapper.PluginMapper;
import org.apache.shenyu.admin.model.dto.BatchCommonDTO;
import org.apache.shenyu.admin.model.dto.PluginDTO;
import org.apache.shenyu.admin.model.page.CommonPager;
import org.apache.shenyu.admin.model.page.PageParameter;
import org.apache.shenyu.admin.model.query.PluginQuery;
+import org.apache.shenyu.admin.model.vo.PluginVO;
import org.apache.shenyu.admin.service.PluginService;
import org.apache.shenyu.admin.service.SyncDataService;
+import org.apache.shenyu.admin.service.provider.PluginNameProvider;
+import org.apache.shenyu.admin.spring.SpringBeanUtils;
import org.apache.shenyu.admin.utils.ShenyuResultMessage;
-import org.apache.shenyu.admin.model.vo.PluginVO;
import org.apache.shenyu.common.constant.AdminConstants;
import org.apache.shenyu.common.enums.DataEventTypeEnum;
import org.apache.shenyu.common.utils.DateUtils;
@@ -38,6 +41,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@@ -50,6 +54,8 @@ import java.util.List;
import static org.hamcrest.core.Is.is;
import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -58,20 +64,26 @@ import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*/
@ExtendWith(MockitoExtension.class)
public final class PluginControllerTest {
-
+
private MockMvc mockMvc;
-
+
@InjectMocks
private PluginController pluginController;
-
+
@Mock
private PluginService pluginService;
-
+
@Mock
private SyncDataService syncDataService;
-
+
+ @Mock
+ private PluginNameProvider nameProvider;
+
+ @Mock
+ private PluginMapper pluginMapper;
+
private PluginVO pluginVO;
-
+
@BeforeEach
public void setUp() {
this.mockMvc = MockMvcBuilders.standaloneSetup(pluginController)
@@ -79,8 +91,10 @@ public final class PluginControllerTest {
.build();
this.pluginVO = new PluginVO("123", "1", "t_n", "1", 1, true,
DateUtils.localDateTimeToString(LocalDateTime.now()),
DateUtils.localDateTimeToString(LocalDateTime.now()));
- }
+
SpringBeanUtils.getInstance().setApplicationContext(mock(ConfigurableApplicationContext.class));
+ }
+
@Test
public void testQueryPlugins() throws Exception {
final PageParameter pageParameter = new PageParameter();
@@ -101,7 +115,7 @@ public final class PluginControllerTest {
.andExpect(jsonPath("$.data.dataList[0].name",
is(pluginVO.getName())))
.andReturn();
}
-
+
@Test
public void testQueryAllPlugins() throws Exception {
given(this.pluginService.listAll())
@@ -110,7 +124,7 @@ public final class PluginControllerTest {
.andExpect(status().isOk())
.andReturn();
}
-
+
@Test
public void testDetailPlugin() throws Exception {
given(this.pluginService.findById("123")).willReturn(pluginVO);
@@ -120,7 +134,7 @@ public final class PluginControllerTest {
.andExpect(jsonPath("$.data.id", is(pluginVO.getId())))
.andReturn();
}
-
+
@Test
public void testCreatePlugin() throws Exception {
PluginDTO pluginDTO = new PluginDTO();
@@ -128,6 +142,12 @@ public final class PluginControllerTest {
pluginDTO.setName("test");
pluginDTO.setEnabled(true);
pluginDTO.setRole("1");
+ pluginDTO.setSort(100);
+
when(SpringBeanUtils.getInstance().getBean(PluginNameProvider.class)).thenReturn(nameProvider);
+
when(SpringBeanUtils.getInstance().getBean(PluginMapper.class)).thenReturn(pluginMapper);
+
+ when(nameProvider.existed(pluginDTO.getName())).thenReturn(null);
+ when(pluginMapper.existed(pluginDTO.getId())).thenReturn(true);
given(this.pluginService.createOrUpdate(pluginDTO)).willReturn(StringUtils.EMPTY);
this.mockMvc.perform(MockMvcRequestBuilders.post("/plugin/")
.contentType(MediaType.APPLICATION_JSON)
@@ -135,25 +155,24 @@ public final class PluginControllerTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$.message",
is(ShenyuResultMessage.CREATE_SUCCESS)))
.andReturn();
-
-
given(this.pluginService.createOrUpdate(pluginDTO)).willReturn(AdminConstants.PLUGIN_NAME_IS_EXIST);
+ when(nameProvider.existed(pluginDTO.getName())).thenReturn(true);
this.mockMvc.perform(MockMvcRequestBuilders.post("/plugin/")
.contentType(MediaType.APPLICATION_JSON)
.content(GsonUtils.getInstance().toJson(pluginDTO)))
.andExpect(status().isOk())
- .andExpect(jsonPath("$.message",
is(AdminConstants.PLUGIN_NAME_IS_EXIST)))
+ .andExpect(jsonPath("$.message", is("Request error! invalid
argument [name: The plugin name already exists and can't be added
repeatedly!]")))
.andReturn();
-
-
given(this.pluginService.createOrUpdate(pluginDTO)).willReturn(AdminConstants.PLUGIN_NAME_NOT_EXIST);
+ when(nameProvider.existed(pluginDTO.getName())).thenReturn(false);
+ when(pluginMapper.existed(pluginDTO.getId())).thenReturn(false);
this.mockMvc.perform(MockMvcRequestBuilders.post("/plugin/")
.contentType(MediaType.APPLICATION_JSON)
.content(GsonUtils.getInstance().toJson(pluginDTO)))
.andExpect(status().isOk())
- .andExpect(jsonPath("$.message",
is(AdminConstants.PLUGIN_NAME_NOT_EXIST)))
+ .andExpect(jsonPath("$.message", is("Request error! invalid
argument [id: the plugin is not exited]")))
.andReturn();
-
+
}
-
+
@Test
public void testUpdatePlugin() throws Exception {
PluginDTO pluginDTO = new PluginDTO();
@@ -161,6 +180,11 @@ public final class PluginControllerTest {
pluginDTO.setName("test1");
pluginDTO.setEnabled(true);
pluginDTO.setRole("1");
+ pluginDTO.setSort(100);
+
when(SpringBeanUtils.getInstance().getBean(PluginNameProvider.class)).thenReturn(nameProvider);
+
when(SpringBeanUtils.getInstance().getBean(PluginMapper.class)).thenReturn(pluginMapper);
+ when(nameProvider.existed(pluginDTO.getName())).thenReturn(false);
+ when(pluginMapper.existed(pluginDTO.getId())).thenReturn(true);
given(this.pluginService.createOrUpdate(pluginDTO)).willReturn(StringUtils.EMPTY);
this.mockMvc.perform(MockMvcRequestBuilders.put("/plugin/{id}", "123")
.contentType(MediaType.APPLICATION_JSON)
@@ -168,7 +192,7 @@ public final class PluginControllerTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$.message",
is(ShenyuResultMessage.UPDATE_SUCCESS)))
.andReturn();
-
+
given(this.pluginService.createOrUpdate(pluginDTO)).willReturn(AdminConstants.PLUGIN_NAME_IS_EXIST);
this.mockMvc.perform(MockMvcRequestBuilders.put("/plugin/{id}", "123")
.contentType(MediaType.APPLICATION_JSON)
@@ -176,7 +200,7 @@ public final class PluginControllerTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$.message",
is(AdminConstants.PLUGIN_NAME_IS_EXIST)))
.andReturn();
-
+
given(this.pluginService.createOrUpdate(pluginDTO)).willReturn(AdminConstants.PLUGIN_NAME_NOT_EXIST);
this.mockMvc.perform(MockMvcRequestBuilders.put("/plugin/{id}", "123")
.contentType(MediaType.APPLICATION_JSON)
@@ -185,7 +209,7 @@ public final class PluginControllerTest {
.andExpect(jsonPath("$.message",
is(AdminConstants.PLUGIN_NAME_NOT_EXIST)))
.andReturn();
}
-
+
@Test
public void testDeletePlugins() throws Exception {
given(this.pluginService.delete(Collections.singletonList("123"))).willReturn(StringUtils.EMPTY);
@@ -195,7 +219,7 @@ public final class PluginControllerTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$.message",
is(ShenyuResultMessage.DELETE_SUCCESS)))
.andReturn();
-
+
given(this.pluginService.delete(Collections.singletonList("123"))).willReturn(AdminConstants.SYS_PLUGIN_ID_NOT_EXIST);
this.mockMvc.perform(MockMvcRequestBuilders.delete("/plugin/batch")
.contentType(MediaType.APPLICATION_JSON)
@@ -203,7 +227,7 @@ public final class PluginControllerTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$.message",
is(AdminConstants.SYS_PLUGIN_ID_NOT_EXIST)))
.andReturn();
-
+
given(this.pluginService.delete(Collections.singletonList("123"))).willReturn(AdminConstants.SYS_PLUGIN_NOT_DELETE);
this.mockMvc.perform(MockMvcRequestBuilders.delete("/plugin/batch")
.contentType(MediaType.APPLICATION_JSON)
@@ -212,7 +236,7 @@ public final class PluginControllerTest {
.andExpect(jsonPath("$.message",
is(AdminConstants.SYS_PLUGIN_NOT_DELETE)))
.andReturn();
}
-
+
@Test
public void testEnabled() throws Exception {
BatchCommonDTO batchCommonDTO = new BatchCommonDTO();
@@ -225,7 +249,7 @@ public final class PluginControllerTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$.message",
is(ShenyuResultMessage.ENABLE_SUCCESS)))
.andReturn();
-
+
given(this.pluginService.enabled(batchCommonDTO.getIds(),
batchCommonDTO.getEnabled())).willReturn(AdminConstants.SYS_PLUGIN_ID_NOT_EXIST);
this.mockMvc.perform(MockMvcRequestBuilders.post("/plugin/enabled")
.contentType(MediaType.APPLICATION_JSON)
@@ -234,7 +258,7 @@ public final class PluginControllerTest {
.andExpect(jsonPath("$.message",
is(AdminConstants.SYS_PLUGIN_ID_NOT_EXIST)))
.andReturn();
}
-
+
@Test
public void testSyncPluginAll() throws Exception {
given(this.syncDataService.syncAll(DataEventTypeEnum.REFRESH)).willReturn(true);
@@ -242,14 +266,14 @@ public final class PluginControllerTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$.message",
is(ShenyuResultMessage.SYNC_SUCCESS)))
.andReturn();
-
+
given(this.syncDataService.syncAll(DataEventTypeEnum.REFRESH)).willReturn(false);
this.mockMvc.perform(MockMvcRequestBuilders.post("/plugin/syncPluginAll"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.message",
is(ShenyuResultMessage.SYNC_FAIL)))
.andReturn();
}
-
+
@Test
public void testSyncPluginData() throws Exception {
given(this.syncDataService.syncPluginData("123")).willReturn(true);
@@ -257,12 +281,12 @@ public final class PluginControllerTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$.message",
is(ShenyuResultMessage.SYNC_SUCCESS)))
.andReturn();
-
+
given(this.syncDataService.syncPluginData("123")).willReturn(false);
this.mockMvc.perform(MockMvcRequestBuilders.put("/plugin/syncPluginData/{id}",
"123"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.message",
is(ShenyuResultMessage.SYNC_FAIL)))
.andReturn();
}
-
+
}
diff --git
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/PluginServiceTest.java
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/PluginServiceTest.java
index 65ee0f2..95020a5 100644
---
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/PluginServiceTest.java
+++
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/PluginServiceTest.java
@@ -50,9 +50,7 @@ import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
@@ -108,10 +106,6 @@ public final class PluginServiceTest {
publishEvent();
testCreate();
testUpdate();
-
- testCreateShouldPluginNameIsExist();
- testUpdateShouldPluginNameNotExistWithPluginDO();
- testUpdateShouldPluginNameNotExistWithDiffId();
}
@Test
@@ -123,7 +117,7 @@ public final class PluginServiceTest {
final List<SelectorDO> selectorDOList = new ArrayList<>();
selectorDOList.add(SelectorDO.builder().id("101").build());
when(selectorMapper.findByPluginIds(Collections.singletonList("101"))).thenReturn(selectorDOList);
- assertEquals(pluginService.delete(Collections.singletonList("123")),
StringUtils.EMPTY);
+ assertEquals(StringUtils.EMPTY,
pluginService.delete(Collections.singletonList("123")));
}
@Test
@@ -132,14 +126,13 @@ public final class PluginServiceTest {
PluginDO pluginDO = buildPluginDO("123");
final List<String> ids = Collections.singletonList(pluginDO.getId());
- assertEquals(pluginService.delete(ids),
AdminConstants.SYS_PLUGIN_ID_NOT_EXIST);
+ assertEquals(AdminConstants.SYS_PLUGIN_ID_NOT_EXIST,
pluginService.delete(ids));
}
@Test
public void testEnable() {
List<String> idList = Lists.list("123", "1234");
- Set<String> idSet = new HashSet<>(idList);
publishEvent();
BatchCommonDTO batchCommonDTO = new BatchCommonDTO();
batchCommonDTO.setEnabled(false);
@@ -205,7 +198,7 @@ public final class PluginServiceTest {
private void testCreate() {
PluginDTO pluginDTO = buildPluginDTO("");
when(pluginMapper.insert(any())).thenReturn(1);
- assertEquals(this.pluginService.createOrUpdate(pluginDTO),
StringUtils.EMPTY);
+ assertEquals(StringUtils.EMPTY,
this.pluginService.createOrUpdate(pluginDTO));
}
private void testUpdate() {
@@ -216,32 +209,9 @@ public final class PluginServiceTest {
pluginDTO.setId("123");
pluginDTO.setName("test");
when(pluginMapper.update(any())).thenReturn(1);
- assertEquals(this.pluginService.createOrUpdate(pluginDTO),
StringUtils.EMPTY);
- }
-
- private void testCreateShouldPluginNameIsExist() {
- PluginDO pluginDO = buildPluginDO();
- when(pluginMapper.selectByName(any())).thenReturn(pluginDO);
-
- PluginDTO pluginDTO = buildPluginDTO("");
- assertEquals(this.pluginService.createOrUpdate(pluginDTO),
AdminConstants.PLUGIN_NAME_IS_EXIST);
- }
-
- private void testUpdateShouldPluginNameNotExistWithPluginDO() {
- when(pluginMapper.selectByName(any())).thenReturn(null);
-
- PluginDTO pluginDTO = buildPluginDTO("123");
- assertEquals(this.pluginService.createOrUpdate(pluginDTO),
AdminConstants.PLUGIN_NAME_NOT_EXIST);
- }
-
- private void testUpdateShouldPluginNameNotExistWithDiffId() {
- PluginDO pluginDO = buildPluginDO();
- when(pluginMapper.selectByName(any())).thenReturn(pluginDO);
-
- PluginDTO pluginDTO = buildPluginDTO("456");
- assertEquals(this.pluginService.createOrUpdate(pluginDTO),
AdminConstants.PLUGIN_NAME_NOT_EXIST);
+ assertEquals(StringUtils.EMPTY,
this.pluginService.createOrUpdate(pluginDTO));
}
-
+
private PluginDTO buildPluginDTO() {
return buildPluginDTO("123");
}