diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36e0e079d..af8f24a70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,9 @@ on: pull_request: branches: [ develop, v1.x-develop ] +permissions: + contents: read # to fetch code (actions/checkout) + jobs: unix: runs-on: ${{ matrix.os }} diff --git a/.github/workflows/it.yml b/.github/workflows/it.yml index 5a7319c9d..1177bd911 100644 --- a/.github/workflows/it.yml +++ b/.github/workflows/it.yml @@ -9,6 +9,9 @@ on: pull_request: branches: [ develop, v1.x-develop ] +permissions: + contents: read # to fetch code (actions/checkout) + jobs: unix: runs-on: ${{ matrix.os }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ff3ded6e..97273c48d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -172,7 +172,7 @@ ## 0.2.1-release(Sept 28, 2018) -* FIx deregister last instance failed error. +* Fix deregister last instance failed error. * Fix url pattern error. * Fully integrate with and seamlessly support Spring framework, Spring Boot and Spring Cloud * Separate nacos-api from nacos client implementation diff --git a/README.md b/README.md index 269370c9d..263c3f537 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,8 @@ For more details, see [quick-start.](https://nacos.io/en-us/docs/quick-start.htm You can view the full documentation from the [Nacos website](https://nacos.io/en-us/docs/what-is-nacos.html). +You can also read this online eBook from the [NACOS ARCHITECTURE & PRINCIPLES](https://www.yuque.com/nacos/ebook/kbyo6n). + All the latest and long-term notice can also be found here from [Github notice issue](https://github.com/alibaba/nacos/labels/notice). ## Contributing diff --git a/address/src/main/java/com/alibaba/nacos/address/component/AddressServerGeneratorManager.java b/address/src/main/java/com/alibaba/nacos/address/component/AddressServerGeneratorManager.java index 7e7584e60..dc2a5a52a 100644 --- a/address/src/main/java/com/alibaba/nacos/address/component/AddressServerGeneratorManager.java +++ b/address/src/main/java/com/alibaba/nacos/address/component/AddressServerGeneratorManager.java @@ -18,8 +18,8 @@ package com.alibaba.nacos.address.component; import com.alibaba.nacos.address.constant.AddressServerConstants; import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.common.utils.InternetAddressUtil; -import com.alibaba.nacos.naming.core.Instance; import com.alibaba.nacos.common.utils.StringUtils; import org.springframework.stereotype.Component; @@ -76,9 +76,9 @@ public class AddressServerGeneratorManager { instance.setPort(Integer.parseInt(ipAndPort[1])); instance.setClusterName(clusterName); instance.setServiceName(serviceName); - instance.setTenant(Constants.DEFAULT_NAMESPACE_ID); - instance.setApp(rawProductName); instance.setEphemeral(false); + instance.getMetadata().put("app", rawProductName); + instance.getMetadata().put("tenant", Constants.DEFAULT_NAMESPACE_ID); instanceList.add(instance); } @@ -99,7 +99,7 @@ public class AddressServerGeneratorManager { * @param instanceList a instance set will generate string response to client. * @return the result of response to client */ - public String generateResponseIps(List instanceList) { + public String generateResponseIps(List instanceList) { StringBuilder ips = new StringBuilder(); instanceList.forEach(instance -> { diff --git a/address/src/main/java/com/alibaba/nacos/address/controller/AddressServerClusterController.java b/address/src/main/java/com/alibaba/nacos/address/controller/AddressServerClusterController.java index 6ec2425d4..365a72fe7 100644 --- a/address/src/main/java/com/alibaba/nacos/address/controller/AddressServerClusterController.java +++ b/address/src/main/java/com/alibaba/nacos/address/controller/AddressServerClusterController.java @@ -21,13 +21,19 @@ import com.alibaba.nacos.address.component.AddressServerManager; import com.alibaba.nacos.address.constant.AddressServerConstants; import com.alibaba.nacos.address.misc.Loggers; import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; +import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.utils.InternetAddressUtil; -import com.alibaba.nacos.naming.core.Cluster; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.ServiceManager; import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.naming.core.ClusterOperator; +import com.alibaba.nacos.naming.core.InstanceOperator; +import com.alibaba.nacos.naming.core.v2.ServiceManager; +import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; +import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; +import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; +import com.alibaba.nacos.naming.core.v2.pojo.Service; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; @@ -36,6 +42,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; +import java.util.Optional; /** * Address server cluster controller. @@ -47,15 +54,22 @@ import java.util.List; @RequestMapping({AddressServerConstants.ADDRESS_SERVER_REQUEST_URL + "/nodes"}) public class AddressServerClusterController { - private final ServiceManager serviceManager; + private final InstanceOperator instanceOperator; + + private final NamingMetadataManager metadataManager; + + private final ClusterOperator clusterOperator; private final AddressServerManager addressServerManager; private final AddressServerGeneratorManager addressServerGeneratorManager; - public AddressServerClusterController(ServiceManager serviceManager, AddressServerManager addressServerManager, + public AddressServerClusterController(InstanceOperator instanceOperator, NamingMetadataManager metadataManager, + ClusterOperator clusterOperator, AddressServerManager addressServerManager, AddressServerGeneratorManager addressServerGeneratorManager) { - this.serviceManager = serviceManager; + this.instanceOperator = instanceOperator; + this.metadataManager = metadataManager; + this.clusterOperator = clusterOperator; this.addressServerManager = addressServerManager; this.addressServerGeneratorManager = addressServerGeneratorManager; } @@ -86,23 +100,13 @@ public class AddressServerClusterController { try { String serviceName = addressServerGeneratorManager.generateNacosServiceName(productName); - Cluster clusterObj = new Cluster(); - clusterObj.setName(clusterName); - clusterObj.setHealthChecker(new AbstractHealthChecker.None()); - serviceManager.createServiceIfAbsent(Constants.DEFAULT_NAMESPACE_ID, serviceName, false, clusterObj); - String[] ipArray = addressServerManager.splitIps(ips); - String checkResult = InternetAddressUtil.checkIPs(ipArray); - if (InternetAddressUtil.checkOK(checkResult)) { - List instanceList = addressServerGeneratorManager - .generateInstancesByIps(serviceName, rawProductName, clusterName, ipArray); - for (Instance instance : instanceList) { - serviceManager.registerInstance(Constants.DEFAULT_NAMESPACE_ID, serviceName, instance); - } + Result result = registerCluster(serviceName, rawProductName, clusterName, ips); + if (InternetAddressUtil.checkOK(result.getCheckResult())) { responseEntity = ResponseEntity .ok("product=" + rawProductName + ",cluster=" + rawClusterName + "; put success with size=" - + instanceList.size()); + + result.getSize()); } else { - responseEntity = ResponseEntity.status(HttpStatus.BAD_REQUEST).body(checkResult); + responseEntity = ResponseEntity.status(HttpStatus.BAD_REQUEST).body(result.getCheckResult()); } } catch (Exception e) { responseEntity = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage()); @@ -111,6 +115,35 @@ public class AddressServerClusterController { return responseEntity; } + private Result registerCluster(String serviceName, String productName, String clusterName, String ips) + throws NacosException { + String serviceWithoutGroup = NamingUtils.getServiceName(serviceName); + String groupName = NamingUtils.getGroupName(serviceName); + Service service = Service.newService(Constants.DEFAULT_NAMESPACE_ID, groupName, serviceWithoutGroup, false); + service = ServiceManager.getInstance().getSingleton(service); + if (service.isEphemeral()) { + return new Result( + String.format("Service %s is ephemeral service, can't use as address server", serviceName), 0); + } + ServiceMetadata serviceMetadata = metadataManager.getServiceMetadata(service).orElse(new ServiceMetadata()); + if (!serviceMetadata.getClusters().containsKey(clusterName)) { + ClusterMetadata metadata = new ClusterMetadata(); + metadata.setHealthyCheckType(AbstractHealthChecker.None.TYPE); + metadata.setHealthChecker(new AbstractHealthChecker.None()); + clusterOperator.updateClusterMetadata(Constants.DEFAULT_NAMESPACE_ID, serviceName, clusterName, metadata); + } + String[] ipArray = addressServerManager.splitIps(ips); + String checkResult = InternetAddressUtil.checkIPs(ipArray); + if (InternetAddressUtil.checkOK(checkResult)) { + List instanceList = addressServerGeneratorManager + .generateInstancesByIps(serviceName, productName, clusterName, ipArray); + for (Instance instance : instanceList) { + instanceOperator.registerInstance(Constants.DEFAULT_NAMESPACE_ID, serviceName, instance); + } + } + return new Result(checkResult, ipArray.length); + } + /** * Delete cluster. * @@ -134,9 +167,12 @@ public class AddressServerClusterController { try { String serviceName = addressServerGeneratorManager.generateNacosServiceName(productName); - Service service = serviceManager.getService(Constants.DEFAULT_NAMESPACE_ID, serviceName); + String serviceWithoutGroup = NamingUtils.getServiceName(serviceName); + String groupName = NamingUtils.getGroupName(serviceName); + Optional service = com.alibaba.nacos.naming.core.v2.ServiceManager + .getInstance().getSingletonIfExist(Constants.DEFAULT_NAMESPACE_ID, groupName, serviceWithoutGroup); - if (service == null) { + if (!service.isPresent()) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("product=" + rawProductName + " not found."); } if (StringUtils.isBlank(ips)) { @@ -149,8 +185,9 @@ public class AddressServerClusterController { if (InternetAddressUtil.checkOK(checkResult)) { List instanceList = addressServerGeneratorManager .generateInstancesByIps(serviceName, rawProductName, clusterName, ipArray); - serviceManager.removeInstance(Constants.DEFAULT_NAMESPACE_ID, serviceName, false, - instanceList.toArray(new Instance[0])); + for (Instance each : instanceList) { + instanceOperator.removeInstance(Constants.DEFAULT_NAMESPACE_ID, serviceName, each); + } } else { responseEntity = ResponseEntity.status(HttpStatus.BAD_REQUEST).body(checkResult); } @@ -162,4 +199,23 @@ public class AddressServerClusterController { return responseEntity; } + private class Result { + + private final String checkResult; + + private final int size; + + public Result(String checkResult, int size) { + this.checkResult = checkResult; + this.size = size; + } + + public String getCheckResult() { + return checkResult; + } + + public int getSize() { + return size; + } + } } diff --git a/address/src/main/java/com/alibaba/nacos/address/controller/ServerListController.java b/address/src/main/java/com/alibaba/nacos/address/controller/ServerListController.java index 0fc03b0fd..21dd8bbb0 100644 --- a/address/src/main/java/com/alibaba/nacos/address/controller/ServerListController.java +++ b/address/src/main/java/com/alibaba/nacos/address/controller/ServerListController.java @@ -18,9 +18,15 @@ package com.alibaba.nacos.address.controller; import com.alibaba.nacos.address.component.AddressServerGeneratorManager; import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.naming.core.Cluster; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.ServiceManager; +import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.naming.core.v2.ServiceManager; +import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; +import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; +import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; +import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.utils.ServiceUtil; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; @@ -28,6 +34,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; +import java.util.Optional; + /** * Server list controller. * @@ -37,14 +45,17 @@ import org.springframework.web.bind.annotation.RestController; @RestController public class ServerListController { - private final ServiceManager serviceManager; - private final AddressServerGeneratorManager addressServerBuilderManager; - public ServerListController(ServiceManager serviceManager, - AddressServerGeneratorManager addressServerBuilderManager) { - this.serviceManager = serviceManager; + private final NamingMetadataManager metadataManager; + + private final ServiceStorage serviceStorage; + + public ServerListController(AddressServerGeneratorManager addressServerBuilderManager, + NamingMetadataManager metadataManager, ServiceStorage serviceStorage) { this.addressServerBuilderManager = addressServerBuilderManager; + this.metadataManager = metadataManager; + this.serviceStorage = serviceStorage; } /** @@ -59,20 +70,22 @@ public class ServerListController { String productName = addressServerBuilderManager.generateProductName(product); String serviceName = addressServerBuilderManager.generateNacosServiceName(productName); - Service service = serviceManager.getService(Constants.DEFAULT_NAMESPACE_ID, serviceName); - if (service == null) { - + String serviceWithoutGroup = NamingUtils.getServiceName(serviceName); + String groupName = NamingUtils.getGroupName(serviceName); + Optional service = ServiceManager.getInstance() + .getSingletonIfExist(Constants.DEFAULT_NAMESPACE_ID, groupName, serviceWithoutGroup); + if (!service.isPresent()) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("product=" + product + " not found."); } - - if (!service.getClusterMap().containsKey(cluster)) { - + ClusterMetadata metadata = metadataManager.getServiceMetadata(service.get()).orElse(new ServiceMetadata()) + .getClusters().get(cluster); + if (null == metadata) { return ResponseEntity.status(HttpStatus.NOT_FOUND) .body("product=" + product + ",cluster=" + cluster + " not found."); } - - Cluster clusterObj = service.getClusterMap().get(cluster); + ServiceInfo serviceInfo = serviceStorage.getData(service.get()); + serviceInfo = ServiceUtil.selectInstances(serviceInfo, cluster, false); return ResponseEntity.status(HttpStatus.OK) - .body(addressServerBuilderManager.generateResponseIps(clusterObj.allIPs(false))); + .body(addressServerBuilderManager.generateResponseIps(serviceInfo.getHosts())); } } diff --git a/address/src/test/java/com/alibaba/nacos/address/component/AddressServerGeneratorManagerTest.java b/address/src/test/java/com/alibaba/nacos/address/component/AddressServerGeneratorManagerTest.java index 0f42249bf..497c94dea 100644 --- a/address/src/test/java/com/alibaba/nacos/address/component/AddressServerGeneratorManagerTest.java +++ b/address/src/test/java/com/alibaba/nacos/address/component/AddressServerGeneratorManagerTest.java @@ -17,7 +17,7 @@ package com.alibaba.nacos.address.component; import com.alibaba.nacos.address.constant.AddressServerConstants; -import com.alibaba.nacos.naming.core.Instance; +import com.alibaba.nacos.api.naming.pojo.Instance; import org.junit.Assert; import org.junit.Test; @@ -67,7 +67,7 @@ public class AddressServerGeneratorManagerTest { @Test public void testGenerateResponseIps() { - final List instanceList = new ArrayList<>(); + final List instanceList = new ArrayList<>(); Instance instance1 = new Instance(); instance1.setIp("192.168.3.1"); instance1.setPort(8848); diff --git a/address/src/test/java/com/alibaba/nacos/address/controller/AddressServerClusterControllerTest.java b/address/src/test/java/com/alibaba/nacos/address/controller/AddressServerClusterControllerTest.java index 366ef5008..aa7f8eddb 100644 --- a/address/src/test/java/com/alibaba/nacos/address/controller/AddressServerClusterControllerTest.java +++ b/address/src/test/java/com/alibaba/nacos/address/controller/AddressServerClusterControllerTest.java @@ -21,8 +21,12 @@ import com.alibaba.nacos.address.component.AddressServerManager; import com.alibaba.nacos.address.constant.AddressServerConstants; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.ServiceManager; +import com.alibaba.nacos.naming.core.ClusterOperator; +import com.alibaba.nacos.naming.core.InstanceOperator; +import com.alibaba.nacos.naming.core.v2.ServiceManager; +import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,124 +44,94 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. public class AddressServerClusterControllerTest { @Mock - private ServiceManager serviceManager; + private InstanceOperator instanceOperator; + + @Mock + private NamingMetadataManager metadataManager; + + @Mock + private ClusterOperator clusterOperator; private MockMvc mockMvc; @Before public void before() { - mockMvc = MockMvcBuilders.standaloneSetup(new AddressServerClusterController(serviceManager, new AddressServerManager(), - new AddressServerGeneratorManager())).build(); + mockMvc = MockMvcBuilders.standaloneSetup( + new AddressServerClusterController(instanceOperator, metadataManager, clusterOperator, + new AddressServerManager(), new AddressServerGeneratorManager())).build(); + Service service = Service + .newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "nacos.as.default", false); + ServiceManager.getInstance().getSingleton(service); + } + + @After + public void tearDown() { + Service service = Service + .newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "nacos.as.default", false); + ServiceManager.getInstance().removeSingleton(service); } @Test public void testPostCluster() throws Exception { - mockMvc.perform(post("/nacos/v1/as/nodes") - .param("product", "default") - .param("cluster", "serverList") - .param("ips", "192.168.3.1,192.168.3.2")) - .andExpect(status().isOk()); - + mockMvc.perform(post("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") + .param("ips", "192.168.3.1,192.168.3.2")).andExpect(status().isOk()); + } @Test public void testPostClusterWithErrorIps() throws Exception { - mockMvc.perform(post("/nacos/v1/as/nodes") - .param("product", "default") - .param("cluster", "serverList") - .param("ips", "192.168.1")) - .andExpect(status().isBadRequest()); + mockMvc.perform(post("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") + .param("ips", "192.168.1")).andExpect(status().isBadRequest()); } @Test public void testPostClusterThrowException() throws Exception { - - Mockito.doThrow(new NacosException(500, "create service error")).when(serviceManager) - .createServiceIfAbsent(Mockito.eq(Constants.DEFAULT_NAMESPACE_ID), Mockito.eq( - Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default"), - Mockito.eq(false), Mockito.any()); - - mockMvc.perform(post("/nacos/v1/as/nodes") - .param("product", "default") - .param("cluster", "serverList") - .param("ips", "192.168.1")) - .andExpect(status().isInternalServerError()); + + Mockito.doThrow(new NacosException(500, "create service error")).when(clusterOperator) + .updateClusterMetadata(Mockito.eq(Constants.DEFAULT_NAMESPACE_ID), Mockito.eq( + Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default"), + Mockito.eq("serverList"), Mockito.any()); + + mockMvc.perform(post("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") + .param("ips", "192.168.1")).andExpect(status().isInternalServerError()); } @Test public void testDeleteCluster() throws Exception { - - Mockito.when(serviceManager.getService(Mockito.eq(Constants.DEFAULT_NAMESPACE_ID), - Mockito.eq(Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default"))) - .thenReturn(new Service(Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default")); - - mockMvc.perform(delete("/nacos/v1/as/nodes") - .param("product", "default") - .param("cluster", "serverList") - .param("ips", "192.168.3.1,192.168.3.2") - ).andExpect(status().isOk()); - + mockMvc.perform(delete("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") + .param("ips", "192.168.3.1,192.168.3.2")).andExpect(status().isOk()); } @Test public void testDeleteClusterCannotFindService() throws Exception { - - mockMvc.perform(delete("/nacos/v1/as/nodes") - .param("product", "default") - .param("cluster", "serverList") - .param("ips", "192.168.3.1,192.168.3.2") - ).andExpect(status().isNotFound()); + tearDown(); + mockMvc.perform(delete("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") + .param("ips", "192.168.3.1,192.168.3.2")).andExpect(status().isNotFound()); } @Test public void testDeleteClusterEmptyIps() throws Exception { - - Mockito.when(serviceManager.getService(Mockito.eq(Constants.DEFAULT_NAMESPACE_ID), - Mockito.eq(Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default"))) - .thenReturn(new Service(Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default")); - - mockMvc.perform(delete("/nacos/v1/as/nodes") - .param("product", "default") - .param("cluster", "serverList") - .param("ips", "") - ).andExpect(status().isBadRequest()); + mockMvc.perform(delete("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") + .param("ips", "")).andExpect(status().isBadRequest()); } @Test public void testDeleteClusterErrorIps() throws Exception { - - Mockito.when(serviceManager.getService(Mockito.eq(Constants.DEFAULT_NAMESPACE_ID), - Mockito.eq(Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default"))) - .thenReturn(new Service(Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default")); - - mockMvc.perform(delete("/nacos/v1/as/nodes") - .param("product", "default") - .param("cluster", "serverList") - .param("ips", "192.168.1") - ).andExpect(status().isBadRequest()); + mockMvc.perform(delete("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") + .param("ips", "192.168.1")).andExpect(status().isBadRequest()); } @Test public void testDeleteClusterThrowException() throws Exception { - - Mockito.when(serviceManager.getService(Mockito.eq(Constants.DEFAULT_NAMESPACE_ID), - Mockito.eq(Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default"))) - .thenReturn(new Service(Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default")); - - Mockito.doThrow(new NacosException(500, "remove service error")) - .when(serviceManager) - .removeInstance(Mockito.eq(Constants.DEFAULT_NAMESPACE_ID), - Mockito.eq(Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default"), - Mockito.eq(false), + Mockito.doThrow(new NacosException(500, "remove service error")).when(instanceOperator) + .removeInstance(Mockito.eq(Constants.DEFAULT_NAMESPACE_ID), Mockito.eq( + Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default"), Mockito.any()); - - mockMvc.perform(delete("/nacos/v1/as/nodes") - .param("product", "default") - .param("cluster", "serverList") - .param("ips", "192.168.3.1,192.168.3.2") - ).andExpect(status().isInternalServerError()); + + mockMvc.perform(delete("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") + .param("ips", "192.168.3.1,192.168.3.2")).andExpect(status().isInternalServerError()); } - + } diff --git a/address/src/test/java/com/alibaba/nacos/address/controller/ServerListControllerTest.java b/address/src/test/java/com/alibaba/nacos/address/controller/ServerListControllerTest.java index 43fbaa9fd..44c27b3db 100644 --- a/address/src/test/java/com/alibaba/nacos/address/controller/ServerListControllerTest.java +++ b/address/src/test/java/com/alibaba/nacos/address/controller/ServerListControllerTest.java @@ -17,25 +17,29 @@ package com.alibaba.nacos.address.controller; import com.alibaba.nacos.address.component.AddressServerGeneratorManager; -import com.alibaba.nacos.address.constant.AddressServerConstants; import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.naming.core.Cluster; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.ServiceManager; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.naming.core.v2.ServiceManager; +import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; +import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; +import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; +import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; +import java.util.Optional; +import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -43,68 +47,56 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. public class ServerListControllerTest { @Mock - private ServiceManager serviceManager; + private NamingMetadataManager metadataManager; + + @Mock + private ServiceStorage serviceStorage; + + private Service service; private MockMvc mockMvc; @Before public void before() { - this.mockMvc = MockMvcBuilders - .standaloneSetup(new ServerListController(serviceManager, new AddressServerGeneratorManager())) - .build(); + this.mockMvc = MockMvcBuilders.standaloneSetup( + new ServerListController(new AddressServerGeneratorManager(), metadataManager, serviceStorage)).build(); + service = Service + .newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "nacos.as.default", false); + ServiceManager.getInstance().getSingleton(service); + } + + @After + public void tearDown() { + ServiceManager.getInstance().removeSingleton(service); } @Test public void testGetCluster() throws Exception { - - final Service service = new Service( - Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default"); - - Cluster cluster = new Cluster(); - cluster.setName("serverList"); - cluster.setService(service); - - final HashMap clusterMap = new HashMap<>(1); - clusterMap.put("serverList", cluster); - service.setClusterMap(clusterMap); + final Service service = Service + .newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "nacos.as.default", false); + ServiceMetadata serviceMetadata = new ServiceMetadata(); + serviceMetadata.getClusters().put("serverList", new ClusterMetadata()); + when(metadataManager.getServiceMetadata(service)).thenReturn(Optional.of(serviceMetadata)); List list = new ArrayList<>(2); - list.add(new Instance("192.168.3.1", 8848)); - list.add(new Instance("192.168.3.2", 8848)); - cluster.updateIps(list, false); - - Mockito.when(serviceManager.getService(Mockito.eq(Constants.DEFAULT_NAMESPACE_ID), - Mockito.eq(Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default"))) - .thenReturn(service); - - mockMvc.perform(get("/nacos/serverList")) - .andExpect(status().isOk()); - + list.add(new Instance()); + list.add(new Instance()); + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.setHosts(list); + when(serviceStorage.getData(service)).thenReturn(serviceInfo); + mockMvc.perform(get("/nacos/serverList")).andExpect(status().isOk()); } @Test public void testGetClusterCannotFindService() throws Exception { - - mockMvc.perform(get("/default/serverList")) - .andExpect(status().isNotFound()); + tearDown(); + mockMvc.perform(get("/default/serverList")).andExpect(status().isNotFound()); } @Test public void testGetClusterCannotFindCluster() throws Exception { - - final Service service = new Service( - Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default"); - - final HashMap clusterMap = new HashMap<>(1); - service.setClusterMap(clusterMap); - - Mockito.when(serviceManager.getService(Mockito.eq(Constants.DEFAULT_NAMESPACE_ID), - Mockito.eq(Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default"))) - .thenReturn(service); - - mockMvc.perform(get("/nacos/serverList")) - .andExpect(status().isNotFound()); + mockMvc.perform(get("/nacos/serverList")).andExpect(status().isNotFound()); } } diff --git a/api/src/main/java/com/alibaba/nacos/api/PropertyKeyConst.java b/api/src/main/java/com/alibaba/nacos/api/PropertyKeyConst.java index 7cf8c8dc5..8257511be 100644 --- a/api/src/main/java/com/alibaba/nacos/api/PropertyKeyConst.java +++ b/api/src/main/java/com/alibaba/nacos/api/PropertyKeyConst.java @@ -75,6 +75,8 @@ public class PropertyKeyConst { public static final String NAMING_PUSH_EMPTY_PROTECTION = "namingPushEmptyProtection"; + public static final String NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE = "namingAsyncQuerySubscribeService"; + public static final String PUSH_RECEIVER_UDP_PORT = "push.receiver.udp.port"; /** diff --git a/api/src/main/java/com/alibaba/nacos/api/SystemPropertyKeyConst.java b/api/src/main/java/com/alibaba/nacos/api/SystemPropertyKeyConst.java index 7429b824a..3eac8aaa1 100644 --- a/api/src/main/java/com/alibaba/nacos/api/SystemPropertyKeyConst.java +++ b/api/src/main/java/com/alibaba/nacos/api/SystemPropertyKeyConst.java @@ -27,8 +27,6 @@ public interface SystemPropertyKeyConst { String NAMING_SERVER_PORT = "nacos.naming.exposed.port"; - String NAMING_WEB_CONTEXT = "nacos.naming.web.context"; - /** * In the cloud (Alibaba Cloud or other cloud vendors) environment, whether to enable namespace resolution in the * cloud environment. diff --git a/api/src/main/java/com/alibaba/nacos/api/config/filter/IFilterConfig.java b/api/src/main/java/com/alibaba/nacos/api/annotation/NacosApi.java similarity index 54% rename from api/src/main/java/com/alibaba/nacos/api/config/filter/IFilterConfig.java rename to api/src/main/java/com/alibaba/nacos/api/annotation/NacosApi.java index 4087ac155..e00f2a707 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/filter/IFilterConfig.java +++ b/api/src/main/java/com/alibaba/nacos/api/annotation/NacosApi.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2018 Alibaba Group Holding Ltd. + * Copyright 1999-2022 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. @@ -14,29 +14,23 @@ * limitations under the License. */ -package com.alibaba.nacos.api.config.filter; +package com.alibaba.nacos.api.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** - * Filter Config Interface. - * - * @author Nacos + * An annotation for Nacos API v2 Controller. + * @author dongyafei + * @date 2022/7/22 */ -@Deprecated -public interface IFilterConfig { - - /** - * get filter name. - * - * @return filter name - */ - String getFilterName(); - - /** - * Get init param. - * - * @param name parameter name - * @return param - */ - Object getInitParameter(String name); - + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface NacosApi { + } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosConfigurationProperties.java b/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosConfigurationProperties.java index 0e6bc518d..785e2f05f 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosConfigurationProperties.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosConfigurationProperties.java @@ -62,13 +62,6 @@ public @interface NacosConfigurationProperties { */ String dataId(); - /** - * set config type is yaml this method is deprecated, we support you use {@link #type()} to set config type. - * - * @return default value false - */ - @Deprecated boolean yaml() default false; - /** * config style. * diff --git a/api/src/main/java/com/alibaba/nacos/api/config/filter/AbstractConfigFilter.java b/api/src/main/java/com/alibaba/nacos/api/config/filter/AbstractConfigFilter.java index e87b94267..1e0ba7e1d 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/filter/AbstractConfigFilter.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/filter/AbstractConfigFilter.java @@ -22,13 +22,5 @@ package com.alibaba.nacos.api.config.filter; * @author luyanbo(RobberPhex) */ public abstract class AbstractConfigFilter implements IConfigFilter { - - /** - * init. - * - * @param filterConfig Filter Config - */ - @Override - public void init(IFilterConfig filterConfig) { - } + } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilter.java b/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilter.java index c4222b532..3d78bc8ec 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilter.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilter.java @@ -30,14 +30,6 @@ import java.util.Properties; */ public interface IConfigFilter { - /** - * Init. - * - * @param filterConfig Filter Config - */ - @Deprecated - void init(IFilterConfig filterConfig); - /** * Init. * diff --git a/api/src/main/java/com/alibaba/nacos/api/exception/api/NacosApiException.java b/api/src/main/java/com/alibaba/nacos/api/exception/api/NacosApiException.java new file mode 100644 index 000000000..d6fccfd60 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/exception/api/NacosApiException.java @@ -0,0 +1,78 @@ +/* + * Copyright 1999-2022 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.nacos.api.exception.api; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.utils.StringUtils; + +/** Exception for open API.
+ * errCode -> HTTP status code inherited from {@link NacosException}
+ * errMsg -> detail error message inherited from {@link NacosException}
+ * detailErrCode -> error code for api v2.0
+ * errAbstract -> abstract error message for api v2.0 + * @author dongyafei + * @date 2022/7/22 + */ +public class NacosApiException extends NacosException { + + /** + * serialVersionUID. + */ + private static final long serialVersionUID = 2245627968556056573L; + + /** + * error code for api v2.0. + */ + private int detailErrCode; + + /** + * abstract error description for api v2.0. + */ + private String errAbstract; + + public NacosApiException() { + } + + public NacosApiException(int statusCode, ErrorCode errorCode, Throwable throwable, String message) { + super(statusCode, message, throwable); + this.detailErrCode = errorCode.getCode(); + this.errAbstract = errorCode.getMsg(); + } + + public NacosApiException(int statusCode, ErrorCode errorCode, String message) { + super(statusCode, message); + this.detailErrCode = errorCode.getCode(); + this.errAbstract = errorCode.getMsg(); + } + + public int getDetailErrCode() { + return detailErrCode; + } + + public String getErrAbstract() { + if (!StringUtils.isBlank(this.errAbstract)) { + return this.errAbstract; + } + return Constants.NULL; + } + + public void setErrAbstract(String errAbstract) { + this.errAbstract = errAbstract; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/model/v2/ErrorCode.java b/api/src/main/java/com/alibaba/nacos/api/model/v2/ErrorCode.java new file mode 100644 index 000000000..b86897c11 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/model/v2/ErrorCode.java @@ -0,0 +1,204 @@ +/* + * Copyright 1999-2022 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.nacos.api.model.v2; + +/** + * Response Error Code. + * + * @author dongyafei + * @date 2022/7/22 + */ + +public enum ErrorCode { + + /** + * success. + */ + SUCCESS(0, "success"), + + /** + * parameter missing. + */ + PARAMETER_MISSING(10000, "parameter missing"), + + /** + * access denied. + */ + ACCESS_DENIED(10001, "access denied"), + + /** + * data access error. + */ + DATA_ACCESS_ERROR(10002, "data access error"), + + /** + * 'tenant' parameter error. + */ + TENANT_PARAM_ERROR(20001, "'tenant' parameter error"), + + /** + * parameter validate error. + */ + PARAMETER_VALIDATE_ERROR(20002, "parameter validate error"), + + /** + * MediaType Error. + */ + MEDIA_TYPE_ERROR(20003, "MediaType Error"), + + /** + * resource not found. + */ + RESOURCE_NOT_FOUND(20004, "resource not found"), + + /** + * resource conflict. + */ + RESOURCE_CONFLICT(20005, "resource conflict"), + + /** + * config listener is null. + */ + CONFIG_LISTENER_IS_NULL(20006, "config listener is null"), + + /** + * config listener error. + */ + CONFIG_LISTENER_ERROR(20007, "config listener error"), + + /** + * invalid dataId. + */ + INVALID_DATA_ID(20008, "invalid dataId"), + + /** + * parameter mismatch. + */ + PARAMETER_MISMATCH(20009, "parameter mismatch"), + + /** + * service name error. + */ + SERVICE_NAME_ERROR(21000, "service name error"), + + /** + * weight error. + */ + WEIGHT_ERROR(21001, "weight error"), + + /** + * instance metadata error. + */ + INSTANCE_METADATA_ERROR(21002, "instance metadata error"), + + /** + * instance not found. + */ + INSTANCE_NOT_FOUND(21003, "instance not found"), + + /** + * instance error. + */ + INSTANCE_ERROR(21004, "instance error"), + + /** + * service metadata error. + */ + SERVICE_METADATA_ERROR(21005, "service metadata error"), + + /** + * selector error. + */ + SELECTOR_ERROR(21006, "selector error"), + + /** + * service already exist. + */ + SERVICE_ALREADY_EXIST(21007, "service already exist"), + + /** + * service not exist. + */ + SERVICE_NOT_EXIST(21008, "service not exist"), + + /** + * service delete failure. + */ + SERVICE_DELETE_FAILURE(21009, "service delete failure"), + + /** + * healthy param miss. + */ + HEALTHY_PARAM_MISS(21010, "healthy param miss"), + + /** + * health check still running. + */ + HEALTH_CHECK_STILL_RUNNING(21011, "health check still running"), + + /** + * illegal namespace. + */ + ILLEGAL_NAMESPACE(22000, "illegal namespace"), + + /** + * namespace not exist. + */ + NAMESPACE_NOT_EXIST(22001, "namespace not exist"), + + /** + * namespace already exist. + */ + NAMESPACE_ALREADY_EXIST(22002, "namespace already exist"), + + /** + * illegal state. + */ + ILLEGAL_STATE(23000, "illegal state"), + + /** + * node info error. + */ + NODE_INFO_ERROR(23001, "node info error"), + + /** + * node down failure. + */ + NODE_DOWN_FAILURE(23001, "node down failure"), + + /** + * server error. + */ + SERVER_ERROR(30000, "server error"); + + private final Integer code; + + private final String msg; + + public Integer getCode() { + return code; + } + + public String getMsg() { + return msg; + } + + ErrorCode(Integer code, String msg) { + this.code = code; + this.msg = msg; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/model/v2/Result.java b/api/src/main/java/com/alibaba/nacos/api/model/v2/Result.java new file mode 100644 index 000000000..ee1bb7a70 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/model/v2/Result.java @@ -0,0 +1,115 @@ +/* + * Copyright 1999-2022 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.nacos.api.model.v2; + +import java.io.Serializable; + +/** + * Response Result. + * + * @author dongyafei + * @date 2022/7/12 + */ +public class Result implements Serializable { + + private static final long serialVersionUID = 6258345442767540526L; + + private final Integer code; + + private final String message; + + private final T data; + + public Result(Integer code, String message, T data) { + this.code = code; + this.message = message; + this.data = data; + } + + public Result() { + this(null); + } + + public Result(T data) { + this(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), data); + } + + public Result(Integer code, String message) { + this(code, message, null); + } + + /** + * Success return with nothing. + * @param data type + * @return Result + */ + public static Result success() { + return new Result<>(); + } + + /** + * Success return with data. + * @param data type + * @return Result + */ + public static Result success(T data) { + return new Result<>(data); + } + + /** + * Failed return with message and detail error information. + * @return Result + */ + public static Result failure(String message) { + return Result.failure(ErrorCode.SERVER_ERROR, message); + } + + /** + * Failed return with errorCode and message. + * @param data type + * @return Result + */ + public static Result failure(ErrorCode errorCode) { + return new Result<>(errorCode.getCode(), errorCode.getMsg()); + } + + /** + * Failed return with errorCode, message and data. + * @param data type + * @return Result + */ + public static Result failure(ErrorCode errorCode, T data) { + return new Result<>(errorCode.getCode(), errorCode.getMsg(), data); + } + + @Override + public String toString() { + return "Result{" + "errorCode=" + code + ", message='" + message + '\'' + ", data=" + data + '}'; + } + + public Integer getCode() { + return code; + } + + public String getMessage() { + return message; + } + + public T getData() { + return data; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java b/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java index be6170cd5..d27ad5bd0 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java @@ -107,6 +107,17 @@ public interface NamingService { */ void batchRegisterInstance(String serviceName, String groupName, List instances) throws NacosException; + /** + * batch deRegister instance to service with specified instance properties. + * + * @param serviceName name of service + * @param groupName group of service + * @param instances instances to deRegister + * @throws NacosException nacos exception + * @since 2.2.0 + */ + void batchDeregisterInstance(String serviceName, String groupName, List instances) throws NacosException; + /** * deregister instance from a service. * diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/BatchInstanceResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/BatchInstanceResponse.java index 983941e2b..afee5e72b 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/BatchInstanceResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/BatchInstanceResponse.java @@ -16,25 +16,18 @@ package com.alibaba.nacos.api.naming.remote.response; -import com.alibaba.nacos.api.remote.response.Response; - /** * batch instance response. * * @author chenhao26 */ -public class BatchInstanceResponse extends Response { - - private String type; +public class BatchInstanceResponse extends InstanceResponse { public BatchInstanceResponse() { + super(); } public BatchInstanceResponse(String type) { - this.type = type; - } - - public void setType(String type) { - this.type = type; + super(type); } } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/InstanceResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/InstanceResponse.java index 3c02543d7..56606faa5 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/InstanceResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/InstanceResponse.java @@ -38,4 +38,7 @@ public class InstanceResponse extends Response { this.type = type; } + public String getType() { + return type; + } } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java b/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java index 5106a89ad..7c88abc70 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java @@ -18,6 +18,8 @@ package com.alibaba.nacos.api.naming.utils; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.utils.StringUtils; @@ -138,11 +140,11 @@ public class NamingUtils { public static void checkInstanceIsLegal(Instance instance) throws NacosException { if (instance.getInstanceHeartBeatTimeOut() < instance.getInstanceHeartBeatInterval() || instance.getIpDeleteTimeout() < instance.getInstanceHeartBeatInterval()) { - throw new NacosException(NacosException.INVALID_PARAM, + throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, "Instance 'heart beat interval' must less than 'heart beat timeout' and 'ip delete timeout'."); } if (!StringUtils.isEmpty(instance.getClusterName()) && !CLUSTER_NAME_PATTERN.matcher(instance.getClusterName()).matches()) { - throw new NacosException(NacosException.INVALID_PARAM, + throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, String.format("Instance 'clusterName' should be characters with only 0-9a-zA-Z-. (current: %s)", instance.getClusterName())); } @@ -155,7 +157,7 @@ public class NamingUtils { */ public static void checkInstanceIsEphemeral(Instance instance) throws NacosException { if (!instance.isEphemeral()) { - throw new NacosException(NacosException.INVALID_PARAM, + throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, String.format("Batch registration does not allow persistent instance registration , Instanceļ¼š%s", instance)); } } diff --git a/api/src/main/java/com/alibaba/nacos/api/utils/NetUtils.java b/api/src/main/java/com/alibaba/nacos/api/utils/NetUtils.java index 123a01083..894700ffa 100644 --- a/api/src/main/java/com/alibaba/nacos/api/utils/NetUtils.java +++ b/api/src/main/java/com/alibaba/nacos/api/utils/NetUtils.java @@ -30,14 +30,11 @@ import java.util.Enumeration; * @author xuanyin.zy */ public class NetUtils { - - @Deprecated - private static final String CLIENT_NAMING_LOCAL_IP_PROPERTY = "com.alibaba.nacos.client.naming.local.ip"; - + private static final String CLIENT_LOCAL_IP_PROPERTY = "com.alibaba.nacos.client.local.ip"; - + private static final String CLIENT_LOCAL_PREFER_HOSTNAME_PROPERTY = "com.alibaba.nacos.client.local.preferHostname"; - + private static final String LEGAL_LOCAL_IP_PROPERTY = "java.net.preferIPv6Addresses"; private static final String DEFAULT_SOLVE_FAILED_RETURN = "resolve_failed"; @@ -53,23 +50,19 @@ public class NetUtils { if (!StringUtils.isEmpty(localIp)) { return localIp; } - if (System.getProperties().containsKey(CLIENT_LOCAL_IP_PROPERTY)) { return localIp = System.getProperty(CLIENT_LOCAL_IP_PROPERTY, getAddress()); } - - String ip = System.getProperty(CLIENT_NAMING_LOCAL_IP_PROPERTY, getAddress()); - - return localIp = ip; - + localIp = getAddress(); + return localIp; } - + private static String getAddress() { InetAddress inetAddress = findFirstNonLoopbackAddress(); if (inetAddress == null) { return DEFAULT_SOLVE_FAILED_RETURN; } - + boolean preferHost = Boolean.parseBoolean(System.getProperty(CLIENT_LOCAL_PREFER_HOSTNAME_PROPERTY)); return preferHost ? inetAddress.getHostName() : inetAddress.getHostAddress(); } @@ -91,9 +84,8 @@ public class NetUtils { for (Enumeration addrs = ifc.getInetAddresses(); addrs.hasMoreElements(); ) { InetAddress address = addrs.nextElement(); - boolean isLegalIpVersion = - Boolean.parseBoolean(System.getProperty(LEGAL_LOCAL_IP_PROPERTY)) - ? address instanceof Inet6Address : address instanceof Inet4Address; + boolean isLegalIpVersion = Boolean.parseBoolean(System.getProperty(LEGAL_LOCAL_IP_PROPERTY)) + ? address instanceof Inet6Address : address instanceof Inet4Address; if (isLegalIpVersion && !address.isLoopbackAddress()) { result = address; } diff --git a/api/src/test/java/com/alibaba/nacos/api/naming/utils/NamingUtilsTest.java b/api/src/test/java/com/alibaba/nacos/api/naming/utils/NamingUtilsTest.java index 38f74753f..979c6e4bd 100644 --- a/api/src/test/java/com/alibaba/nacos/api/naming/utils/NamingUtilsTest.java +++ b/api/src/test/java/com/alibaba/nacos/api/naming/utils/NamingUtilsTest.java @@ -54,7 +54,7 @@ public class NamingUtilsTest { NamingUtils.checkInstanceIsLegal(instance); assertTrue(false); } catch (Exception e) { - assertTrue(NacosException.class.equals(e.getClass())); + assertTrue(e instanceof NacosException); assertEquals( "Instance 'clusterName' should be characters with only 0-9a-zA-Z-. (current: cluster1,cluster2)", e.getMessage()); @@ -75,7 +75,7 @@ public class NamingUtilsTest { NamingUtils.checkInstanceIsLegal(instance); assertTrue(false); } catch (Exception e) { - assertTrue(NacosException.class.equals(e.getClass())); + assertTrue(e instanceof NacosException); assertEquals( "Instance 'heart beat interval' must less than 'heart beat timeout' and 'ip delete timeout'.", e.getMessage()); @@ -98,7 +98,7 @@ public class NamingUtilsTest { NamingUtils.batchCheckInstanceIsLegal(instanceList); assertTrue(false); } catch (Exception e) { - assertTrue(NacosException.class.equals(e.getClass())); + assertTrue(e instanceof NacosException); assertEquals( "Instance 'clusterName' should be characters with only 0-9a-zA-Z-. (current: cluster1,cluster2)", e.getMessage()); @@ -124,7 +124,7 @@ public class NamingUtilsTest { NamingUtils.batchCheckInstanceIsLegal(instanceList); assertTrue(false); } catch (Exception e) { - assertTrue(NacosException.class.equals(e.getClass())); + assertTrue(e instanceof NacosException); assertEquals( "Instance 'heart beat interval' must less than 'heart beat timeout' and 'ip delete timeout'.", e.getMessage()); @@ -166,4 +166,4 @@ public class NamingUtilsTest { String str2 = "123456"; assertTrue(NamingUtils.isNumber(str2)); } -} \ No newline at end of file +} diff --git a/api/src/test/java/com/alibaba/nacos/api/utils/NetUtilsTest.java b/api/src/test/java/com/alibaba/nacos/api/utils/NetUtilsTest.java index f9accc4c7..be9dfb6a2 100644 --- a/api/src/test/java/com/alibaba/nacos/api/utils/NetUtilsTest.java +++ b/api/src/test/java/com/alibaba/nacos/api/utils/NetUtilsTest.java @@ -34,7 +34,6 @@ public class NetUtilsTest { field.setAccessible(true); field.set(null, ""); System.clearProperty("com.alibaba.nacos.client.local.ip"); - System.clearProperty("com.alibaba.nacos.client.naming.local.ip"); System.clearProperty("com.alibaba.nacos.client.local.preferHostname"); } @@ -45,12 +44,6 @@ public class NetUtilsTest { assertEquals("10.2.8.8", NetUtils.localIP()); } - @Test - public void testCompatibleLocalIP() { - System.setProperty("com.alibaba.nacos.client.naming.local.ip", "10.2.7.8"); - assertEquals("10.2.7.8", NetUtils.localIP()); - } - @Test public void testPreferHostname() throws Exception { Class clazz = Class.forName("com.alibaba.nacos.api.utils.NetUtils"); @@ -63,4 +56,4 @@ public class NetUtilsTest { assertEquals(hostname, NetUtils.localIP()); } -} \ No newline at end of file +} diff --git a/client/pom.xml b/client/pom.xml index 7d606636c..6ed18aea2 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -145,7 +145,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.1 + 3.2.4 false @@ -159,25 +159,56 @@ false true false - false + true + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + io.grpc:grpc-netty-shaded + + + META-INF/native/*.* + + + + + com.alibaba.nacos:nacos-api + + + com/alibaba/nacos/api/**/** + + + + + true true - io.grpc:* - io.opencensus:* - io.perfmark:* - com.google.guava:guava - com.google.guava:failureaccess - com.google.errorprone:error_prone_annotations - com.google.j2objc:j2objc-annotations - com.google.code.gson:gson - com.google.protobuf:protobuf-java com.alibaba.nacos:nacos-api com.alibaba.nacos:nacos-common - org.checkerframework:checker-qual + org.conscrypt:conscrypt-openjdk + org.mortbay.jetty.alpn:alpn-boot + org.eclipse.jetty.npn:npn-api + org.reflections:reflections + com.google.guava:guava + io.grpc:* + io.opencensus:* + org.javassist:* + io.perfmark:perfmark-api + com.google.*:* + javax.annotation:javax.annotation-api + org.checkerframework:* + org.codehaus.mojo:* + @@ -202,6 +233,12 @@ com.google com.alibaba.nacos.shaded.com.google + + + javax.annotation + com.alibaba.nacos.shaded.javax.annotation + + io.perfmark com.alibaba.nacos.shaded.io.perfmark @@ -227,7 +264,6 @@ com.alibaba.nacos.shaded.org.example - + + maven-jar-plugin + + + pure-jar + package + + jar + + + pure + + + + @@ -250,7 +301,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.1 + 3.2.4 false @@ -264,29 +315,60 @@ false true false - false + true + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + io.grpc:grpc-netty-shaded + + + META-INF/native/*.* + + + + + com.alibaba.nacos:nacos-api + + + com/alibaba/nacos/api/**/** + + + + + true true - + - io.grpc:* - io.opencensus:* - io.perfmark:* - com.google.guava:guava - com.google.guava:failureaccess - com.google.errorprone:error_prone_annotations - com.google.j2objc:j2objc-annotations - com.google.code.gson:gson - com.google.protobuf:protobuf-java com.alibaba.nacos:nacos-api com.alibaba.nacos:nacos-common - org.checkerframework:checker-qual + org.conscrypt:conscrypt-openjdk + org.mortbay.jetty.alpn:alpn-boot + org.eclipse.jetty.npn:npn-api + org.reflections:reflections + com.google.guava:guava + io.grpc:* + io.opencensus:* + org.javassist:* + io.perfmark:perfmark-api + com.google.*:* + javax.annotation:javax.annotation-api + org.checkerframework:* + org.codehaus.mojo:* + - + - + io.grpc com.alibaba.nacos.shaded.io.grpc @@ -302,11 +384,17 @@ io.grpc.netty.shaded.io.grpc.netty.* - + com.google com.alibaba.nacos.shaded.com.google + + + javax.annotation + com.alibaba.nacos.shaded.javax.annotation + + io.perfmark com.alibaba.nacos.shaded.io.perfmark @@ -331,20 +419,34 @@ org.example com.alibaba.nacos.shaded.org.example - - + - + + + maven-jar-plugin + + + pure-jar + package + + jar + + + pure + + + + diff --git a/client/src/main/java/com/alibaba/nacos/client/auth/ram/identify/CredentialService.java b/client/src/main/java/com/alibaba/nacos/client/auth/ram/identify/CredentialService.java index b49a848a8..e02d79745 100644 --- a/client/src/main/java/com/alibaba/nacos/client/auth/ram/identify/CredentialService.java +++ b/client/src/main/java/com/alibaba/nacos/client/auth/ram/identify/CredentialService.java @@ -126,25 +126,4 @@ public final class CredentialService implements SpasCredentialLoader { public void registerCredentialListener(CredentialListener listener) { this.listener = listener; } - - @Deprecated - public void setAccessKey(String accessKey) { - credentials.setAccessKey(accessKey); - } - - @Deprecated - public void setSecretKey(String secretKey) { - credentials.setSecretKey(secretKey); - } - - @Deprecated - public String getAccessKey() { - return credentials.getAccessKey(); - } - - @Deprecated - public String getSecretKey() { - return credentials.getSecretKey(); - } - } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/http/ServerHttpAgent.java b/client/src/main/java/com/alibaba/nacos/client/config/http/ServerHttpAgent.java index ae2d94473..b0312ccb2 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/http/ServerHttpAgent.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/http/ServerHttpAgent.java @@ -275,6 +275,7 @@ public class ServerHttpAgent implements HttpAgent { String className = this.getClass().getName(); LOGGER.info("{} do shutdown begin", className); ConfigHttpClientManager.getInstance().shutdown(); + serverListMgr.shutdown(); LOGGER.info("{} do shutdown stop", className); } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/CacheData.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/CacheData.java index 738688688..715956664 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/CacheData.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/CacheData.java @@ -111,6 +111,11 @@ public class CacheData { */ private volatile boolean isSyncWithServer = false; + /** + * if is cache data is discard,need to remove. + */ + private volatile boolean isDiscard = false; + private String type; public boolean isInitializing() { @@ -402,6 +407,14 @@ public class CacheData { isSyncWithServer = syncWithServer; } + public boolean isDiscard() { + return isDiscard; + } + + public void setDiscard(boolean discard) { + isDiscard = discard; + } + public CacheData(ConfigFilterChainManager configFilterChainManager, String name, String dataId, String group) { this(configFilterChainManager, name, dataId, group, TenantUtil.getUserTenantForAcm()); } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java index 7cb69a8f1..6367c94a5 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java @@ -126,11 +126,11 @@ public class ClientWorker implements Closeable { private int taskPenaltyTime; private boolean enableRemoteSyncConfig = false; - + private static final int MIN_THREAD_NUM = 2; - + private static final int THREAD_MULTIPLE = 1; - + /** * Add listeners for data. * @@ -146,6 +146,7 @@ public class ClientWorker implements Closeable { for (Listener listener : listeners) { cache.addListener(listener); } + cache.setDiscard(false); cache.setSyncWithServer(false); agent.notifyListenConfig(); @@ -169,6 +170,7 @@ public class ClientWorker implements Closeable { for (Listener listener : listeners) { cache.addListener(listener); } + cache.setDiscard(false); cache.setSyncWithServer(false); agent.notifyListenConfig(); } @@ -196,6 +198,7 @@ public class ClientWorker implements Closeable { for (Listener listener : listeners) { cache.addListener(listener); } + cache.setDiscard(false); cache.setSyncWithServer(false); agent.notifyListenConfig(); } @@ -217,6 +220,7 @@ public class ClientWorker implements Closeable { cache.removeListener(listener); if (cache.getListeners().isEmpty()) { cache.setSyncWithServer(false); + cache.setDiscard(true); agent.removeCache(dataId, group); } } @@ -240,6 +244,7 @@ public class ClientWorker implements Closeable { cache.removeListener(listener); if (cache.getListeners().isEmpty()) { cache.setSyncWithServer(false); + cache.setDiscard(true); agent.removeCache(dataId, group); } } @@ -425,8 +430,8 @@ public class ClientWorker implements Closeable { } private void refreshContentAndCheck(String groupKey, boolean notify) { - if (cacheMap.get() != null && cacheMap.get().containsKey(groupKey)) { - CacheData cache = cacheMap.get().get(groupKey); + CacheData cache = cacheMap.get().get(groupKey); + if (cache != null) { refreshContentAndCheck(cache, notify); } } @@ -696,7 +701,7 @@ public class ClientWorker implements Closeable { continue; } executeConfigListen(); - } catch (Exception e) { + } catch (Throwable e) { LOGGER.error("[ rpc listen execute ] [rpc listen] exception", e); } } @@ -733,7 +738,7 @@ public class ClientWorker implements Closeable { } } - if (!CollectionUtils.isEmpty(cache.getListeners())) { + if (!cache.isDiscard()) { //get listen config if (!cache.isUseLocalConfigInfo()) { List cacheDatas = listenCachesMap.get(String.valueOf(cache.getTaskId())); @@ -744,7 +749,7 @@ public class ClientWorker implements Closeable { cacheDatas.add(cache); } - } else if (CollectionUtils.isEmpty(cache.getListeners())) { + } else if (cache.isDiscard()) { if (!cache.isUseLocalConfigInfo()) { List cacheDatas = removeListenCachesMap.get(String.valueOf(cache.getTaskId())); @@ -779,7 +784,7 @@ public class ClientWorker implements Closeable { RpcClient rpcClient = ensureRpcClient(taskId); ConfigChangeBatchListenResponse configChangeBatchListenResponse = (ConfigChangeBatchListenResponse) requestProxy( rpcClient, configChangeListenRequest); - if (configChangeBatchListenResponse != null && configChangeBatchListenResponse.isSuccess()) { + if (configChangeBatchListenResponse.isSuccess()) { Set changeKeys = new HashSet<>(); //handle changed keys,notify listener @@ -807,8 +812,8 @@ public class ClientWorker implements Closeable { if (!cacheData.getListeners().isEmpty()) { Long previousTimesStamp = timestampMap.get(groupKey); - if (previousTimesStamp != null && !cacheData.getLastModifiedTs().compareAndSet(previousTimesStamp, - System.currentTimeMillis())) { + if (previousTimesStamp != null && !cacheData.getLastModifiedTs() + .compareAndSet(previousTimesStamp, System.currentTimeMillis())) { continue; } cacheData.setSyncWithServer(true); @@ -844,7 +849,7 @@ public class ClientWorker implements Closeable { if (removeSuccess) { for (CacheData cacheData : removeListenCaches) { synchronized (cacheData) { - if (cacheData.getListeners().isEmpty()) { + if (cacheData.isDiscard()) { ClientWorker.this .removeCache(cacheData.dataId, cacheData.group, cacheData.tenant); } @@ -979,8 +984,8 @@ public class ClientWorker implements Closeable { LOGGER.error("[{}] [sub-server-error] dataId={}, group={}, tenant={}, code={}", this.getName(), dataId, group, tenant, response); throw new NacosException(response.getErrorCode(), - "http error, code=" + response.getErrorCode() + ",msg=" + response.getMessage() + ",dataId=" + dataId + ",group=" + group - + ",tenant=" + tenant); + "http error, code=" + response.getErrorCode() + ",msg=" + response.getMessage() + ",dataId=" + + dataId + ",group=" + group + ",tenant=" + tenant); } } @@ -1012,20 +1017,20 @@ public class ClientWorker implements Closeable { if (request instanceof ConfigQueryRequest) { String tenant = ((ConfigQueryRequest) request).getTenant(); String group = ((ConfigQueryRequest) request).getGroup(); - String dataId = ((ConfigQueryRequest) request).getGroup(); + String dataId = ((ConfigQueryRequest) request).getDataId(); return buildResource(tenant, group, dataId); } if (request instanceof ConfigPublishRequest) { String tenant = ((ConfigPublishRequest) request).getTenant(); String group = ((ConfigPublishRequest) request).getGroup(); - String dataId = ((ConfigPublishRequest) request).getGroup(); + String dataId = ((ConfigPublishRequest) request).getDataId(); return buildResource(tenant, group, dataId); } if (request instanceof ConfigRemoveRequest) { String tenant = ((ConfigRemoveRequest) request).getTenant(); String group = ((ConfigRemoveRequest) request).getGroup(); - String dataId = ((ConfigRemoveRequest) request).getGroup(); + String dataId = ((ConfigRemoveRequest) request).getDataId(); return buildResource(tenant, group, dataId); } return RequestResource.configBuilder().build(); @@ -1059,7 +1064,7 @@ public class ClientWorker implements Closeable { } } catch (Exception e) { LOGGER.warn("[{}] [publish-single] error, dataId={}, group={}, tenant={}, code={}, msg={}", - this.getName(), dataId, group, tenant, "unkonw", e.getMessage()); + this.getName(), dataId, group, tenant, "unknown", e.getMessage()); return false; } } diff --git a/client/src/main/java/com/alibaba/nacos/client/constant/Constants.java b/client/src/main/java/com/alibaba/nacos/client/constant/Constants.java index 53a35d574..f38cc7db7 100644 --- a/client/src/main/java/com/alibaba/nacos/client/constant/Constants.java +++ b/client/src/main/java/com/alibaba/nacos/client/constant/Constants.java @@ -35,7 +35,7 @@ public class Constants { public static final String JM_SNAPSHOT_PATH = "JM.SNAPSHOT.PATH"; - public static final String NACOS_ENVS_SEARCH = "nacos.envs.search"; + public static final String NACOS_ENV_FIRST = "nacos.env.first"; } diff --git a/client/src/main/java/com/alibaba/nacos/client/env/AbstractPropertySource.java b/client/src/main/java/com/alibaba/nacos/client/env/AbstractPropertySource.java index 3b34951d6..779580a18 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/AbstractPropertySource.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/AbstractPropertySource.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.client.env; +import java.util.Properties; + abstract class AbstractPropertySource { /** @@ -31,4 +33,17 @@ abstract class AbstractPropertySource { */ abstract String getProperty(String key); + /** + * Tests if the specified object is a key in this propertySource. + * @param key key ā€“ possible key + * @return true if and only if the specified object is a key in this propertySource, false otherwise. + */ + abstract boolean containsKey(String key); + + /** + * to properties. + * @return properties + */ + abstract Properties asProperties(); + } diff --git a/client/src/main/java/com/alibaba/nacos/client/env/DefaultSettingPropertySource.java b/client/src/main/java/com/alibaba/nacos/client/env/DefaultSettingPropertySource.java index 6539c6989..6990e0e47 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/DefaultSettingPropertySource.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/DefaultSettingPropertySource.java @@ -21,7 +21,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.InputStream; -import java.net.URL; import java.util.Properties; class DefaultSettingPropertySource extends AbstractPropertySource { @@ -33,12 +32,10 @@ class DefaultSettingPropertySource extends AbstractPropertySource { private final Properties defaultSetting = new Properties(); DefaultSettingPropertySource() { - try { - final URL resourceUrl = ResourceUtils.getResourceUrl(DEFAULT_SETTING_PATH); - final InputStream inputStream = resourceUrl.openStream(); + try (final InputStream inputStream = ResourceUtils.getResourceUrl(DEFAULT_SETTING_PATH).openStream()) { defaultSetting.load(inputStream); } catch (Exception e) { - LOGGER.warn("load default setting failed"); + LOGGER.error("load default setting failed", e); } } @@ -51,4 +48,16 @@ class DefaultSettingPropertySource extends AbstractPropertySource { String getProperty(String key) { return defaultSetting.getProperty(key); } + + @Override + boolean containsKey(String key) { + return defaultSetting.containsKey(key); + } + + @Override + Properties asProperties() { + Properties properties = new Properties(); + properties.putAll(defaultSetting); + return properties; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/env/JvmArgsPropertySource.java b/client/src/main/java/com/alibaba/nacos/client/env/JvmArgsPropertySource.java index 5c4475c05..e9448d8df 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/JvmArgsPropertySource.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/JvmArgsPropertySource.java @@ -35,4 +35,16 @@ class JvmArgsPropertySource extends AbstractPropertySource { String getProperty(String key) { return properties.getProperty(key); } + + @Override + boolean containsKey(String key) { + return properties.containsKey(key); + } + + @Override + Properties asProperties() { + Properties properties = new Properties(); + properties.putAll(this.properties); + return properties; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironment.java b/client/src/main/java/com/alibaba/nacos/client/env/NacosClientProperties.java similarity index 58% rename from client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironment.java rename to client/src/main/java/com/alibaba/nacos/client/env/NacosClientProperties.java index 14269fc6d..021ae97d1 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironment.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/NacosClientProperties.java @@ -16,12 +16,27 @@ package com.alibaba.nacos.client.env; +import java.util.Properties; + /** - * nacos env interface. - * + * NacosClientProperties interface. + * include all the properties from jvm args, system environment, default setting. + * more details you can see https://github.com/alibaba/nacos/issues/8622 * @author onewe */ -public interface NacosEnvironment { +public interface NacosClientProperties { + + /** + * all the NacosClientProperties object must be created by PROTOTYPE, + * so child NacosClientProperties can read properties from the PROTOTYPE. + * it looks like this: + * |-PROTOTYPE----------------> ip=127.0.0.1 + * |---|-child1---------------> port=6379 + * if you search key called "port" from child1, certainly you will get 6379 + * if you search key called "ip" from child1, you will get 127.0.0.1. + * because the child can read properties from parent NacosClientProperties + */ + NacosClientProperties PROTOTYPE = SearchableProperties.INSTANCE; /** * get property, if the value can not be got by the special key, the null will be returned. @@ -87,4 +102,42 @@ public interface NacosEnvironment { */ Long getLong(String key, Long defaultValue); + /** + * set property. + * @param key key + * @param value value + */ + void setProperty(String key, String value); + + /** + * add properties. + * @param properties properties + */ + void addProperties(Properties properties); + + /** + * Tests if the specified object is a key in this NacosClientProperties. + * @param key key ā€“ possible key + * @return true if and only if the specified object is a key in this NacosClientProperties, false otherwise. + */ + boolean containsKey(String key); + + /** + * get properties from NacosClientProperties. + * @return properties + */ + Properties asProperties(); + + /** + * create a new NacosClientProperties which scope is itself. + * @return NacosClientProperties + */ + NacosClientProperties derive(); + + /** + * create a new NacosClientProperties from NacosClientProperties#PROTOTYPE and init. + * @param properties properties + * @return NacosClientProperties + */ + NacosClientProperties derive(Properties properties); } diff --git a/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironmentFactory.java b/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironmentFactory.java deleted file mode 100644 index 74e65a461..000000000 --- a/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironmentFactory.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 1999-2022 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.nacos.client.env; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.Properties; - -class NacosEnvironmentFactory { - - /** - * create nacos environment. - * @return NacosEnvironment's proxy object, it contains a SearchableEnvironment object. - * @see SearchableEnvironment - */ - static NacosEnvironment createEnvironment() { - - return (NacosEnvironment) Proxy.newProxyInstance(NacosEnvironmentFactory.class.getClassLoader(), new Class[] {NacosEnvironment.class}, - new NacosEnvironmentDelegate() { - volatile NacosEnvironment environment; - - @Override - public void init(Properties properties) { - if (environment == null) { - synchronized (NacosEnvironmentFactory.class) { - if (environment == null) { - environment = new SearchableEnvironment(properties); - } - } - } - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (environment == null) { - throw new IllegalStateException( - "Nacos environment doesn't init, please call NacosEnvs#init method then try it again."); - } - return method.invoke(environment, args); - } - }); - } - - interface NacosEnvironmentDelegate extends InvocationHandler { - - /** - * init environment. - * @param properties user customize properties - */ - void init(Properties properties); - } - -} diff --git a/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvs.java b/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvs.java deleted file mode 100644 index 8510e575a..000000000 --- a/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvs.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 1999-2022 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.nacos.client.env; - -import java.lang.reflect.Proxy; -import java.util.Properties; - -/** - * environment utils. - * @author onewe - */ -public class NacosEnvs { - - private static final NacosEnvironment ENVIRONMENT = NacosEnvironmentFactory.createEnvironment(); - - /** - * init environment. - * @param properties properties - */ - public static void init(Properties properties) { - NacosEnvironmentFactory.NacosEnvironmentDelegate warrper = (NacosEnvironmentFactory.NacosEnvironmentDelegate) Proxy.getInvocationHandler( - ENVIRONMENT); - warrper.init(properties); - } - - public static String getProperty(String key, String defaultValue) { - return ENVIRONMENT.getProperty(key, defaultValue); - } - - /** - * get property, if the value can not be got by the special key, the null will be returned. - * - * @param key special key - * @return string value or null. - */ - public static String getProperty(String key) { - return ENVIRONMENT.getProperty(key); - } - - /** - * get boolean, if the value can not be got by the special key, the null will be returned. - * - * @param key special key - * @return boolean value or null. - */ - public static Boolean getBoolean(String key) { - return ENVIRONMENT.getBoolean(key); - } - - /** - * get boolean, if the value can not be got by the special key, the default value will be returned. - * - * @param key special key - * @param defaultValue default value - * @return boolean value or defaultValue. - */ - public static Boolean getBoolean(String key, Boolean defaultValue) { - return ENVIRONMENT.getBoolean(key, defaultValue); - } - - /** - * get integer, if the value can not be got by the special key, the null will be returned. - * - * @param key special key - * @return integer value or null - */ - public static Integer getInteger(String key) { - return ENVIRONMENT.getInteger(key); - } - - /** - * get integer, if the value can not be got by the special key, the default value will be returned. - * - * @param key special key - * @param defaultValue default value - * @return integer value or default value - */ - public static Integer getInteger(String key, Integer defaultValue) { - return ENVIRONMENT.getInteger(key, defaultValue); - } - - /** - * get long, if the value can not be got by the special key, the null will be returned. - * - * @param key special key - * @return long value or null - */ - public static Long getLong(String key) { - return ENVIRONMENT.getLong(key); - } - - /** - * get long, if the value can not be got by the special key, the default value will be returned. - * - * @param key special key - * @param defaultValue default value - * @return long value or default value - */ - public static Long getLong(String key, Long defaultValue) { - return ENVIRONMENT.getLong(key, defaultValue); - } -} diff --git a/client/src/main/java/com/alibaba/nacos/client/env/PropertiesPropertySource.java b/client/src/main/java/com/alibaba/nacos/client/env/PropertiesPropertySource.java index 991f83a4d..6ae9d1fa6 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/PropertiesPropertySource.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/PropertiesPropertySource.java @@ -16,14 +16,23 @@ package com.alibaba.nacos.client.env; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; import java.util.Properties; class PropertiesPropertySource extends AbstractPropertySource { - private final Properties properties; + private final Properties properties = new Properties(); - PropertiesPropertySource(Properties properties) { - this.properties = properties; + private final PropertiesPropertySource parent; + + PropertiesPropertySource() { + this.parent = null; + } + + PropertiesPropertySource(PropertiesPropertySource parent) { + this.parent = parent; } @Override @@ -33,6 +42,67 @@ class PropertiesPropertySource extends AbstractPropertySource { @Override String getProperty(String key) { - return properties.getProperty(key); + return getProperty(this, key); + } + + private String getProperty(PropertiesPropertySource propertiesPropertySource, String key) { + final String value = propertiesPropertySource.properties.getProperty(key); + if (value != null) { + return value; + } + final PropertiesPropertySource parent = propertiesPropertySource.parent; + if (parent == null) { + return null; + } + return getProperty(parent, key); + } + + @Override + boolean containsKey(String key) { + return containsKey(this, key); + } + + boolean containsKey(PropertiesPropertySource propertiesPropertySource, String key) { + final boolean exist = propertiesPropertySource.properties.containsKey(key); + if (exist) { + return true; + } + final PropertiesPropertySource parent = propertiesPropertySource.parent; + if (parent == null) { + return false; + } + return containsKey(parent, key); + } + + @Override + Properties asProperties() { + List propertiesList = new ArrayList<>(8); + + propertiesList = lookForProperties(this, propertiesList); + + Properties ret = new Properties(); + final ListIterator iterator = propertiesList.listIterator(propertiesList.size()); + while (iterator.hasPrevious()) { + final Properties properties = iterator.previous(); + ret.putAll(properties); + } + return ret; + } + + List lookForProperties(PropertiesPropertySource propertiesPropertySource, List propertiesList) { + propertiesList.add(propertiesPropertySource.properties); + final PropertiesPropertySource parent = propertiesPropertySource.parent; + if (parent == null) { + return propertiesList; + } + return lookForProperties(parent, propertiesList); + } + + synchronized void setProperty(String key, String value) { + properties.setProperty(key, value); + } + + synchronized void addProperties(Properties source) { + properties.putAll(source); } } diff --git a/client/src/main/java/com/alibaba/nacos/client/env/PropertySourceSearch.java b/client/src/main/java/com/alibaba/nacos/client/env/PropertySourceSearch.java deleted file mode 100644 index e73dc995a..000000000 --- a/client/src/main/java/com/alibaba/nacos/client/env/PropertySourceSearch.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 1999-2022 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.nacos.client.env; - -import com.alibaba.nacos.client.constant.Constants; -import com.alibaba.nacos.client.env.convert.CompositeConverter; -import com.alibaba.nacos.common.utils.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.stream.Collectors; - -class PropertySourceSearch { - - private static final Logger LOGGER = LoggerFactory.getLogger(PropertySourceSearch.class); - - private static final List DEFAULT_ORDER = Arrays.asList(SourceType.PROPERTIES, SourceType.JVM, - SourceType.SYS); - - private final List propertySources; - - private final CompositeConverter converter; - - private PropertySourceSearch(List propertySources) { - this.propertySources = propertySources; - this.propertySources.add(new DefaultSettingPropertySource()); - this.converter = new CompositeConverter(); - } - - static PropertySourceSearch build(Properties properties) { - if (properties == null) { - properties = new Properties(); - } - PropertiesPropertySource customizePropertySource = new PropertiesPropertySource(properties); - JvmArgsPropertySource jvmArgsPropertySource = new JvmArgsPropertySource(); - SystemEnvPropertySource systemEnvPropertySource = new SystemEnvPropertySource(); - - String searchPattern = jvmArgsPropertySource.getProperty(Constants.SysEnv.NACOS_ENVS_SEARCH); - if (StringUtils.isBlank(searchPattern)) { - searchPattern = systemEnvPropertySource.getProperty(Constants.SysEnv.NACOS_ENVS_SEARCH); - } - - return resolve(searchPattern, customizePropertySource, jvmArgsPropertySource, systemEnvPropertySource); - } - - private static PropertySourceSearch resolve(String pattern, AbstractPropertySource... propertySources) { - - if (StringUtils.isBlank(pattern)) { - return createPropertySourceSearchWithDefaultOrder(propertySources); - } - - try { - final SourceType sourceType = SourceType.valueOf(pattern.toUpperCase()); - return createPropertySourceSearchByFirstType(sourceType, propertySources); - } catch (Exception e) { - LOGGER.error("first source type parse error, it will be use default order!"); - return createPropertySourceSearchWithDefaultOrder(propertySources); - } - } - - private static PropertySourceSearch createPropertySourceSearchWithDefaultOrder(AbstractPropertySource... propertySources) { - final Map sourceMap = Arrays.stream(propertySources) - .collect(Collectors.toMap(AbstractPropertySource::getType, propertySource -> propertySource)); - final List collect = DEFAULT_ORDER.stream().map(sourceMap::get).collect(Collectors.toList()); - return new PropertySourceSearch(collect); - } - - private static PropertySourceSearch createPropertySourceSearchByFirstType(SourceType firstType, - AbstractPropertySource... propertySources) { - - List tempList = new ArrayList<>(4); - tempList.add(firstType); - - final Map sourceMap = Arrays.stream(propertySources) - .collect(Collectors.toMap(AbstractPropertySource::getType, propertySource -> propertySource)); - final List collect = DEFAULT_ORDER.stream().filter(sourceType -> !sourceType.equals(firstType)) - .collect(() -> tempList, List::add, List::addAll).stream().map(sourceMap::get) - .collect(Collectors.toList()); - - return new PropertySourceSearch(collect); - } - - Optional search(String key, Class targetType) { - if (targetType == null) { - throw new IllegalArgumentException("target type must be not null!"); - } - - for (AbstractPropertySource propertySource : propertySources) { - final String value = propertySource.getProperty(key); - if (value != null) { - if (String.class.isAssignableFrom(targetType)) { - return (Optional) Optional.of(value); - } - return Optional.ofNullable(converter.convert(value, targetType)); - } - } - return Optional.empty(); - } -} diff --git a/client/src/main/java/com/alibaba/nacos/client/env/SearchableEnvironment.java b/client/src/main/java/com/alibaba/nacos/client/env/SearchableEnvironment.java deleted file mode 100644 index 7dbb6bc5c..000000000 --- a/client/src/main/java/com/alibaba/nacos/client/env/SearchableEnvironment.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 1999-2022 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.nacos.client.env; - -import java.util.Properties; - -/** - * Searchable environment. - * - * @author onewe - */ -class SearchableEnvironment implements NacosEnvironment { - - private final PropertySourceSearch sourceSearch; - - SearchableEnvironment(Properties properties) { - this.sourceSearch = PropertySourceSearch.build(properties); - } - - @Override - public String getProperty(String key) { - return getProperty(key, null); - } - - @Override - public String getProperty(String key, String defaultValue) { - return sourceSearch.search(key, String.class).orElse(defaultValue); - } - - @Override - public Boolean getBoolean(String key) { - return getBoolean(key, null); - } - - @Override - public Boolean getBoolean(String key, Boolean defaultValue) { - return sourceSearch.search(key, Boolean.class).orElse(defaultValue); - } - - @Override - public Integer getInteger(String key) { - return getInteger(key, null); - } - - @Override - public Integer getInteger(String key, Integer defaultValue) { - return sourceSearch.search(key, Integer.class).orElse(defaultValue); - } - - @Override - public Long getLong(String key) { - return getLong(key, null); - } - - @Override - public Long getLong(String key, Long defaultValue) { - return sourceSearch.search(key, Long.class).orElse(defaultValue); - } - -} diff --git a/client/src/main/java/com/alibaba/nacos/client/env/SearchableProperties.java b/client/src/main/java/com/alibaba/nacos/client/env/SearchableProperties.java new file mode 100644 index 000000000..ac7d5b92f --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/env/SearchableProperties.java @@ -0,0 +1,236 @@ +/* + * Copyright 1999-2022 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.nacos.client.env; + +import com.alibaba.nacos.client.constant.Constants; +import com.alibaba.nacos.client.env.convert.CompositeConverter; +import com.alibaba.nacos.common.utils.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Properties; +import java.util.stream.Collectors; + +/** + * Searchable NacosClientProperties. + * the SearchableProperties that it can be specified search order by + * nacos.env.first + * @author onewe + */ +class SearchableProperties implements NacosClientProperties { + + private static final Logger LOGGER = LoggerFactory.getLogger(SearchableProperties.class); + + private static final JvmArgsPropertySource JVM_ARGS_PROPERTY_SOURCE = new JvmArgsPropertySource(); + + private static final SystemEnvPropertySource SYSTEM_ENV_PROPERTY_SOURCE = new SystemEnvPropertySource(); + + private static final DefaultSettingPropertySource DEFAULT_SETTING_PROPERTY_SOURCE = new DefaultSettingPropertySource(); + + private static final List DEFAULT_ORDER = Arrays.asList(SourceType.PROPERTIES, SourceType.JVM, + SourceType.ENV, SourceType.DEFAULT_SETTING); + + private static final CompositeConverter CONVERTER = new CompositeConverter(); + + static final SearchableProperties INSTANCE = new SearchableProperties(); + + private final List propertySources; + + private final PropertiesPropertySource propertiesPropertySource; + + private SearchableProperties() { + this(new PropertiesPropertySource()); + } + + private SearchableProperties(PropertiesPropertySource propertiesPropertySource) { + this.propertiesPropertySource = propertiesPropertySource; + this.propertySources = build(propertiesPropertySource, + JVM_ARGS_PROPERTY_SOURCE, SYSTEM_ENV_PROPERTY_SOURCE, DEFAULT_SETTING_PROPERTY_SOURCE); + } + + @Override + public String getProperty(String key) { + return getProperty(key, null); + } + + @Override + public String getProperty(String key, String defaultValue) { + return this.search(key, String.class).orElse(defaultValue); + } + + @Override + public Boolean getBoolean(String key) { + return getBoolean(key, null); + } + + @Override + public Boolean getBoolean(String key, Boolean defaultValue) { + return this.search(key, Boolean.class).orElse(defaultValue); + } + + @Override + public Integer getInteger(String key) { + return getInteger(key, null); + } + + @Override + public Integer getInteger(String key, Integer defaultValue) { + return this.search(key, Integer.class).orElse(defaultValue); + } + + @Override + public Long getLong(String key) { + return getLong(key, null); + } + + @Override + public Long getLong(String key, Long defaultValue) { + return this.search(key, Long.class).orElse(defaultValue); + } + + @Override + public void setProperty(String key, String value) { + propertiesPropertySource.setProperty(key, value); + } + + @Override + public void addProperties(Properties properties) { + propertiesPropertySource.addProperties(properties); + } + + @Override + public Properties asProperties() { + Properties properties = new Properties(); + final ListIterator iterator = propertySources.listIterator( + propertySources.size()); + while (iterator.hasPrevious()) { + final AbstractPropertySource previous = iterator.previous(); + properties.putAll(previous.asProperties()); + } + return properties; + } + + @Override + public boolean containsKey(String key) { + for (AbstractPropertySource propertySource : propertySources) { + final boolean containing = propertySource.containsKey(key); + if (containing) { + return true; + } + } + return false; + } + + private Optional search(String key, Class targetType) { + if (targetType == null) { + throw new IllegalArgumentException("target type must not be null!"); + } + + for (AbstractPropertySource propertySource : propertySources) { + final String value = propertySource.getProperty(key); + if (value != null) { + if (String.class.isAssignableFrom(targetType)) { + try { + return (Optional) Optional.of(value); + } catch (Exception e) { + LOGGER.error("target type convert error", e); + return Optional.empty(); + } + + } + return Optional.ofNullable(CONVERTER.convert(value, targetType)); + } + } + return Optional.empty(); + } + + private List build(AbstractPropertySource... propertySources) { + + String firstEnv = JVM_ARGS_PROPERTY_SOURCE.getProperty(Constants.SysEnv.NACOS_ENV_FIRST); + if (StringUtils.isBlank(firstEnv)) { + firstEnv = SYSTEM_ENV_PROPERTY_SOURCE.getProperty(Constants.SysEnv.NACOS_ENV_FIRST); + } + + if (StringUtils.isBlank(firstEnv)) { + return sortPropertySourceDefaultOrder(propertySources); + } + + try { + final SourceType sourceType = SourceType.valueOf(firstEnv.toUpperCase()); + if (SourceType.DEFAULT_SETTING.equals(sourceType) || SourceType.PROPERTIES.equals(sourceType)) { + return sortPropertySourceDefaultOrder(propertySources); + } + return sortPropertySource(sourceType, propertySources); + } catch (Exception e) { + LOGGER.error("first source type parse error, it will be used default order!", e); + return sortPropertySourceDefaultOrder(propertySources); + } + } + + private List sortPropertySourceDefaultOrder( + AbstractPropertySource... propertySources) { + final Map sourceMap = Arrays.stream(propertySources) + .collect(Collectors.toMap(AbstractPropertySource::getType, propertySource -> propertySource)); + final List collect = DEFAULT_ORDER.stream().map(sourceMap::get) + .collect(Collectors.toList()); + LOGGER.info("properties search order:PROPERTIES->JVM->ENV->DEFAULT_SETTING"); + return collect; + } + + private List sortPropertySource(SourceType firstType, + AbstractPropertySource... propertySources) { + List tempList = new ArrayList<>(4); + tempList.add(firstType); + + final Map sourceMap = Arrays.stream(propertySources) + .collect(Collectors.toMap(AbstractPropertySource::getType, propertySource -> propertySource)); + final List collect = DEFAULT_ORDER.stream() + .filter(sourceType -> !sourceType.equals(firstType)).collect(() -> tempList, List::add, List::addAll) + .stream().map(sourceMap::get).filter(Objects::nonNull).collect(Collectors.toList()); + + StringBuilder orderInfo = new StringBuilder("properties search order:"); + for (int i = 0; i < collect.size(); i++) { + final AbstractPropertySource abstractPropertySource = collect.get(i); + orderInfo.append(abstractPropertySource.getType().toString()); + if (i < collect.size() - 1) { + orderInfo.append("->"); + } + } + LOGGER.info(orderInfo.toString()); + + return collect; + } + + @Override + public NacosClientProperties derive() { + return new SearchableProperties(new PropertiesPropertySource(this.propertiesPropertySource)); + } + + @Override + public NacosClientProperties derive(Properties properties) { + final NacosClientProperties nacosClientProperties = this.derive(); + nacosClientProperties.addProperties(properties); + return nacosClientProperties; + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/env/SourceType.java b/client/src/main/java/com/alibaba/nacos/client/env/SourceType.java index 8a5d1096b..4727199c6 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/SourceType.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/SourceType.java @@ -18,17 +18,17 @@ package com.alibaba.nacos.client.env; enum SourceType { /** - * get value from system environment. + * get value from properties. */ - SYS, + PROPERTIES, /** * get value from jvm args. */ JVM, /** - * get value from properties. + * get value from system environment. */ - PROPERTIES, + ENV, /** * get value from default setting. */ diff --git a/client/src/main/java/com/alibaba/nacos/client/env/SystemEnvPropertySource.java b/client/src/main/java/com/alibaba/nacos/client/env/SystemEnvPropertySource.java index 6aee164da..d8689f130 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/SystemEnvPropertySource.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/SystemEnvPropertySource.java @@ -17,18 +17,15 @@ package com.alibaba.nacos.client.env; import java.util.Map; +import java.util.Properties; class SystemEnvPropertySource extends AbstractPropertySource { - private final Map env; - - SystemEnvPropertySource() { - this.env = System.getenv(); - } + private final Map env = System.getenv(); @Override SourceType getType() { - return SourceType.SYS; + return SourceType.ENV; } @Override @@ -78,7 +75,15 @@ class SystemEnvPropertySource extends AbstractPropertySource { return null; } - private boolean containsKey(String name) { + @Override + boolean containsKey(String name) { return this.env.containsKey(name); } + + @Override + Properties asProperties() { + Properties properties = new Properties(); + properties.putAll(this.env); + return properties; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java index 5a9a37598..bed4737e1 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java @@ -156,6 +156,13 @@ public class NacosNamingService implements NamingService { clientProxy.batchRegisterService(serviceName, groupName, instances); } + @Override + public void batchDeregisterInstance(String serviceName, String groupName, List instances) + throws NacosException { + NamingUtils.batchCheckInstanceIsLegal(instances); + clientProxy.batchDeregisterService(serviceName, groupName, instances); + } + @Override public void deregisterInstance(String serviceName, String ip, int port) throws NacosException { deregisterInstance(serviceName, ip, port, Constants.DEFAULT_CLUSTER_NAME); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/core/ServiceInfoUpdateService.java b/client/src/main/java/com/alibaba/nacos/client/naming/core/ServiceInfoUpdateService.java index ddb0d619f..386feeecd 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/core/ServiceInfoUpdateService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/core/ServiceInfoUpdateService.java @@ -61,8 +61,11 @@ public class ServiceInfoUpdateService implements Closeable { private final InstancesChangeNotifier changeNotifier; + private final boolean asyncQuerySubscribeService; + public ServiceInfoUpdateService(Properties properties, ServiceInfoHolder serviceInfoHolder, NamingClientProxy namingClientProxy, InstancesChangeNotifier changeNotifier) { + this.asyncQuerySubscribeService = isAsyncQueryForSubscribeService(properties); this.executor = new ScheduledThreadPoolExecutor(initPollingThreadCount(properties), new NameThreadFactory("com.alibaba.nacos.client.naming.updater")); this.serviceInfoHolder = serviceInfoHolder; @@ -70,6 +73,13 @@ public class ServiceInfoUpdateService implements Closeable { this.changeNotifier = changeNotifier; } + private boolean isAsyncQueryForSubscribeService(Properties properties) { + if (properties == null || !properties.containsKey(PropertyKeyConst.NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE)) { + return true; + } + return ConvertUtils.toBoolean(properties.getProperty(PropertyKeyConst.NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE), true); + } + private int initPollingThreadCount(Properties properties) { if (properties == null) { return UtilAndComs.DEFAULT_POLLING_THREAD_COUNT; @@ -86,6 +96,9 @@ public class ServiceInfoUpdateService implements Closeable { * @param clusters clusters */ public void scheduleUpdateIfAbsent(String serviceName, String groupName, String clusters) { + if (!asyncQuerySubscribeService) { + return; + } String serviceKey = ServiceInfo.getKey(NamingUtils.getGroupedName(serviceName, groupName), clusters); if (futureMap.get(serviceKey) != null) { return; @@ -193,9 +206,10 @@ public class ServiceInfoUpdateService implements Closeable { // TODO multiple time can be configured. delayTime = serviceObj.getCacheMillis() * DEFAULT_UPDATE_CACHE_TIME_MULTIPLE; resetFailCount(); + } catch (NacosException e) { + handleNacosException(e); } catch (Throwable e) { - incFailCount(); - NAMING_LOGGER.warn("[NA] failed to update serviceName: {}", groupedServiceName, e); + handleUnknownException(e); } finally { if (!isCancel) { executor.schedule(this, Math.min(delayTime << failCount, DEFAULT_DELAY * 60), @@ -204,6 +218,20 @@ public class ServiceInfoUpdateService implements Closeable { } } + private void handleNacosException(NacosException e) { + incFailCount(); + int errorCode = e.getErrCode(); + if (NacosException.SERVER_ERROR == errorCode) { + handleUnknownException(e); + } + NAMING_LOGGER.warn("Can't update serviceName: {}, reason: {}", groupedServiceName, e.getErrMsg()); + } + + private void handleUnknownException(Throwable throwable) { + incFailCount(); + NAMING_LOGGER.warn("[NA] failed to update serviceName: {}", groupedServiceName, throwable); + } + private void incFailCount() { int limit = 6; if (failCount == limit) { diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxy.java index c46adf324..f24cdda35 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxy.java @@ -55,6 +55,17 @@ public interface NamingClientProxy extends Closeable { */ void batchRegisterService(String serviceName, String groupName, List instances) throws NacosException; + /** + * Batch register instance to service with specified instance properties. + * + * @param serviceName service name + * @param groupName group name + * @param instances deRegister instance + * @throws NacosException nacos exception + * @since 2.2.0 + */ + void batchDeregisterService(String serviceName, String groupName, List instances) throws NacosException; + /** * Deregister instance from a service. * diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java index f7af0cd6c..f3b65ac6b 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java @@ -107,6 +107,17 @@ public class NamingClientProxyDelegate implements NamingClientProxy { NAMING_LOGGER.info("batchRegisterInstance instances: {} ,serviceName: {} finish.", instances, serviceName); } + @Override + public void batchDeregisterService(String serviceName, String groupName, List instances) + throws NacosException { + NAMING_LOGGER.info("batch DeregisterInstance instances: {} ,serviceName: {} begin.", instances, serviceName); + if (CollectionUtils.isEmpty(instances)) { + NAMING_LOGGER.warn("batch DeregisterInstance instances is Empty:{}", instances); + } + grpcClientProxy.batchDeregisterService(serviceName, groupName, instances); + NAMING_LOGGER.info("batch DeregisterInstance instances: {} ,serviceName: {} finish.", instances, serviceName); + } + @Override public void deregisterService(String serviceName, String groupName, Instance instance) throws NacosException { getExecuteClientProxy(instance).deregisterService(serviceName, groupName, instance); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java index d7c739d98..85adb4008 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java @@ -33,6 +33,7 @@ import com.alibaba.nacos.api.naming.remote.response.BatchInstanceResponse; import com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse; import com.alibaba.nacos.api.naming.remote.response.ServiceListResponse; import com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse; +import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; @@ -42,6 +43,8 @@ import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.event.ServerListChangedEvent; import com.alibaba.nacos.client.naming.remote.AbstractNamingClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; +import com.alibaba.nacos.client.naming.remote.gprc.redo.data.BatchInstanceRedoData; +import com.alibaba.nacos.client.naming.remote.gprc.redo.data.InstanceRedoData; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; @@ -49,14 +52,18 @@ import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.remote.client.RpcClientFactory; import com.alibaba.nacos.common.remote.client.ServerListFactory; +import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.JacksonUtils; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; @@ -124,6 +131,50 @@ public class NamingGrpcClientProxy extends AbstractNamingClientProxy { doBatchRegisterService(serviceName, groupName, instances); } + @Override + public void batchDeregisterService(String serviceName, String groupName, List instances) + throws NacosException { + List retainInstance = getRetainInstance(serviceName, groupName, instances); + batchRegisterService(serviceName, groupName, retainInstance); + } + + /** + * Get instance list that need to be deregistered. + * @param serviceName service name + * @param groupName group name + * @param instances instance list + * @return instance list that need to be deregistered. + */ + private List getRetainInstance(String serviceName, String groupName, List instances) throws NacosException { + if (CollectionUtils.isEmpty(instances)) { + throw new NacosException(NacosException.INVALID_PARAM, + String.format("[Batch deRegistration] need deRegister instance is empty, instances: %s,", instances)); + } + String combinedServiceName = NamingUtils.getGroupedName(serviceName, groupName); + InstanceRedoData instanceRedoData = redoService.getRegisteredInstancesBykey(combinedServiceName); + if (!(instanceRedoData instanceof BatchInstanceRedoData)) { + throw new NacosException(NacosException.INVALID_PARAM, + String.format("[Batch deRegistration] batch deRegister is not BatchInstanceRedoData type , instances: %s,", instances)); + } + + BatchInstanceRedoData batchInstanceRedoData = (BatchInstanceRedoData) instanceRedoData; + List allInstance = batchInstanceRedoData.getInstances(); + if (CollectionUtils.isEmpty(allInstance)) { + throw new NacosException(NacosException.INVALID_PARAM, + String.format("[Batch deRegistration] not found all registerInstance , serviceNameļ¼š%s , groupName: %s", serviceName, groupName)); + } + + Map instanceMap = instances + .stream().collect(Collectors.toMap(Function.identity(), Function.identity())); + List retainInstances = new ArrayList<>(); + for (Instance instance : allInstance) { + if (!instanceMap.containsKey(instance)) { + retainInstances.add(instance); + } + } + return retainInstances; + } + /** * Execute batch register operation. * @@ -280,7 +331,7 @@ public class NamingGrpcClientProxy extends AbstractNamingClientProxy { * @throws NacosException nacos exception */ public void doUnsubscribe(String serviceName, String groupName, String clusters) throws NacosException { - SubscribeServiceRequest request = new SubscribeServiceRequest(namespaceId, serviceName, groupName, clusters, + SubscribeServiceRequest request = new SubscribeServiceRequest(namespaceId, groupName, serviceName, clusters, false); requestToServer(request, SubscribeServiceResponse.class); redoService.removeSubscriberForRedo(serviceName, groupName, clusters); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java index 4b68b6e3b..a355999c8 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java @@ -277,6 +277,14 @@ public class NamingGrpcRedoService implements ConnectionEventListener { return result; } + /** + * get Cache service. + * @return cache service + */ + public InstanceRedoData getRegisteredInstancesBykey(String combinedServiceName) { + return registeredInstances.get(combinedServiceName); + } + /** * Shutdown redo service. */ diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java index 2622d0f8c..3f6c5f4a7 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java @@ -172,6 +172,12 @@ public class NamingHttpClientProxy extends AbstractNamingClientProxy { throw new UnsupportedOperationException("Do not support persistent instances to perform batch registration methods."); } + @Override + public void batchDeregisterService(String serviceName, String groupName, List instances) + throws NacosException { + throw new UnsupportedOperationException("Do not support persistent instances to perform batch de registration methods."); + } + @Override public void deregisterService(String serviceName, String groupName, Instance instance) throws NacosException { NAMING_LOGGER diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/utils/InitUtils.java b/client/src/main/java/com/alibaba/nacos/client/naming/utils/InitUtils.java index 7bbd9fbe4..e147a2edd 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/utils/InitUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/utils/InitUtils.java @@ -95,21 +95,6 @@ public class InitUtils { UtilAndComs.nacosUrlBase = UtilAndComs.webContext + "/v1/ns"; UtilAndComs.nacosUrlInstance = UtilAndComs.nacosUrlBase + "/instance"; }); - initWebRootContext(); - } - - /** - * Init web root context. - */ - @Deprecated - public static void initWebRootContext() { - // support the web context with ali-yun if the app deploy by EDAS - final String webContext = System.getProperty(SystemPropertyKeyConst.NAMING_WEB_CONTEXT); - TemplateUtils.stringNotEmptyAndThenExecute(webContext, () -> { - UtilAndComs.webContext = ContextPathUtil.normalizeContextPath(webContext); - UtilAndComs.nacosUrlBase = UtilAndComs.webContext + "/v1/ns"; - UtilAndComs.nacosUrlInstance = UtilAndComs.nacosUrlBase + "/instance"; - }); } /** diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/utils/UtilAndComs.java b/client/src/main/java/com/alibaba/nacos/client/naming/utils/UtilAndComs.java index 39a3b0b12..815f0c7b9 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/utils/UtilAndComs.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/utils/UtilAndComs.java @@ -17,7 +17,6 @@ package com.alibaba.nacos.client.naming.utils; import com.alibaba.nacos.common.utils.ThreadUtils; -import com.alibaba.nacos.common.utils.VersionUtils; /** * Util and constants. @@ -26,10 +25,6 @@ import com.alibaba.nacos.common.utils.VersionUtils; */ public class UtilAndComs { - // using com.alibaba.nacos.common.utils.VersionUtils.getFullClientVersion instead. - @Deprecated - public static final String VERSION = VersionUtils.getFullClientVersion(); - public static String webContext = "/nacos"; public static String nacosUrlBase = webContext + "/v1/ns"; diff --git a/client/src/test/java/com/alibaba/nacos/client/auth/ram/identify/CredentialServiceTest.java b/client/src/test/java/com/alibaba/nacos/client/auth/ram/identify/CredentialServiceTest.java index 9d5447e78..815275997 100644 --- a/client/src/test/java/com/alibaba/nacos/client/auth/ram/identify/CredentialServiceTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/auth/ram/identify/CredentialServiceTest.java @@ -116,31 +116,4 @@ public class CredentialServiceTest extends TestCase { Assert.assertEquals(expect, actual); } - - @Test - public void testGetAkAndSk() { - CredentialService credentialService1 = CredentialService.getInstance(); - Credentials c = new Credentials(); - c.setAccessKey("ak"); - c.setSecretKey("sk"); - credentialService1.setCredential(c); - - Assert.assertEquals("ak", credentialService1.getAccessKey()); - Assert.assertEquals("sk", credentialService1.getSecretKey()); - } - - @Test - public void testSetSecretKey() { - CredentialService credentialService1 = CredentialService.getInstance(); - Credentials c = new Credentials(); - c.setAccessKey("ak"); - c.setSecretKey("sk"); - credentialService1.setCredential(c); - credentialService1.setAccessKey("ak1"); - credentialService1.setSecretKey("sk1"); - - Assert.assertEquals("ak1", credentialService1.getAccessKey()); - Assert.assertEquals("sk1", credentialService1.getSecretKey()); - } - } diff --git a/client/src/test/java/com/alibaba/nacos/client/config/filter/impl/ConfigFilterChainManagerTest.java b/client/src/test/java/com/alibaba/nacos/client/config/filter/impl/ConfigFilterChainManagerTest.java index 1d62d2437..ef74f305b 100644 --- a/client/src/test/java/com/alibaba/nacos/client/config/filter/impl/ConfigFilterChainManagerTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/config/filter/impl/ConfigFilterChainManagerTest.java @@ -21,7 +21,6 @@ import com.alibaba.nacos.api.config.filter.IConfigFilter; import com.alibaba.nacos.api.config.filter.IConfigFilterChain; import com.alibaba.nacos.api.config.filter.IConfigRequest; import com.alibaba.nacos.api.config.filter.IConfigResponse; -import com.alibaba.nacos.api.config.filter.IFilterConfig; import com.alibaba.nacos.api.exception.NacosException; import org.junit.Assert; import org.junit.Test; @@ -43,11 +42,7 @@ public class ConfigFilterChainManagerTest { this.name = name; this.order = order; } - - @Override - public void init(IFilterConfig filterConfig) { - } - + @Override public void init(Properties properties) { } @@ -135,4 +130,4 @@ public class ConfigFilterChainManagerTest { Assert.assertEquals(2, configContext.getParameter("filterCount")); } -} \ No newline at end of file +} diff --git a/client/src/test/java/com/alibaba/nacos/client/config/filter/impl/DemoFilter1.java b/client/src/test/java/com/alibaba/nacos/client/config/filter/impl/DemoFilter1.java index da1e15db6..8d1493508 100644 --- a/client/src/test/java/com/alibaba/nacos/client/config/filter/impl/DemoFilter1.java +++ b/client/src/test/java/com/alibaba/nacos/client/config/filter/impl/DemoFilter1.java @@ -20,7 +20,6 @@ import com.alibaba.nacos.api.config.filter.IConfigFilter; import com.alibaba.nacos.api.config.filter.IConfigFilterChain; import com.alibaba.nacos.api.config.filter.IConfigRequest; import com.alibaba.nacos.api.config.filter.IConfigResponse; -import com.alibaba.nacos.api.config.filter.IFilterConfig; import com.alibaba.nacos.api.exception.NacosException; import java.util.Properties; @@ -29,11 +28,6 @@ public class DemoFilter1 implements IConfigFilter { private static final String DEFAULT_NAME = DemoFilter1.class.getName(); - @Override - public void init(IFilterConfig filterConfig) { - - } - @Override public void init(Properties properties) { diff --git a/client/src/test/java/com/alibaba/nacos/client/config/filter/impl/DemoFilter2.java b/client/src/test/java/com/alibaba/nacos/client/config/filter/impl/DemoFilter2.java index 3cc6f4221..9c876395d 100644 --- a/client/src/test/java/com/alibaba/nacos/client/config/filter/impl/DemoFilter2.java +++ b/client/src/test/java/com/alibaba/nacos/client/config/filter/impl/DemoFilter2.java @@ -20,7 +20,6 @@ import com.alibaba.nacos.api.config.filter.IConfigFilter; import com.alibaba.nacos.api.config.filter.IConfigFilterChain; import com.alibaba.nacos.api.config.filter.IConfigRequest; import com.alibaba.nacos.api.config.filter.IConfigResponse; -import com.alibaba.nacos.api.config.filter.IFilterConfig; import com.alibaba.nacos.api.exception.NacosException; import java.util.Properties; @@ -29,11 +28,6 @@ public class DemoFilter2 implements IConfigFilter { private static final String DEFAULT_NAME = DemoFilter2.class.getName(); - @Override - public void init(IFilterConfig filterConfig) { - - } - @Override public void init(Properties properties) { diff --git a/client/src/test/java/com/alibaba/nacos/client/env/NacosClientPropertiesTest.java b/client/src/test/java/com/alibaba/nacos/client/env/NacosClientPropertiesTest.java new file mode 100644 index 000000000..6e0c361e1 --- /dev/null +++ b/client/src/test/java/com/alibaba/nacos/client/env/NacosClientPropertiesTest.java @@ -0,0 +1,308 @@ +/* + * Copyright 1999-2022 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.nacos.client.env; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Properties; + +public class NacosClientPropertiesTest { + + @BeforeClass + public static void init() { + System.setProperty("nacos.env.first", "jvm"); + } + + @AfterClass + public static void teardown() { + System.clearProperty("nacos.env.first"); + } + + @Test + public void testGetProperty() { + NacosClientProperties.PROTOTYPE.setProperty("nacos.home", "/home/nacos"); + final String value = NacosClientProperties.PROTOTYPE.getProperty("nacos.home"); + Assert.assertEquals("/home/nacos", value); + } + + @Test + public void testGetPropertyMultiLayer() { + + NacosClientProperties.PROTOTYPE.setProperty("top.layer", "top"); + + final NacosClientProperties layerAEnv = NacosClientProperties.PROTOTYPE.derive(); + layerAEnv.setProperty("a.layer", "a"); + + final NacosClientProperties layerBEnv = layerAEnv.derive(); + layerBEnv.setProperty("b.layer", "b"); + + final NacosClientProperties layerCEnv = layerBEnv.derive(); + layerCEnv.setProperty("c.layer", "c"); + + String value = layerCEnv.getProperty("c.layer"); + Assert.assertEquals("c", value); + + value = layerCEnv.getProperty("b.layer"); + Assert.assertEquals("b", value); + + value = layerCEnv.getProperty("a.layer"); + Assert.assertEquals("a", value); + + value = layerCEnv.getProperty("top.layer"); + Assert.assertEquals("top", value); + } + + @Test + public void testGetPropertyDefaultValue() { + final String value = NacosClientProperties.PROTOTYPE.getProperty("nacos.home.default", "/home/default_value"); + Assert.assertEquals("/home/default_value", value); + } + + @Test + public void testGetBoolean() { + NacosClientProperties.PROTOTYPE.setProperty("use.cluster", "true"); + final Boolean value = NacosClientProperties.PROTOTYPE.getBoolean("use.cluster"); + Assert.assertTrue(value); + } + + @Test + public void testGetBooleanDefaultValue() { + final Boolean value = NacosClientProperties.PROTOTYPE.getBoolean("use.cluster.default", false); + Assert.assertFalse(value); + } + + @Test + public void testGetInteger() { + NacosClientProperties.PROTOTYPE.setProperty("max.timeout", "200"); + final Integer value = NacosClientProperties.PROTOTYPE.getInteger("max.timeout"); + Assert.assertEquals(200, value.intValue()); + } + + @Test + public void testGetIntegerDefaultValue() { + final Integer value = NacosClientProperties.PROTOTYPE.getInteger("max.timeout.default", 400); + Assert.assertEquals(400, value.intValue()); + } + + @Test + public void testGetLong() { + NacosClientProperties.PROTOTYPE.setProperty("connection.timeout", "200"); + final Long value = NacosClientProperties.PROTOTYPE.getLong("connection.timeout"); + Assert.assertEquals(200L, value.longValue()); + } + + @Test + public void testGetLongDefault() { + final Long value = NacosClientProperties.PROTOTYPE.getLong("connection.timeout.default", 400L); + Assert.assertEquals(400L, value.longValue()); + } + + @Test + public void testGetPropertyDefaultSetting() { + + final String value = NacosClientProperties.PROTOTYPE.getProperty("nacos.home.default.test"); + Assert.assertEquals("/home/default_setting", value); + } + + @Test + public void setProperty() { + NacosClientProperties.PROTOTYPE.setProperty("nacos.set.property", "true"); + final String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.set.property"); + Assert.assertEquals("true", ret); + } + + @Test + public void setPropertyWithScope() { + + final NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); + properties.setProperty("nacos.set.property.scope", "config"); + + String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.set.property.scope"); + Assert.assertNull(ret); + + ret = properties.getProperty("nacos.set.property.scope"); + Assert.assertEquals("config", ret); + } + + @Test + public void testAddProperties() { + Properties properties = new Properties(); + properties.setProperty("nacos.add.properties", "true"); + + NacosClientProperties.PROTOTYPE.addProperties(properties); + + final String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.add.properties"); + + Assert.assertEquals("true", ret); + } + + @Test + public void testAddPropertiesWithScope() { + + Properties properties = new Properties(); + properties.setProperty("nacos.add.properties.scope", "config"); + + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(); + nacosClientProperties.addProperties(properties); + + String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.add.properties.scope"); + Assert.assertNull(ret); + + ret = nacosClientProperties.getProperty("nacos.add.properties.scope"); + Assert.assertEquals("config", ret); + + } + + @Test + public void testTestDerive() { + Properties properties = new Properties(); + properties.setProperty("nacos.derive.properties.scope", "derive"); + + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties); + + final String value = nacosClientProperties.getProperty("nacos.derive.properties.scope"); + + Assert.assertEquals("derive", value); + + } + + @Test + public void testContainsKey() { + NacosClientProperties.PROTOTYPE.setProperty("nacos.contains.key", "true"); + + boolean ret = NacosClientProperties.PROTOTYPE.containsKey("nacos.contains.key"); + Assert.assertTrue(ret); + + ret = NacosClientProperties.PROTOTYPE.containsKey("nacos.contains.key.in.sys"); + Assert.assertFalse(ret); + } + + @Test + public void testContainsKeyMultiLayers() { + + NacosClientProperties.PROTOTYPE.setProperty("top.layer", "top"); + + final NacosClientProperties layerAEnv = NacosClientProperties.PROTOTYPE.derive(); + layerAEnv.setProperty("a.layer", "a"); + + final NacosClientProperties layerBEnv = layerAEnv.derive(); + layerBEnv.setProperty("b.layer", "b"); + + final NacosClientProperties layerCEnv = layerBEnv.derive(); + layerCEnv.setProperty("c.layer", "c"); + + boolean exist = layerCEnv.containsKey("c.layer"); + Assert.assertTrue(exist); + + exist = layerCEnv.containsKey("b.layer"); + Assert.assertTrue(exist); + + exist = layerCEnv.containsKey("a.layer"); + Assert.assertTrue(exist); + + exist = layerCEnv.containsKey("top.layer"); + Assert.assertTrue(exist); + + } + + @Test + public void testContainsKeyWithScope() { + NacosClientProperties.PROTOTYPE.setProperty("nacos.contains.global.scope", "global"); + final NacosClientProperties namingProperties = NacosClientProperties.PROTOTYPE.derive(); + namingProperties.setProperty("nacos.contains.naming.scope", "naming"); + + boolean ret = NacosClientProperties.PROTOTYPE.containsKey("nacos.contains.global.scope"); + Assert.assertTrue(ret); + + ret = NacosClientProperties.PROTOTYPE.containsKey("nacos.contains.naming.scope"); + Assert.assertFalse(ret); + + ret = namingProperties.containsKey("nacos.contains.naming.scope"); + Assert.assertTrue(ret); + + ret = namingProperties.containsKey("nacos.contains.global.scope"); + Assert.assertTrue(ret); + + } + + @Test + public void testAsProperties() { + NacosClientProperties.PROTOTYPE.setProperty("nacos.as.properties", "true"); + final Properties properties = NacosClientProperties.PROTOTYPE.asProperties(); + Assert.assertNotNull(properties); + Assert.assertEquals("true", properties.getProperty("nacos.as.properties")); + } + + @Test + public void testAsPropertiesWithScope() { + + NacosClientProperties.PROTOTYPE.setProperty("nacos.as.properties.global.scope", "global"); + NacosClientProperties.PROTOTYPE.setProperty("nacos.server.addr.scope", "global"); + + final NacosClientProperties configProperties = NacosClientProperties.PROTOTYPE.derive(); + configProperties.setProperty("nacos.server.addr.scope", "config"); + + final Properties properties = configProperties.asProperties(); + Assert.assertNotNull(properties); + + String ret = properties.getProperty("nacos.as.properties.global.scope"); + Assert.assertEquals("global", ret); + + ret = properties.getProperty("nacos.server.addr.scope"); + Assert.assertEquals("config", ret); + } + + @Test + public void testGetPropertyWithScope() { + + NacosClientProperties.PROTOTYPE.setProperty("nacos.global.scope", "global"); + + final NacosClientProperties configProperties = NacosClientProperties.PROTOTYPE.derive(); + configProperties.setProperty("nacos.config.scope", "config"); + + final NacosClientProperties namingProperties = NacosClientProperties.PROTOTYPE.derive(); + namingProperties.setProperty("nacos.naming.scope", "naming"); + + String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.global.scope"); + Assert.assertEquals("global", ret); + + ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.config.scope"); + Assert.assertNull(ret); + + ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.naming.scope"); + Assert.assertNull(ret); + + ret = configProperties.getProperty("nacos.config.scope"); + Assert.assertEquals("config", ret); + ret = configProperties.getProperty("nacos.global.scope"); + Assert.assertEquals("global", ret); + ret = configProperties.getProperty("nacos.naming.scope"); + Assert.assertNull(ret); + + ret = namingProperties.getProperty("nacos.naming.scope"); + Assert.assertEquals("naming", ret); + ret = namingProperties.getProperty("nacos.global.scope"); + Assert.assertEquals("global", ret); + ret = namingProperties.getProperty("nacos.config.scope"); + Assert.assertNull(ret); + + } + +} diff --git a/client/src/test/java/com/alibaba/nacos/client/env/NacosEnvironmentFactoryTest.java b/client/src/test/java/com/alibaba/nacos/client/env/NacosEnvironmentFactoryTest.java deleted file mode 100644 index 13bda0fab..000000000 --- a/client/src/test/java/com/alibaba/nacos/client/env/NacosEnvironmentFactoryTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 1999-2022 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.nacos.client.env; - -import org.junit.Assert; -import org.junit.Test; - -import java.lang.reflect.Proxy; -import java.util.Properties; - -public class NacosEnvironmentFactoryTest { - - @Test(expected = IllegalStateException.class) - public void testCreateEnvironment() { - final NacosEnvironment environment = NacosEnvironmentFactory.createEnvironment(); - Assert.assertNotNull(environment); - Assert.assertTrue(Proxy.isProxyClass(environment.getClass())); - environment.getProperty("test.exception"); - } - - @Test - public void testNacosEnvInit() { - final NacosEnvironment environment = NacosEnvironmentFactory.createEnvironment(); - final NacosEnvironmentFactory.NacosEnvironmentDelegate invocationHandler = - (NacosEnvironmentFactory.NacosEnvironmentDelegate) Proxy.getInvocationHandler( - environment); - Properties properties = new Properties(); - properties.setProperty("init.nacos", "true"); - - invocationHandler.init(properties); - - final String property = environment.getProperty("init.nacos"); - Assert.assertEquals("true", property); - } - -} diff --git a/client/src/test/java/com/alibaba/nacos/client/env/NacosEnvsTest.java b/client/src/test/java/com/alibaba/nacos/client/env/NacosEnvsTest.java deleted file mode 100644 index 28ed1e6dd..000000000 --- a/client/src/test/java/com/alibaba/nacos/client/env/NacosEnvsTest.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 1999-2022 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.nacos.client.env; - -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.MockedStatic; -import org.mockito.Mockito; - -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.Properties; - -public class NacosEnvsTest { - - static MockedStatic mockedStatic; - - @BeforeClass - public static void before() { - mockedStatic = Mockito.mockStatic(NacosEnvironmentFactory.class); - mockedStatic.when(NacosEnvironmentFactory::createEnvironment).thenReturn(createProxy()); - - } - - @AfterClass - public static void teardown() { - if (mockedStatic != null) { - mockedStatic.close(); - } - } - - private static NacosEnvironment createProxy() { - return (NacosEnvironment) Proxy.newProxyInstance(NacosEnvironmentFactory.class.getClassLoader(), - new Class[] {NacosEnvironment.class}, new NacosEnvironmentFactory.NacosEnvironmentDelegate() { - volatile NacosEnvironment environment; - - @Override - public void init(Properties properties) { - environment = new SearchableEnvironment(properties); - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (environment == null) { - throw new IllegalStateException( - "Nacos environment doesn't init, please call NEnvs#init method then try it again."); - } - return method.invoke(environment, args); - } - }); - } - - @Test - public void testGetProperty() { - - final Properties properties = new Properties(); - properties.setProperty("nacos.home", "/home/nacos"); - NacosEnvs.init(properties); - final String value = NacosEnvs.getProperty("nacos.home"); - - Assert.assertEquals("/home/nacos", value); - } - - @Test - public void testGetPropertyDefaultValue() { - - final Properties properties = new Properties(); - NacosEnvs.init(properties); - final String value = NacosEnvs.getProperty("nacos.home", "/home/default_value"); - - Assert.assertEquals("/home/default_value", value); - } - - @Test - public void testGetBoolean() { - final Properties properties = new Properties(); - properties.setProperty("use.cluster", "true"); - NacosEnvs.init(properties); - - final Boolean value = NacosEnvs.getBoolean("use.cluster"); - Assert.assertTrue(value); - } - - @Test - public void testGetBooleanDefaultValue() { - final Properties properties = new Properties(); - NacosEnvs.init(properties); - - final Boolean value = NacosEnvs.getBoolean("use.cluster", false); - Assert.assertFalse(value); - } - - @Test - public void testGetInteger() { - final Properties properties = new Properties(); - properties.setProperty("max.timeout", "200"); - NacosEnvs.init(properties); - - final Integer value = NacosEnvs.getInteger("max.timeout"); - - Assert.assertEquals(200, value.intValue()); - } - - @Test - public void testGetIntegerDefaultValue() { - final Properties properties = new Properties(); - NacosEnvs.init(properties); - - final Integer value = NacosEnvs.getInteger("max.timeout", 400); - Assert.assertEquals(400, value.intValue()); - } - - @Test - public void testGetLong() { - final Properties properties = new Properties(); - properties.setProperty("connection.timeout", "200"); - NacosEnvs.init(properties); - - final Long value = NacosEnvs.getLong("connection.timeout"); - Assert.assertEquals(200L, value.longValue()); - } - - @Test - public void testGetLongDefault() { - final Properties properties = new Properties(); - NacosEnvs.init(properties); - final Long value = NacosEnvs.getLong("connection.timeout", 400L); - Assert.assertEquals(400L, value.longValue()); - } - - @Test - public void testGetPropertyJvmFirst() { - System.setProperty("nacos.envs.search", "jvm"); - System.setProperty("nacos.home", "/home/jvm_first"); - - Properties properties = new Properties(); - properties.setProperty("nacos.home", "/home/properties_first"); - - NacosEnvs.init(properties); - final String value = NacosEnvs.getProperty("nacos.home"); - - Assert.assertEquals("/home/jvm_first", value); - System.clearProperty("nacos.envs.search"); - System.clearProperty("nacos.home"); - } - - @Test - public void testGetPropertyDefaultSetting() { - Properties properties = new Properties(); - - NacosEnvs.init(properties); - final String value = NacosEnvs.getProperty("nacos.home.default.test"); - - Assert.assertEquals("/home/default_setting", value); - - } - -} diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java index 6be6dd88b..1835bf3b8 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java @@ -116,6 +116,27 @@ public class NacosNamingServiceTest { instances -> CollectionUtils.isEqualCollection(instanceList, instances))); } + @Test + public void testBatchDeRegisterInstance() throws NacosException { + Instance instance = new Instance(); + String serviceName = "service1"; + String ip = "1.1.1.1"; + int port = 10000; + instance.setServiceName(serviceName); + instance.setEphemeral(true); + instance.setPort(port); + instance.setIp(ip); + List instanceList = new ArrayList<>(); + instanceList.add(instance); + //when + try { + client.batchDeregisterInstance(serviceName, Constants.DEFAULT_GROUP, instanceList); + } catch (Exception e) { + Assert.assertTrue(e instanceof NacosException); + Assert.assertTrue(e.getMessage().contains("not found")); + } + } + @Test public void testRegisterInstance2() throws NacosException { //given diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java index 96e237272..e001599ce 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java @@ -109,6 +109,12 @@ public class AbstractNamingClientProxyTest { } + @Override + public void batchDeregisterService(String serviceName, String groupName, List instances) + throws NacosException { + + } + @Override public void deregisterService(String serviceName, String groupName, Instance instance) throws NacosException { diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java index 0da87a8ee..40789acf4 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java @@ -48,6 +48,7 @@ import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.RpcClient; +import com.alibaba.nacos.common.remote.client.RpcClientConfig; import com.alibaba.nacos.common.remote.client.ServerListFactory; import org.junit.Assert; import org.junit.Before; @@ -62,8 +63,10 @@ import org.mockito.junit.MockitoJUnitRunner; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -77,7 +80,7 @@ import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class NamingGrpcClientProxyTest { - + @Rule public ExpectedException expectedException = ExpectedException.none(); @@ -165,7 +168,7 @@ public class NamingGrpcClientProxyTest { public void testRegisterServiceThrowsException() throws NacosException { expectedException.expect(NacosException.class); expectedException.expectMessage("Request nacos server failed: "); - + when(this.rpcClient.request(Mockito.any())).thenReturn(null); try { @@ -284,8 +287,11 @@ public class NamingGrpcClientProxyTest { verify(this.rpcClient, times(1)).request(argThat(request -> { if (request instanceof SubscribeServiceRequest) { SubscribeServiceRequest request1 = (SubscribeServiceRequest) request; - // not subscribe - return !request1.isSubscribe(); + + // verify request fields + return !request1.isSubscribe() && SERVICE_NAME.equals(request1.getServiceName()) && GROUP_NAME.equals( + request1.getGroupName()) && CLUSTERS.equals(request1.getClusters()) && NAMESPACE_ID.equals( + request1.getNamespace()); } return false; })); @@ -320,7 +326,42 @@ public class NamingGrpcClientProxyTest { @Test public void testServerListChanged() throws Exception { - RpcClient rpc = new RpcClient("testServerListHasChanged", factory) { + RpcClient rpc = new RpcClient(new RpcClientConfig() { + @Override + public String name() { + return "testServerListHasChanged"; + } + + @Override + public int retryTimes() { + return 3; + } + + @Override + public long timeOutMills() { + return 3000L; + } + + @Override + public long connectionKeepAlive() { + return 5000L; + } + + @Override + public int healthCheckRetryTimes() { + return 1; + } + + @Override + public long healthCheckTimeOut() { + return 3000L; + } + + @Override + public Map labels() { + return new HashMap<>(); + } + }, factory) { @Override public ConnectionType getConnectionType() { return ConnectionType.GRPC; diff --git a/common/src/main/java/com/alibaba/nacos/common/constant/HttpHeaderConsts.java b/common/src/main/java/com/alibaba/nacos/common/constant/HttpHeaderConsts.java index 80246ec22..5a9f8737a 100644 --- a/common/src/main/java/com/alibaba/nacos/common/constant/HttpHeaderConsts.java +++ b/common/src/main/java/com/alibaba/nacos/common/constant/HttpHeaderConsts.java @@ -34,5 +34,7 @@ public interface HttpHeaderConsts { String CONNECTION = "Requester"; String REQUEST_ID = "RequestId"; String REQUEST_MODULE = "Request-Module"; + String APP_FILED = "app"; + String CLIENT_IP = "clientIp"; } diff --git a/common/src/main/java/com/alibaba/nacos/common/notify/Event.java b/common/src/main/java/com/alibaba/nacos/common/notify/Event.java index 408b7a84b..0939b5bc7 100644 --- a/common/src/main/java/com/alibaba/nacos/common/notify/Event.java +++ b/common/src/main/java/com/alibaba/nacos/common/notify/Event.java @@ -27,11 +27,11 @@ import java.util.concurrent.atomic.AtomicLong; */ @SuppressWarnings({"PMD.AbstractClassShouldStartWithAbstractNamingRule"}) public abstract class Event implements Serializable { - + private static final long serialVersionUID = -3731383194964997493L; - + private static final AtomicLong SEQUENCE = new AtomicLong(0); - + private final long sequence = SEQUENCE.getAndIncrement(); /** @@ -51,5 +51,15 @@ public abstract class Event implements Serializable { public String scope() { return null; } + + /** + * Whether is plugin event. If so, the event can be dropped when no publish and subscriber without any hint. Default + * false + * + * @return {@code true} if is plugin event, otherwise {@code false} + */ + public boolean isPluginEvent() { + return false; + } } diff --git a/common/src/main/java/com/alibaba/nacos/common/notify/NotifyCenter.java b/common/src/main/java/com/alibaba/nacos/common/notify/NotifyCenter.java index 60d019aec..293fa26cb 100644 --- a/common/src/main/java/com/alibaba/nacos/common/notify/NotifyCenter.java +++ b/common/src/main/java/com/alibaba/nacos/common/notify/NotifyCenter.java @@ -301,6 +301,9 @@ public class NotifyCenter { if (publisher != null) { return publisher.publish(event); } + if (event.isPluginEvent()) { + return true; + } LOGGER.warn("There are no [{}] publishers for this event, please register", topic); return false; } diff --git a/common/src/main/java/com/alibaba/nacos/common/notify/listener/SmartSubscriber.java b/common/src/main/java/com/alibaba/nacos/common/notify/listener/SmartSubscriber.java index 8e1da9f8b..8ce72b3a5 100644 --- a/common/src/main/java/com/alibaba/nacos/common/notify/listener/SmartSubscriber.java +++ b/common/src/main/java/com/alibaba/nacos/common/notify/listener/SmartSubscriber.java @@ -30,9 +30,9 @@ import java.util.List; public abstract class SmartSubscriber extends Subscriber { /** - * Returns which event type are smartsubscriber interested in. + * Returns which event type are smart subscriber interested in. * - * @return The interestd event types. + * @return The interested event types. */ public abstract List> subscribeTypes(); diff --git a/common/src/main/java/com/alibaba/nacos/common/notify/listener/Subscriber.java b/common/src/main/java/com/alibaba/nacos/common/notify/listener/Subscriber.java index 2e6500b55..ba3ca8094 100644 --- a/common/src/main/java/com/alibaba/nacos/common/notify/listener/Subscriber.java +++ b/common/src/main/java/com/alibaba/nacos/common/notify/listener/Subscriber.java @@ -69,6 +69,6 @@ public abstract class Subscriber { * @return Whether the event's scope matches current subscriber */ public boolean scopeMatches(T event) { - return event.scope() == null; + return true; } } diff --git a/common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClient.java b/common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClient.java index 8edb11100..9f3ba8216 100644 --- a/common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClient.java +++ b/common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClient.java @@ -33,6 +33,7 @@ import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.PayloadRegistry; import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.LoggerUtils; import com.alibaba.nacos.common.utils.NumberUtils; import com.alibaba.nacos.common.utils.StringUtils; @@ -40,7 +41,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; @@ -79,23 +80,10 @@ public abstract class RpcClient implements Closeable { protected volatile Connection currentConnection; - protected Map labels = new HashMap<>(); - - private String name; - private String tenant; - private static final int RETRY_TIMES = 3; - - private static final long DEFAULT_TIMEOUT_MILLS = 3000L; - protected ClientAbilities clientAbilities; - /** - * default keep alive time 5s. - */ - private long keepAliveTime = 5000L; - private long lastActiveTimeStamp = System.currentTimeMillis(); /** @@ -110,28 +98,34 @@ public abstract class RpcClient implements Closeable { private static final Pattern EXCLUDE_PROTOCOL_PATTERN = Pattern.compile("(?<=\\w{1,5}://)(.*)"); + protected RpcClientConfig rpcClientConfig; + static { PayloadRegistry.init(); } - public RpcClient(String name) { - this(name, null); + public RpcClient(RpcClientConfig rpcClientConfig) { + this(rpcClientConfig, null); } - public RpcClient(ServerListFactory serverListFactory) { - this(null, serverListFactory); + public RpcClient(RpcClientConfig rpcClientConfig, ServerListFactory serverListFactory) { + this.rpcClientConfig = rpcClientConfig; + this.serverListFactory = serverListFactory; + init(); } - public RpcClient(String name, ServerListFactory serverListFactory) { - this.name = name; - if (serverListFactory != null) { - this.serverListFactory = serverListFactory; + protected void init() { + if (this.serverListFactory != null) { rpcClientStatus.compareAndSet(RpcClientStatus.WAIT_INIT, RpcClientStatus.INITIALIZED); LoggerUtils.printIfInfoEnabled(LOGGER, "RpcClient init in constructor, ServerListFactory = {}", serverListFactory.getClass().getName()); } } + public Map labels() { + return Collections.unmodifiableMap(rpcClientConfig.labels()); + } + /** * init client abilities. * @@ -154,35 +148,11 @@ public abstract class RpcClient implements Closeable { this.serverListFactory = serverListFactory; rpcClientStatus.compareAndSet(RpcClientStatus.WAIT_INIT, RpcClientStatus.INITIALIZED); - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] RpcClient init, ServerListFactory = {}", name, + LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] RpcClient init, ServerListFactory = {}", rpcClientConfig.name(), serverListFactory.getClass().getName()); return this; } - /** - * init labels. - * - * @param labels labels - */ - public RpcClient labels(Map labels) { - this.labels.putAll(labels); - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] RpcClient init label, labels = {}", name, this.labels); - return this; - } - - /** - * init keepalive time. - * - * @param keepAliveTime keepAliveTime - * @param timeUnit timeUnit - */ - public RpcClient keepAlive(long keepAliveTime, TimeUnit timeUnit) { - this.keepAliveTime = keepAliveTime * timeUnit.toMillis(keepAliveTime); - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] RpcClient init keepalive time, keepAliveTimeMillis = {}", name, - keepAliveTime); - return this; - } - /** * Notify when client disconnected. */ @@ -190,13 +160,13 @@ public abstract class RpcClient implements Closeable { if (connectionEventListeners.isEmpty()) { return; } - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Notify disconnected event to listeners", name); + LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Notify disconnected event to listeners", rpcClientConfig.name()); for (ConnectionEventListener connectionEventListener : connectionEventListeners) { try { connectionEventListener.onDisConnect(); } catch (Throwable throwable) { - LoggerUtils.printIfErrorEnabled(LOGGER, "[{}] Notify disconnect listener error, listener = {}", name, - connectionEventListener.getClass().getName()); + LoggerUtils.printIfErrorEnabled(LOGGER, "[{}] Notify disconnect listener error, listener = {}", + rpcClientConfig.name(), connectionEventListener.getClass().getName()); } } } @@ -208,13 +178,13 @@ public abstract class RpcClient implements Closeable { if (connectionEventListeners.isEmpty()) { return; } - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Notify connected event to listeners.", name); + LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Notify connected event to listeners.", rpcClientConfig.name()); for (ConnectionEventListener connectionEventListener : connectionEventListeners) { try { connectionEventListener.onConnected(); } catch (Throwable throwable) { - LoggerUtils.printIfErrorEnabled(LOGGER, "[{}] Notify connect listener error, listener = {}", name, - connectionEventListener.getClass().getName()); + LoggerUtils.printIfErrorEnabled(LOGGER, "[{}] Notify connect listener error, listener = {}", + rpcClientConfig.name(), connectionEventListener.getClass().getName()); } } } @@ -310,18 +280,18 @@ public abstract class RpcClient implements Closeable { break; } ReconnectContext reconnectContext = reconnectionSignal - .poll(keepAliveTime, TimeUnit.MILLISECONDS); + .poll(rpcClientConfig.connectionKeepAlive(), TimeUnit.MILLISECONDS); if (reconnectContext == null) { // check alive time. - if (System.currentTimeMillis() - lastActiveTimeStamp >= keepAliveTime) { + if (System.currentTimeMillis() - lastActiveTimeStamp >= rpcClientConfig.connectionKeepAlive()) { boolean isHealthy = healthCheck(); if (!isHealthy) { if (currentConnection == null) { continue; } LoggerUtils.printIfInfoEnabled(LOGGER, - "[{}] Server healthy check fail, currentConnection = {}", name, - currentConnection.getConnectionId()); + "[{}] Server healthy check fail, currentConnection = {}", + rpcClientConfig.name(), currentConnection.getConnectionId()); RpcClientStatus rpcClientStatus = RpcClient.this.rpcClientStatus.get(); if (RpcClientStatus.SHUTDOWN.equals(rpcClientStatus)) { @@ -359,8 +329,8 @@ public abstract class RpcClient implements Closeable { } if (!serverExist) { LoggerUtils.printIfInfoEnabled(LOGGER, - "[{}] Recommend server is not in server list, ignore recommend server {}", name, - reconnectContext.serverInfo.getAddress()); + "[{}] Recommend server is not in server list, ignore recommend server {}", + rpcClientConfig.name(), reconnectContext.serverInfo.getAddress()); reconnectContext.serverInfo = null; @@ -373,31 +343,33 @@ public abstract class RpcClient implements Closeable { } }); - // connect to server, try to connect to server sync RETRY_TIMES times, async starting if failed. + // connect to server, try to connect to server sync retryTimes times, async starting if failed. Connection connectToServer = null; rpcClientStatus.set(RpcClientStatus.STARTING); - int startUpRetryTimes = RETRY_TIMES; + int startUpRetryTimes = rpcClientConfig.retryTimes(); while (startUpRetryTimes > 0 && connectToServer == null) { try { startUpRetryTimes--; ServerInfo serverInfo = nextRpcServer(); - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Try to connect to server on start up, server: {}", name, - serverInfo); + LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Try to connect to server on start up, server: {}", + rpcClientConfig.name(), serverInfo); connectToServer = connectToServer(serverInfo); } catch (Throwable e) { LoggerUtils.printIfWarnEnabled(LOGGER, "[{}] Fail to connect to server on start up, error message = {}, start up retry times left: {}", - name, e.getMessage(), startUpRetryTimes, e); + rpcClientConfig.name(), e.getMessage(), startUpRetryTimes, e); } } if (connectToServer != null) { - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Success to connect to server [{}] on start up, connectionId = {}", - name, connectToServer.serverInfo.getAddress(), connectToServer.getConnectionId()); + LoggerUtils + .printIfInfoEnabled(LOGGER, "[{}] Success to connect to server [{}] on start up, connectionId = {}", + rpcClientConfig.name(), connectToServer.serverInfo.getAddress(), + connectToServer.getConnectionId()); this.currentConnection = connectToServer; rpcClientStatus.set(RpcClientStatus.RUNNING); eventLinkedBlockingQueue.offer(new ConnectionEvent(ConnectionEvent.CONNECTED)); @@ -440,7 +412,7 @@ public abstract class RpcClient implements Closeable { } } } catch (Exception e) { - LoggerUtils.printIfErrorEnabled(LOGGER, "[{}] Switch server error, {}", name, e); + LoggerUtils.printIfErrorEnabled(LOGGER, "[{}] Switch server error, {}", rpcClientConfig.name(), e); } return new ConnectResetResponse(); } @@ -464,12 +436,17 @@ public abstract class RpcClient implements Closeable { if (this.currentConnection == null) { return false; } - try { - Response response = this.currentConnection.request(healthCheckRequest, 3000L); - // not only check server is ok, also check connection is register. - return response != null && response.isSuccess(); - } catch (NacosException e) { - // ignore + int reTryTimes = rpcClientConfig.healthCheckRetryTimes(); + while (reTryTimes >= 0) { + reTryTimes--; + try { + Response response = this.currentConnection + .request(healthCheckRequest, rpcClientConfig.healthCheckTimeOut()); + // not only check server is ok, also check connection is register. + return response != null && response.isSuccess(); + } catch (NacosException e) { + // ignore + } } return false; } @@ -495,14 +472,14 @@ public abstract class RpcClient implements Closeable { AtomicReference recommendServer = new AtomicReference<>(recommendServerInfo); if (onRequestFail && healthCheck()) { - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Server check success, currentServer is {} ", name, - currentConnection.serverInfo.getAddress()); + LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Server check success, currentServer is {} ", + rpcClientConfig.name(), currentConnection.serverInfo.getAddress()); rpcClientStatus.set(RpcClientStatus.RUNNING); return; } - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Try to reconnect to a new server, server is {}", name, - recommendServerInfo == null ? " not appointed, will choose a random server." + LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Try to reconnect to a new server, server is {}", + rpcClientConfig.name(), recommendServerInfo == null ? " not appointed, will choose a random server." : (recommendServerInfo.getAddress() + ", will try it once.")); // loop until start client success. @@ -520,13 +497,16 @@ public abstract class RpcClient implements Closeable { // 2.create a new channel to new server Connection connectionNew = connectToServer(serverInfo); if (connectionNew != null) { - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Success to connect a server [{}], connectionId = {}", - name, serverInfo.getAddress(), connectionNew.getConnectionId()); + LoggerUtils + .printIfInfoEnabled(LOGGER, "[{}] Success to connect a server [{}], connectionId = {}", + rpcClientConfig.name(), serverInfo.getAddress(), + connectionNew.getConnectionId()); // successfully create a new connect. if (currentConnection != null) { LoggerUtils.printIfInfoEnabled(LOGGER, - "[{}] Abandon prev connection, server is {}, connectionId is {}", name, - currentConnection.serverInfo.getAddress(), currentConnection.getConnectionId()); + "[{}] Abandon prev connection, server is {}, connectionId is {}", + rpcClientConfig.name(), currentConnection.serverInfo.getAddress(), + currentConnection.getConnectionId()); // set current connection to enable connection event. currentConnection.setAbandon(true); closeConnection(currentConnection); @@ -558,8 +538,9 @@ public abstract class RpcClient implements Closeable { if (reConnectTimes > 0 && reConnectTimes % RpcClient.this.serverListFactory.getServerList().size() == 0) { LoggerUtils.printIfInfoEnabled(LOGGER, - "[{}] Fail to connect server, after trying {} times, last try server is {}, error = {}", name, - reConnectTimes, serverInfo, lastException == null ? "unknown" : lastException); + "[{}] Fail to connect server, after trying {} times, last try server is {}, error = {}", + rpcClientConfig.name(), reConnectTimes, serverInfo, + lastException == null ? "unknown" : lastException); if (Integer.MAX_VALUE == retryTurns) { retryTurns = 50; } else { @@ -583,11 +564,14 @@ public abstract class RpcClient implements Closeable { } if (isShutdown()) { - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Client is shutdown, stop reconnect to server", name); + LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Client is shutdown, stop reconnect to server", + rpcClientConfig.name()); } } catch (Exception e) { - LoggerUtils.printIfWarnEnabled(LOGGER, "[{}] Fail to reconnect to server, error is {}", name, e); + LoggerUtils + .printIfWarnEnabled(LOGGER, "[{}] Fail to reconnect to server, error is {}", rpcClientConfig.name(), + e); } } @@ -632,7 +616,7 @@ public abstract class RpcClient implements Closeable { * @return response from server. */ public Response request(Request request) throws NacosException { - return request(request, DEFAULT_TIMEOUT_MILLS); + return request(request, rpcClientConfig.timeOutMills()); } /** @@ -646,7 +630,7 @@ public abstract class RpcClient implements Closeable { Response response; Exception exceptionThrow = null; long start = System.currentTimeMillis(); - while (retryTimes < RETRY_TIMES && System.currentTimeMillis() < timeoutMills + start) { + while (retryTimes < rpcClientConfig.retryTimes() && System.currentTimeMillis() < timeoutMills + start) { boolean waitReconnect = false; try { if (this.currentConnection == null || !isRunning()) { @@ -687,8 +671,9 @@ public abstract class RpcClient implements Closeable { } } - LoggerUtils.printIfErrorEnabled(LOGGER, "Send request fail, request = {}, retryTimes = {}, errorMessage = {}", - request, retryTimes, e.getMessage()); + LoggerUtils.printIfErrorEnabled(LOGGER, + "Send request fail, request = {}, retryTimes = {}, errorMessage = {}", request, retryTimes, + e.getMessage()); exceptionThrow = e; @@ -719,7 +704,8 @@ public abstract class RpcClient implements Closeable { Exception exceptionToThrow = null; long start = System.currentTimeMillis(); - while (retryTimes < RETRY_TIMES && System.currentTimeMillis() < start + callback.getTimeout()) { + while (retryTimes < rpcClientConfig.retryTimes() && System.currentTimeMillis() < start + callback + .getTimeout()) { boolean waitReconnect = false; try { if (this.currentConnection == null || !isRunning()) { @@ -737,9 +723,9 @@ public abstract class RpcClient implements Closeable { // Do nothing. } } - LoggerUtils - .printIfErrorEnabled(LOGGER, "[{}] Send request fail, request = {}, retryTimes = {}, errorMessage = {}", - name, request, retryTimes, e.getMessage()); + LoggerUtils.printIfErrorEnabled(LOGGER, + "[{}] Send request fail, request = {}, retryTimes = {}, errorMessage = {}", + rpcClientConfig.name(), request, retryTimes, e.getMessage()); exceptionToThrow = e; } @@ -768,7 +754,8 @@ public abstract class RpcClient implements Closeable { int retryTimes = 0; long start = System.currentTimeMillis(); Exception exceptionToThrow = null; - while (retryTimes < RETRY_TIMES && System.currentTimeMillis() < start + DEFAULT_TIMEOUT_MILLS) { + while (retryTimes < rpcClientConfig.retryTimes() && System.currentTimeMillis() < start + rpcClientConfig + .timeOutMills()) { boolean waitReconnect = false; try { if (this.currentConnection == null || !isRunning()) { @@ -785,9 +772,9 @@ public abstract class RpcClient implements Closeable { // Do nothing. } } - LoggerUtils - .printIfErrorEnabled(LOGGER, "[{}] Send request fail, request = {}, retryTimes = {}, errorMessage = {}", - name, request, retryTimes, e.getMessage()); + LoggerUtils.printIfErrorEnabled(LOGGER, + "[{}] Send request fail, request = {}, retryTimes = {}, errorMessage = {}", + rpcClientConfig.name(), request, retryTimes, e.getMessage()); exceptionToThrow = e; } @@ -824,21 +811,21 @@ public abstract class RpcClient implements Closeable { */ protected Response handleServerRequest(final Request request) { - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Receive server push request, request = {}, requestId = {}", name, - request.getClass().getSimpleName(), request.getRequestId()); + LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Receive server push request, request = {}, requestId = {}", + rpcClientConfig.name(), request.getClass().getSimpleName(), request.getRequestId()); lastActiveTimeStamp = System.currentTimeMillis(); for (ServerRequestHandler serverRequestHandler : serverRequestHandlers) { try { Response response = serverRequestHandler.requestReply(request); if (response != null) { - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Ack server push request, request = {}, requestId = {}", name, - request.getClass().getSimpleName(), request.getRequestId()); + LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Ack server push request, request = {}, requestId = {}", + rpcClientConfig.name(), request.getClass().getSimpleName(), request.getRequestId()); return response; } } catch (Exception e) { - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] HandleServerRequest:{}, errorMessage = {}", name, - serverRequestHandler.getClass().getName(), e.getMessage()); + LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] HandleServerRequest:{}, errorMessage = {}", + rpcClientConfig.name(), serverRequestHandler.getClass().getName(), e.getMessage()); } } @@ -852,8 +839,8 @@ public abstract class RpcClient implements Closeable { */ public synchronized void registerConnectionListener(ConnectionEventListener connectionEventListener) { - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Registry connection listener to current client:{}", name, - connectionEventListener.getClass().getName()); + LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Registry connection listener to current client:{}", + rpcClientConfig.name(), connectionEventListener.getClass().getName()); this.connectionEventListeners.add(connectionEventListener); } @@ -863,7 +850,7 @@ public abstract class RpcClient implements Closeable { * @param serverRequestHandler serverRequestHandler */ public synchronized void registerServerRequestHandler(ServerRequestHandler serverRequestHandler) { - LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Register server push request handler:{}", name, + LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Register server push request handler:{}", rpcClientConfig.name(), serverRequestHandler.getClass().getName()); this.serverRequestHandlers.add(serverRequestHandler); @@ -875,16 +862,7 @@ public abstract class RpcClient implements Closeable { * @return property value of name */ public String getName() { - return name; - } - - /** - * Setter method for property name. - * - * @param name value to be assigned to property name - */ - public void setName(String name) { - this.name = name; + return rpcClientConfig.name(); } /** @@ -918,8 +896,7 @@ public abstract class RpcClient implements Closeable { if (matcher.find()) { serverAddress = matcher.group(1); } - - String[] ipPortTuple = serverAddress.split(Constants.COLON, 2); + String[] ipPortTuple = InternetAddressUtil.splitIPPortStr(serverAddress); int defaultPort = Integer.parseInt(System.getProperty("nacos.server.port", "8848")); String serverPort = CollectionUtils.getOrDefault(ipPortTuple, 1, Integer.toString(defaultPort)); @@ -1019,7 +996,7 @@ public abstract class RpcClient implements Closeable { * @return property value of labels */ public Map getLabels() { - return labels; + return rpcClientConfig.labels(); } class ReconnectContext { diff --git a/common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClientConfig.java b/common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClientConfig.java new file mode 100644 index 000000000..f575141d6 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClientConfig.java @@ -0,0 +1,77 @@ +/* + * Copyright 1999-2020 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.nacos.common.remote.client; + +import java.util.Map; + +/** + * RpcClientConfig. + * + * @author karsonto + */ +public interface RpcClientConfig { + + /** + * get name. + * + * @return name. + */ + String name(); + + /** + * get request retry times. + * + * @return retryTimes. + */ + int retryTimes(); + + /** + * get time out mills. + * + * @return timeOutMills. + */ + long timeOutMills(); + + /** + * get connection keep alive time. + * + * @return connectionKeepAlive. + */ + long connectionKeepAlive(); + + /** + * get health check retry times. + * + * @return healthCheckRetryTimes. + */ + int healthCheckRetryTimes(); + + /** + * get health check time out. + * + * @return healthCheckTimeOut. + */ + long healthCheckTimeOut(); + + /** + * get map of labels. + * + * @return labels. + */ + Map labels(); + +} diff --git a/common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClientFactory.java b/common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClientFactory.java index 2d71b9e46..fea5a3daa 100644 --- a/common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClientFactory.java +++ b/common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClientFactory.java @@ -18,7 +18,6 @@ package com.alibaba.nacos.common.remote.client; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.remote.ConnectionType; -import com.alibaba.nacos.common.remote.client.grpc.GrpcClient; import com.alibaba.nacos.common.remote.client.grpc.GrpcClusterClient; import com.alibaba.nacos.common.remote.client.grpc.GrpcSdkClient; import org.slf4j.Logger; @@ -94,11 +93,7 @@ public class RpcClientFactory { return CLIENT_MAP.computeIfAbsent(clientName, clientNameInner -> { LOGGER.info("[RpcClientFactory] create a new rpc client of " + clientName); try { - GrpcClient client = new GrpcSdkClient(clientNameInner); - client.setThreadPoolCoreSize(threadPoolCoreSize); - client.setThreadPoolMaxSize(threadPoolMaxSize); - client.labels(labels); - return client; + return new GrpcSdkClient(clientNameInner, threadPoolCoreSize, threadPoolMaxSize, labels); } catch (Throwable throwable) { LOGGER.error("Error to init GrpcSdkClient for client name :" + clientName, throwable); throw throwable; @@ -134,13 +129,9 @@ public class RpcClientFactory { throw new UnsupportedOperationException("unsupported connection type :" + connectionType.getType()); } - return CLIENT_MAP.computeIfAbsent(clientName, clientNameInner -> { - GrpcClient client = new GrpcClusterClient(clientNameInner); - client.setThreadPoolCoreSize(threadPoolCoreSize); - client.setThreadPoolMaxSize(threadPoolMaxSize); - client.labels(labels); - return client; - }); + return CLIENT_MAP.computeIfAbsent(clientName, + clientNameInner -> new GrpcClusterClient(clientNameInner, threadPoolCoreSize, threadPoolMaxSize, + labels)); } } diff --git a/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/DefaultGrpcClientConfig.java b/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/DefaultGrpcClientConfig.java new file mode 100644 index 000000000..2b36f70fd --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/DefaultGrpcClientConfig.java @@ -0,0 +1,385 @@ +/* + * Copyright 1999-2020 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.nacos.common.remote.client.grpc; + +import com.alibaba.nacos.common.utils.ThreadUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; + +/** + * Default grpc client config. + * + * @author karsonto + */ +public class DefaultGrpcClientConfig implements GrpcClientConfig { + + private String name; + + private int retryTimes; + + private long timeOutMills; + + private long connectionKeepAlive; + + private long threadPoolKeepAlive; + + private int threadPoolCoreSize; + + private int threadPoolMaxSize; + + private long serverCheckTimeOut; + + private int threadPoolQueueSize; + + private int maxInboundMessageSize; + + private int channelKeepAlive; + + private int healthCheckRetryTimes; + + private long healthCheckTimeOut; + + private Map labels; + + /** + * constructor. + * + * @param builder builder of DefaultGrpcClientConfig builder. + */ + private DefaultGrpcClientConfig(Builder builder) { + this.name = builder.name; + this.retryTimes = loadIntegerConfig(GrpcConstants.GRPC_RETRY_TIMES, builder.retryTimes); + this.timeOutMills = loadLongConfig(GrpcConstants.GRPC_TIMEOUT_MILLS, builder.timeOutMills); + this.connectionKeepAlive = loadLongConfig(GrpcConstants.GRPC_CONNECT_KEEP_ALIVE_TIME, + builder.connectionKeepAlive); + this.threadPoolKeepAlive = loadLongConfig(GrpcConstants.GRPC_THREADPOOL_KEEPALIVETIME, + builder.threadPoolKeepAlive); + this.threadPoolCoreSize = loadIntegerConfig(GrpcConstants.GRPC_THREADPOOL_CORE_SIZE, + builder.threadPoolCoreSize); + this.threadPoolMaxSize = loadIntegerConfig(GrpcConstants.GRPC_THREADPOOL_MAX_SIZE, builder.threadPoolMaxSize); + this.serverCheckTimeOut = loadLongConfig(GrpcConstants.GRPC_SERVER_CHECK_TIMEOUT, builder.serverCheckTimeOut); + this.threadPoolQueueSize = loadIntegerConfig(GrpcConstants.GRPC_QUEUESIZE, builder.threadPoolQueueSize); + this.maxInboundMessageSize = loadIntegerConfig(GrpcConstants.GRPC_MAX_INBOUND_MESSAGE_SIZE, + builder.maxInboundMessageSize); + this.channelKeepAlive = loadIntegerConfig(GrpcConstants.GRPC_CHANNEL_KEEP_ALIVE_TIME, builder.channelKeepAlive); + this.healthCheckRetryTimes = loadIntegerConfig(GrpcConstants.GRPC_HEALTHCHECK_RETRY_TIMES, + builder.healthCheckRetryTimes); + this.healthCheckTimeOut = loadLongConfig(GrpcConstants.GRPC_HEALTHCHECK_TIMEOUT, builder.healthCheckTimeOut); + this.labels = builder.labels; + } + + private int loadIntegerConfig(String key, int builderValue) { + return Integer.getInteger(key, builderValue); + } + + private long loadLongConfig(String key, long builderValue) { + return Long.getLong(key, builderValue); + } + + @Override + public String name() { + return this.name; + } + + @Override + public int retryTimes() { + return retryTimes; + } + + @Override + public long timeOutMills() { + return timeOutMills; + } + + @Override + public long connectionKeepAlive() { + return connectionKeepAlive; + } + + @Override + public int threadPoolCoreSize() { + return threadPoolCoreSize; + } + + @Override + public int threadPoolMaxSize() { + return threadPoolMaxSize; + } + + @Override + public long threadPoolKeepAlive() { + return threadPoolKeepAlive; + } + + @Override + public long serverCheckTimeOut() { + return serverCheckTimeOut; + } + + @Override + public int threadPoolQueueSize() { + return threadPoolQueueSize; + } + + @Override + public int maxInboundMessageSize() { + return maxInboundMessageSize; + } + + @Override + public int channelKeepAlive() { + return channelKeepAlive; + } + + @Override + public int healthCheckRetryTimes() { + return healthCheckRetryTimes; + } + + @Override + public long healthCheckTimeOut() { + return healthCheckTimeOut; + } + + @Override + public Map labels() { + return this.labels; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder { + + private String name; + + private int retryTimes = 3; + + private long timeOutMills = 3000L; + + private long connectionKeepAlive = 5000L; + + private long threadPoolKeepAlive = 10000L; + + private int threadPoolCoreSize = ThreadUtils.getSuitableThreadCount(2); + + private int threadPoolMaxSize = ThreadUtils.getSuitableThreadCount(8); + + private long serverCheckTimeOut = 3000L; + + private int threadPoolQueueSize = 10000; + + private int maxInboundMessageSize = 10 * 1024 * 1024; + + private int channelKeepAlive = 6 * 60 * 1000; + + private int healthCheckRetryTimes = 3; + + private long healthCheckTimeOut = 3000L; + + private Map labels = new HashMap<>(); + + private Builder() { + } + + /** + * Set config from properties. + * + * @param properties properties + * @return Builder + */ + public Builder fromProperties(Properties properties) { + if (properties.contains(GrpcConstants.GRPC_NAME)) { + this.name = properties.getProperty(GrpcConstants.GRPC_NAME); + } + if (properties.contains(GrpcConstants.GRPC_RETRY_TIMES)) { + this.retryTimes = Integer.parseInt(properties.getProperty(GrpcConstants.GRPC_RETRY_TIMES)); + } + if (properties.contains(GrpcConstants.GRPC_TIMEOUT_MILLS)) { + this.timeOutMills = Long.parseLong(properties.getProperty(GrpcConstants.GRPC_TIMEOUT_MILLS)); + } + if (properties.contains(GrpcConstants.GRPC_CONNECT_KEEP_ALIVE_TIME)) { + this.connectionKeepAlive = Long + .parseLong(properties.getProperty(GrpcConstants.GRPC_CONNECT_KEEP_ALIVE_TIME)); + } + if (properties.contains(GrpcConstants.GRPC_THREADPOOL_KEEPALIVETIME)) { + this.threadPoolKeepAlive = Long + .parseLong(properties.getProperty(GrpcConstants.GRPC_THREADPOOL_KEEPALIVETIME)); + } + if (properties.contains(GrpcConstants.GRPC_THREADPOOL_CORE_SIZE)) { + this.threadPoolCoreSize = Integer + .parseInt(properties.getProperty(GrpcConstants.GRPC_THREADPOOL_CORE_SIZE)); + } + if (properties.contains(GrpcConstants.GRPC_THREADPOOL_MAX_SIZE)) { + this.threadPoolMaxSize = Integer + .parseInt(properties.getProperty(GrpcConstants.GRPC_THREADPOOL_MAX_SIZE)); + } + if (properties.contains(GrpcConstants.GRPC_SERVER_CHECK_TIMEOUT)) { + this.serverCheckTimeOut = Long + .parseLong(properties.getProperty(GrpcConstants.GRPC_SERVER_CHECK_TIMEOUT)); + } + if (properties.contains(GrpcConstants.GRPC_QUEUESIZE)) { + this.threadPoolQueueSize = Integer.parseInt(properties.getProperty(GrpcConstants.GRPC_QUEUESIZE)); + } + if (properties.contains(GrpcConstants.GRPC_MAX_INBOUND_MESSAGE_SIZE)) { + this.maxInboundMessageSize = Integer + .parseInt(properties.getProperty(GrpcConstants.GRPC_MAX_INBOUND_MESSAGE_SIZE)); + } + if (properties.contains(GrpcConstants.GRPC_CHANNEL_KEEP_ALIVE_TIME)) { + this.channelKeepAlive = Integer + .parseInt(properties.getProperty(GrpcConstants.GRPC_CHANNEL_KEEP_ALIVE_TIME)); + } + if (properties.contains(GrpcConstants.GRPC_HEALTHCHECK_RETRY_TIMES)) { + this.healthCheckRetryTimes = Integer + .parseInt(properties.getProperty(GrpcConstants.GRPC_HEALTHCHECK_RETRY_TIMES)); + } + if (properties.contains(GrpcConstants.GRPC_HEALTHCHECK_TIMEOUT)) { + this.healthCheckTimeOut = Long + .parseLong(properties.getProperty(GrpcConstants.GRPC_HEALTHCHECK_TIMEOUT)); + } + return this; + } + + /** + * set client name. + */ + public Builder setName(String name) { + this.name = name; + return this; + } + + /** + * set retryTimes. + */ + public Builder setRetryTimes(int retryTimes) { + this.retryTimes = retryTimes; + return this; + } + + /** + * set timeOutMills. + */ + public Builder setTimeOutMills(long timeOutMills) { + this.timeOutMills = timeOutMills; + return this; + } + + /** + * set connectionKeepAlive. + */ + public Builder setConnectionKeepAlive(long connectionKeepAlive) { + this.connectionKeepAlive = connectionKeepAlive; + return this; + } + + /** + * set threadPoolKeepAlive. + */ + public Builder setThreadPoolKeepAlive(Long threadPoolKeepAlive) { + this.threadPoolKeepAlive = threadPoolKeepAlive; + return this; + } + + /** + * set threadPoolCoreSize. + */ + public Builder setThreadPoolCoreSize(Integer threadPoolCoreSize) { + if (!Objects.isNull(threadPoolCoreSize)) { + this.threadPoolCoreSize = threadPoolCoreSize; + } + return this; + } + + /** + * set threadPoolMaxSize. + */ + public Builder setThreadPoolMaxSize(Integer threadPoolMaxSize) { + if (!Objects.isNull(threadPoolMaxSize)) { + this.threadPoolMaxSize = threadPoolMaxSize; + } + return this; + } + + /** + * set serverCheckTimeOut. + */ + public Builder setServerCheckTimeOut(Long serverCheckTimeOut) { + this.serverCheckTimeOut = serverCheckTimeOut; + return this; + } + + /** + * set threadPoolQueueSize. + */ + public Builder setThreadPoolQueueSize(int threadPoolQueueSize) { + this.threadPoolQueueSize = threadPoolQueueSize; + return this; + } + + /** + * set maxInboundMessageSize. + */ + public Builder setMaxInboundMessageSize(int maxInboundMessageSize) { + this.maxInboundMessageSize = maxInboundMessageSize; + return this; + } + + /** + * set channelKeepAlive. + */ + public Builder setChannelKeepAlive(int channelKeepAlive) { + this.channelKeepAlive = channelKeepAlive; + return this; + } + + /** + * set healthCheckRetryTimes. + */ + public Builder setHealthCheckRetryTimes(int healthCheckRetryTimes) { + this.healthCheckRetryTimes = healthCheckRetryTimes; + return this; + } + + /** + * set healthCheckTimeOut. + */ + public Builder setHealthCheckTimeOut(long healthCheckTimeOut) { + this.healthCheckTimeOut = healthCheckTimeOut; + return this; + } + + /** + * set labels. + */ + public Builder setLabels(Map labels) { + this.labels.putAll(labels); + return this; + } + + /** + * build GrpcClientConfig. + */ + public GrpcClientConfig build() { + return new DefaultGrpcClientConfig(this); + } + } + +} diff --git a/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClient.java b/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClient.java index d285583fb..81d5e7ff8 100644 --- a/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClient.java +++ b/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClient.java @@ -30,9 +30,9 @@ import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.remote.client.RpcClientStatus; +import com.alibaba.nacos.common.remote.client.ServerListFactory; import com.alibaba.nacos.common.utils.LoggerUtils; import com.alibaba.nacos.common.utils.ThreadFactoryBuilder; -import com.alibaba.nacos.common.utils.ThreadUtils; import com.alibaba.nacos.common.utils.VersionUtils; import com.google.common.util.concurrent.ListenableFuture; import io.grpc.CompressorRegistry; @@ -43,6 +43,8 @@ import io.grpc.stub.StreamObserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Map; +import java.util.Properties; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -56,19 +58,11 @@ import java.util.concurrent.TimeUnit; @SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule") public abstract class GrpcClient extends RpcClient { - static final Logger LOGGER = LoggerFactory.getLogger(GrpcClient.class); + private static final Logger LOGGER = LoggerFactory.getLogger(GrpcClient.class); - protected static final String NACOS_SERVER_GRPC_PORT_OFFSET_KEY = "nacos.server.grpc.port.offset"; + private final GrpcClientConfig clientConfig; - private ThreadPoolExecutor grpcExecutor = null; - - private Integer threadPoolCoreSize; - - private Integer threadPoolMaxSize; - - private static final long DEFAULT_MAX_INBOUND_MESSAGE_SIZE = 10 * 1024 * 1024L; - - private static final long DEFAULT_KEEP_ALIVE_TIME = 6 * 60 * 1000; + private ThreadPoolExecutor grpcExecutor; @Override public ConnectionType getConnectionType() { @@ -76,47 +70,64 @@ public abstract class GrpcClient extends RpcClient { } /** - * Empty constructor. + * constructor. + * + * @param name . */ public GrpcClient(String name) { - super(name); + this(DefaultGrpcClientConfig.newBuilder().setName(name).build()); } /** - * Set core size of thread pool. + * constructor. * - * @param threadPoolCoreSize core size of thread pool for grpc. + * @param properties . */ - public void setThreadPoolCoreSize(Integer threadPoolCoreSize) { - this.threadPoolCoreSize = threadPoolCoreSize; + public GrpcClient(Properties properties) { + this(DefaultGrpcClientConfig.newBuilder().fromProperties(properties).build()); } /** - * Set max size of thread pool. + * constructor. * - * @param threadPoolMaxSize max size of thread pool for grpc. + * @param clientConfig . */ - public void setThreadPoolMaxSize(Integer threadPoolMaxSize) { - this.threadPoolMaxSize = threadPoolMaxSize; + public GrpcClient(GrpcClientConfig clientConfig) { + super(clientConfig); + this.clientConfig = clientConfig; } - protected Integer getThreadPoolCoreSize() { - return threadPoolCoreSize != null ? threadPoolCoreSize : ThreadUtils.getSuitableThreadCount(2); + /** + * constructor. + * + * @param clientConfig . + * @param serverListFactory . + */ + public GrpcClient(GrpcClientConfig clientConfig, ServerListFactory serverListFactory) { + super(clientConfig, serverListFactory); + this.clientConfig = clientConfig; } - protected Integer getThreadPoolMaxSize() { - return threadPoolMaxSize != null ? threadPoolMaxSize : ThreadUtils.getSuitableThreadCount(8); + /** + * constructor. + * + * @param name . + * @param threadPoolCoreSize . + * @param threadPoolMaxSize . + * @param labels . + */ + public GrpcClient(String name, Integer threadPoolCoreSize, Integer threadPoolMaxSize, Map labels) { + this(DefaultGrpcClientConfig.newBuilder().setName(name).setThreadPoolCoreSize(threadPoolCoreSize) + .setThreadPoolMaxSize(threadPoolMaxSize).setLabels(labels).build()); } protected ThreadPoolExecutor createGrpcExecutor(String serverIp) { - ThreadPoolExecutor grpcExecutor = new ThreadPoolExecutor( - getThreadPoolCoreSize(), - getThreadPoolMaxSize(), - 10L, TimeUnit.SECONDS, - new LinkedBlockingQueue<>(10000), - new ThreadFactoryBuilder() - .daemon(true) - .nameFormat("nacos-grpc-client-executor-" + serverIp + "-%d") + // Thread name will use String.format, ipv6 maybe contain special word %, so handle it first. + serverIp = serverIp.replaceAll("%", "-"); + ThreadPoolExecutor grpcExecutor = new ThreadPoolExecutor(clientConfig.threadPoolCoreSize(), + clientConfig.threadPoolMaxSize(), clientConfig.threadPoolKeepAlive(), TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(clientConfig.threadPoolQueueSize()), + new ThreadFactoryBuilder().daemon(true).nameFormat("nacos-grpc-client-executor-" + serverIp + "-%d") .build()); grpcExecutor.allowCoreThreadTimeOut(true); return grpcExecutor; @@ -130,7 +141,7 @@ public abstract class GrpcClient extends RpcClient { grpcExecutor.shutdown(); } } - + /** * Create a stub using a channel. * @@ -140,7 +151,7 @@ public abstract class GrpcClient extends RpcClient { private RequestGrpc.RequestFutureStub createNewChannelStub(ManagedChannel managedChannelTemp) { return RequestGrpc.newFutureStub(managedChannelTemp); } - + /** * create a new channel with specific server address. * @@ -149,24 +160,14 @@ public abstract class GrpcClient extends RpcClient { * @return if server check success,return a non-null channel. */ private ManagedChannel createNewManagedChannel(String serverIp, int serverPort) { - ManagedChannelBuilder managedChannelBuilder = ManagedChannelBuilder.forAddress(serverIp, serverPort).executor(grpcExecutor) - .compressorRegistry(CompressorRegistry.getDefaultInstance()).decompressorRegistry(DecompressorRegistry.getDefaultInstance()) - .maxInboundMessageSize(getInboundMessageSize()).keepAliveTime(keepAliveTimeMillis(), TimeUnit.MILLISECONDS).usePlaintext(); + ManagedChannelBuilder managedChannelBuilder = ManagedChannelBuilder.forAddress(serverIp, serverPort) + .executor(grpcExecutor).compressorRegistry(CompressorRegistry.getDefaultInstance()) + .decompressorRegistry(DecompressorRegistry.getDefaultInstance()) + .maxInboundMessageSize(clientConfig.maxInboundMessageSize()) + .keepAliveTime(clientConfig.channelKeepAlive(), TimeUnit.MILLISECONDS).usePlaintext(); return managedChannelBuilder.build(); } - private int getInboundMessageSize() { - String messageSize = System.getProperty("nacos.remote.client.grpc.maxinbound.message.size", - String.valueOf(DEFAULT_MAX_INBOUND_MESSAGE_SIZE)); - return Integer.parseInt(messageSize); - } - - private int keepAliveTimeMillis() { - String keepAliveTimeMillis = System - .getProperty("nacos.remote.grpc.keep.alive.millis", String.valueOf(DEFAULT_KEEP_ALIVE_TIME)); - return Integer.parseInt(keepAliveTimeMillis); - } - /** * shutdown a channel. * @@ -192,7 +193,7 @@ public abstract class GrpcClient extends RpcClient { ServerCheckRequest serverCheckRequest = new ServerCheckRequest(); Payload grpcRequest = GrpcUtils.convert(serverCheckRequest); ListenableFuture responseFuture = requestBlockingStub.request(grpcRequest); - Payload response = responseFuture.get(3000L, TimeUnit.MILLISECONDS); + Payload response = responseFuture.get(clientConfig.serverCheckTimeOut(), TimeUnit.MILLISECONDS); //receive connection unregister response here,not check response is success. return (Response) GrpcUtils.parse(response); } catch (Exception e) { diff --git a/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClientConfig.java b/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClientConfig.java new file mode 100644 index 000000000..107f7973e --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClientConfig.java @@ -0,0 +1,77 @@ +/* + * Copyright 1999-2020 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.nacos.common.remote.client.grpc; + +import com.alibaba.nacos.common.remote.client.RpcClientConfig; + +/** + * GrpcCleint config. Use to collect and init Grpc client configuration. + * + * @author karsonto + */ +public interface GrpcClientConfig extends RpcClientConfig { + + /** + * get threadPoolCoreSize. + * + * @return threadPoolCoreSize. + */ + int threadPoolCoreSize(); + + /** + * get threadPoolMaxSize. + * + * @return threadPoolMaxSize. + */ + int threadPoolMaxSize(); + + /** + * get thread pool keep alive time. + * + * @return threadPoolKeepAlive. + */ + long threadPoolKeepAlive(); + + /** + * get server check time out. + * + * @return serverCheckTimeOut. + */ + long serverCheckTimeOut(); + + /** + * get thread pool queue size. + * + * @return threadPoolQueueSize. + */ + int threadPoolQueueSize(); + + /** + * get maxInboundMessage size. + * + * @return maxInboundMessageSize. + */ + int maxInboundMessageSize(); + + /** + * get channelKeepAlive time. + * + * @return channelKeepAlive. + */ + int channelKeepAlive(); + +} diff --git a/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClusterClient.java b/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClusterClient.java index 7b63653b6..48567148a 100644 --- a/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClusterClient.java +++ b/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClusterClient.java @@ -18,6 +18,9 @@ package com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.api.common.Constants; +import java.util.Map; +import java.util.Properties; + /** * gRPC client for cluster. * @@ -35,9 +38,40 @@ public class GrpcClusterClient extends GrpcClient { super(name); } + /** + * Empty constructor. + * + * @param config of GrpcClientConfig. + */ + public GrpcClusterClient(GrpcClientConfig config) { + super(config); + } + + /** + * Constructor. + * + * @param properties . + */ + public GrpcClusterClient(Properties properties) { + super(properties); + } + + /** + * Constructor. + * + * @param name name of client. + * @param threadPoolCoreSize . + * @param threadPoolMaxSize . + * @param labels . + */ + public GrpcClusterClient(String name, Integer threadPoolCoreSize, Integer threadPoolMaxSize, + Map labels) { + super(name, threadPoolCoreSize, threadPoolMaxSize, labels); + } + @Override public int rpcPortOffset() { - return Integer.parseInt(System.getProperty(NACOS_SERVER_GRPC_PORT_OFFSET_KEY, + return Integer.parseInt(System.getProperty(GrpcConstants.NACOS_SERVER_GRPC_PORT_OFFSET_KEY, String.valueOf(Constants.CLUSTER_GRPC_PORT_DEFAULT_OFFSET))); } diff --git a/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcConstants.java b/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcConstants.java new file mode 100644 index 000000000..d06b5ae40 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcConstants.java @@ -0,0 +1,107 @@ +/* + * Copyright 1999-2020 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.nacos.common.remote.client.grpc; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * GrpcConsts. + * + * @author karsonto + */ +public class GrpcConstants { + + public static final String NACOS_SERVER_GRPC_PORT_OFFSET_KEY = "nacos.server.grpc.port.offset"; + + public static final String NACOS_CLIENT_GRPC = "nacos.remote.client.grpc"; + + @GRpcConfigLabel + public static final String GRPC_NAME = NACOS_CLIENT_GRPC + ".name"; + + @GRpcConfigLabel + public static final String GRPC_THREADPOOL_KEEPALIVETIME = NACOS_CLIENT_GRPC + ".pool.alive"; + + @GRpcConfigLabel + public static final String GRPC_THREADPOOL_CORE_SIZE = NACOS_CLIENT_GRPC + ".pool.core.size"; + + @GRpcConfigLabel + public static final String GRPC_RETRY_TIMES = NACOS_CLIENT_GRPC + ".retry.times"; + + @GRpcConfigLabel + public static final String GRPC_TIMEOUT_MILLS = NACOS_CLIENT_GRPC + ".timeout"; + + @GRpcConfigLabel + public static final String GRPC_CONNECT_KEEP_ALIVE_TIME = NACOS_CLIENT_GRPC + ".connect.keep.alive"; + + @GRpcConfigLabel + public static final String GRPC_THREADPOOL_MAX_SIZE = NACOS_CLIENT_GRPC + ".pool.max.size"; + + @GRpcConfigLabel + public static final String GRPC_SERVER_CHECK_TIMEOUT = NACOS_CLIENT_GRPC + ".server.check.timeout"; + + @GRpcConfigLabel + public static final String GRPC_QUEUESIZE = NACOS_CLIENT_GRPC + ".queue.size"; + + @GRpcConfigLabel + public static final String GRPC_HEALTHCHECK_RETRY_TIMES = NACOS_CLIENT_GRPC + ".health.retry"; + + @GRpcConfigLabel + public static final String GRPC_HEALTHCHECK_TIMEOUT = NACOS_CLIENT_GRPC + ".health.timeout"; + + @GRpcConfigLabel + public static final String GRPC_MAX_INBOUND_MESSAGE_SIZE = NACOS_CLIENT_GRPC + ".maxinbound.message.size"; + + @GRpcConfigLabel + public static final String GRPC_CHANNEL_KEEP_ALIVE_TIME = NACOS_CLIENT_GRPC + ".channel.keep.alive"; + + private static final Set CONFIG_NAMES = new HashSet<>(); + + @Documented + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.RUNTIME) + protected @interface GRpcConfigLabel { + + } + + static { + Class clazz = GrpcConstants.class; + Field[] declaredFields = clazz.getDeclaredFields(); + for (Field declaredField : declaredFields) { + declaredField.setAccessible(true); + if (declaredField.getType().equals(String.class) && null != declaredField.getAnnotation( + GRpcConfigLabel.class)) { + try { + CONFIG_NAMES.add((String) declaredField.get(null)); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + + public static Set getRpcParams() { + return Collections.unmodifiableSet(CONFIG_NAMES); + } +} diff --git a/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcSdkClient.java b/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcSdkClient.java index 34cf40824..146f6d1aa 100644 --- a/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcSdkClient.java +++ b/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcSdkClient.java @@ -18,6 +18,9 @@ package com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.api.common.Constants; +import java.util.Map; +import java.util.Properties; + /** * gRPC client for sdk. * @@ -27,7 +30,7 @@ import com.alibaba.nacos.api.common.Constants; public class GrpcSdkClient extends GrpcClient { /** - * Empty constructor. + * Constructor. * * @param name name of client. */ @@ -35,9 +38,40 @@ public class GrpcSdkClient extends GrpcClient { super(name); } + /** + * Constructor. + * + * @param properties . + */ + public GrpcSdkClient(Properties properties) { + super(properties); + } + + /** + * Constructor. + * + * @param name name of client. + * @param threadPoolCoreSize . + * @param threadPoolMaxSize . + * @param labels . + */ + public GrpcSdkClient(String name, Integer threadPoolCoreSize, Integer threadPoolMaxSize, + Map labels) { + super(name, threadPoolCoreSize, threadPoolMaxSize, labels); + } + + /** + * constructor. + * + * @param config of GrpcClientConfig. + */ + public GrpcSdkClient(GrpcClientConfig config) { + super(config); + } + @Override public int rpcPortOffset() { - return Integer.parseInt(System.getProperty(NACOS_SERVER_GRPC_PORT_OFFSET_KEY, + return Integer.parseInt(System.getProperty(GrpcConstants.NACOS_SERVER_GRPC_PORT_OFFSET_KEY, String.valueOf(Constants.SDK_GRPC_PORT_DEFAULT_OFFSET))); } diff --git a/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcUtils.java b/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcUtils.java index bd7c60c56..778c95aee 100644 --- a/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcUtils.java +++ b/common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcUtils.java @@ -32,6 +32,8 @@ import com.google.protobuf.ByteString; import com.google.protobuf.UnsafeByteOperations; import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; /** * gRPC utils, use to parse request and response. @@ -59,8 +61,7 @@ public class GrpcUtils { payloadBuilder.setMetadata(metaBuilder.build()); // request body . - request.clearHeaders(); - byte[] jsonBytes = JacksonUtils.toJsonBytes(request); + byte[] jsonBytes = convertRequestToByte(request); return payloadBuilder .setBody(Any.newBuilder().setValue(UnsafeByteOperations.unsafeWrap(jsonBytes))) .build(); @@ -77,11 +78,11 @@ public class GrpcUtils { Metadata newMeta = Metadata.newBuilder().setType(request.getClass().getSimpleName()) .setClientIp(NetUtils.localIP()).putAllHeaders(request.getHeaders()).build(); - request.clearHeaders(); - byte[] jsonBytes = JacksonUtils.toJsonBytes(request); + + byte[] jsonBytes = convertRequestToByte(request); Payload.Builder builder = Payload.newBuilder(); - + return builder .setBody(Any.newBuilder().setValue(UnsafeByteOperations.unsafeWrap(jsonBytes))) .setMetadata(newMeta).build(); @@ -103,6 +104,14 @@ public class GrpcUtils { .setMetadata(metaBuilder.build()).build(); } + private static byte[] convertRequestToByte(Request request) { + Map requestHeaders = new HashMap<>(request.getHeaders()); + request.clearHeaders(); + byte[] jsonBytes = JacksonUtils.toJsonBytes(request); + request.putAllHeader(requestHeaders); + return jsonBytes; + } + /** * parse payload to request/response model. * diff --git a/common/src/main/java/com/alibaba/nacos/common/trace/event/naming/NamingTraceEvent.java b/common/src/main/java/com/alibaba/nacos/common/trace/event/naming/NamingTraceEvent.java index ca195d811..7aa1d8877 100644 --- a/common/src/main/java/com/alibaba/nacos/common/trace/event/naming/NamingTraceEvent.java +++ b/common/src/main/java/com/alibaba/nacos/common/trace/event/naming/NamingTraceEvent.java @@ -31,4 +31,9 @@ public class NamingTraceEvent extends TraceEvent { String serviceNamespace, String serviceGroup, String name) { super(eventType, eventTime, serviceNamespace, serviceGroup, name); } + + @Override + public boolean isPluginEvent() { + return true; + } } diff --git a/common/src/main/java/com/alibaba/nacos/common/trace/publisher/TraceEventPublisher.java b/common/src/main/java/com/alibaba/nacos/common/trace/publisher/TraceEventPublisher.java index cb89af38d..7fc879f46 100644 --- a/common/src/main/java/com/alibaba/nacos/common/trace/publisher/TraceEventPublisher.java +++ b/common/src/main/java/com/alibaba/nacos/common/trace/publisher/TraceEventPublisher.java @@ -103,7 +103,6 @@ public class TraceEventPublisher extends Thread implements ShardedEventPublisher boolean success = this.queue.offer(event); if (!success) { LOGGER.warn("Trace Event Publish failed, event : {}, publish queue size : {}", event, currentEventSize()); - return true; } return true; } diff --git a/common/src/main/java/com/alibaba/nacos/common/trace/publisher/TraceEventPublisherFactory.java b/common/src/main/java/com/alibaba/nacos/common/trace/publisher/TraceEventPublisherFactory.java index 5bf18e742..36f46759c 100644 --- a/common/src/main/java/com/alibaba/nacos/common/trace/publisher/TraceEventPublisherFactory.java +++ b/common/src/main/java/com/alibaba/nacos/common/trace/publisher/TraceEventPublisherFactory.java @@ -50,7 +50,6 @@ public class TraceEventPublisherFactory implements EventPublisherFactory { @Override public EventPublisher apply(final Class eventType, final Integer maxQueueSize) { - // Like ClientEvent$ClientChangeEvent cache by ClientEvent Class cachedEventType = TraceEvent.class; for (Class publisherEvent : publisherEvents) { diff --git a/common/src/test/java/com/alibaba/nacos/common/remote/client/RpcClientFactoryTest.java b/common/src/test/java/com/alibaba/nacos/common/remote/client/RpcClientFactoryTest.java index 9e582f9ed..0311f48fb 100644 --- a/common/src/test/java/com/alibaba/nacos/common/remote/client/RpcClientFactoryTest.java +++ b/common/src/test/java/com/alibaba/nacos/common/remote/client/RpcClientFactoryTest.java @@ -104,7 +104,7 @@ public class RpcClientFactoryTest { RpcClient client = RpcClientFactory .createClient("testClient", ConnectionType.GRPC, Collections.singletonMap("labelKey", "labelValue")); - Assert.assertEquals(Collections.singletonMap("labelKey", "labelValue"), client.labels); + Assert.assertEquals(Collections.singletonMap("labelKey", "labelValue"), client.rpcClientConfig.labels()); Assert.assertEquals(ConnectionType.GRPC, client.getConnectionType()); Assert.assertEquals("testClient", CollectionUtils.getOnlyElement(RpcClientFactory.getAllClientEntries()).getKey()); } @@ -134,7 +134,7 @@ public class RpcClientFactoryTest { Collections.singletonMap("labelKey", "labelValue") ); - Assert.assertEquals(Collections.singletonMap("labelKey", "labelValue"), client.labels); + Assert.assertEquals(Collections.singletonMap("labelKey", "labelValue"), client.rpcClientConfig.labels()); Assert.assertEquals(ConnectionType.GRPC, client.getConnectionType()); Assert.assertEquals("testClient", CollectionUtils.getOnlyElement(RpcClientFactory.getAllClientEntries()).getKey()); } diff --git a/common/src/test/java/com/alibaba/nacos/common/remote/client/RpcClientTest.java b/common/src/test/java/com/alibaba/nacos/common/remote/client/RpcClientTest.java index 680627617..5c8b75d3d 100644 --- a/common/src/test/java/com/alibaba/nacos/common/remote/client/RpcClientTest.java +++ b/common/src/test/java/com/alibaba/nacos/common/remote/client/RpcClientTest.java @@ -21,6 +21,7 @@ import com.alibaba.nacos.api.remote.RequestCallBack; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.ErrorResponse; import com.alibaba.nacos.common.remote.ConnectionType; +import com.alibaba.nacos.common.remote.client.grpc.DefaultGrpcClientConfig; import com.alibaba.nacos.common.remote.client.grpc.GrpcConnection; import org.junit.After; import org.junit.Assert; @@ -36,10 +37,12 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.Queue; -import java.util.concurrent.TimeUnit; +import java.util.Random; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.atLeastOnce; @@ -49,24 +52,21 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class RpcClientTest { RpcClient rpcClient; - Field keepAliveTimeField; - Field serverListFactoryField; Field reconnectionSignalField; - Field retryTimesField; - - Field timeoutMillsField; - Method resolveServerInfoMethod; + Method healthCheck; + Answer runAsSync; Answer notInvoke; @@ -77,9 +77,47 @@ public class RpcClientTest { @Mock Connection connection; + RpcClientConfig rpcClientConfig; + @Before public void setUp() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException { - rpcClient = spy(new RpcClient("testClient") { + rpcClientConfig = spy(new RpcClientConfig() { + @Override + public String name() { + return "test"; + } + + @Override + public int retryTimes() { + return 1; + } + + @Override + public long timeOutMills() { + return 3000L; + } + + @Override + public long connectionKeepAlive() { + return 5000L; + } + + @Override + public int healthCheckRetryTimes() { + return 1; + } + + @Override + public long healthCheckTimeOut() { + return 3000L; + } + + @Override + public Map labels() { + return new HashMap<>(); + } + }); + rpcClient = spy(new RpcClient(rpcClientConfig) { @Override public ConnectionType getConnectionType() { return null; @@ -96,9 +134,6 @@ public class RpcClientTest { } }); - keepAliveTimeField = RpcClient.class.getDeclaredField("keepAliveTime"); - keepAliveTimeField.setAccessible(true); - serverListFactoryField = RpcClient.class.getDeclaredField("serverListFactory"); serverListFactoryField.setAccessible(true); @@ -108,21 +143,12 @@ public class RpcClientTest { modifiersField1.setAccessible(true); modifiersField1.setInt(reconnectionSignalField, reconnectionSignalField.getModifiers() & ~Modifier.FINAL); - retryTimesField = RpcClient.class.getDeclaredField("RETRY_TIMES"); - retryTimesField.setAccessible(true); - Field modifiersField3 = Field.class.getDeclaredField("modifiers"); - modifiersField3.setAccessible(true); - modifiersField3.setInt(retryTimesField, retryTimesField.getModifiers() & ~Modifier.FINAL); - - timeoutMillsField = RpcClient.class.getDeclaredField("DEFAULT_TIMEOUT_MILLS"); - timeoutMillsField.setAccessible(true); - Field modifiersField4 = Field.class.getDeclaredField("modifiers"); - modifiersField4.setAccessible(true); - modifiersField4.setInt(timeoutMillsField, timeoutMillsField.getModifiers() & ~Modifier.FINAL); - resolveServerInfoMethod = RpcClient.class.getDeclaredMethod("resolveServerInfo", String.class); resolveServerInfoMethod.setAccessible(true); + healthCheck = RpcClient.class.getDeclaredMethod("healthCheck"); + healthCheck.setAccessible(true); + runAsSync = invocationOnMock -> { Runnable runnable = (Runnable) invocationOnMock.getArguments()[0]; runnable.run(); @@ -134,7 +160,7 @@ public class RpcClientTest { @After public void tearDown() throws IllegalAccessException { - rpcClient.labels.clear(); + rpcClientConfig.labels().clear(); rpcClient.rpcClientStatus.set(RpcClientStatus.WAIT_INIT); serverListFactoryField.set(rpcClient, null); ((Queue) reconnectionSignalField.get(rpcClient)).clear(); @@ -147,13 +173,48 @@ public class RpcClientTest { public void testInitServerListFactory() { rpcClient.rpcClientStatus.set(RpcClientStatus.WAIT_INIT); rpcClient.serverListFactory(serverListFactory); - Assert.assertEquals(RpcClientStatus.INITIALIZED, rpcClient.rpcClientStatus.get()); + assertEquals(RpcClientStatus.INITIALIZED, rpcClient.rpcClientStatus.get()); rpcClient.rpcClientStatus.set(RpcClientStatus.INITIALIZED); rpcClient.serverListFactory(serverListFactory); - Assert.assertEquals(RpcClientStatus.INITIALIZED, rpcClient.rpcClientStatus.get()); + assertEquals(RpcClientStatus.INITIALIZED, rpcClient.rpcClientStatus.get()); - RpcClient client1 = new RpcClient("test", serverListFactory) { + RpcClient client1 = new RpcClient(new RpcClientConfig() { + @Override + public String name() { + return "test"; + } + + @Override + public int retryTimes() { + return 3; + } + + @Override + public long timeOutMills() { + return 3000L; + } + + @Override + public long connectionKeepAlive() { + return 5000L; + } + + @Override + public int healthCheckRetryTimes() { + return 1; + } + + @Override + public long healthCheckTimeOut() { + return 3000L; + } + + @Override + public Map labels() { + return new HashMap<>(); + } + }, serverListFactory) { @Override public ConnectionType getConnectionType() { return null; @@ -169,9 +230,9 @@ public class RpcClientTest { return null; } }; - Assert.assertEquals(RpcClientStatus.INITIALIZED, client1.rpcClientStatus.get()); + assertEquals(RpcClientStatus.INITIALIZED, client1.rpcClientStatus.get()); - RpcClient client2 = new RpcClient(serverListFactory) { + RpcClient client2 = new RpcClient(rpcClientConfig, serverListFactory) { @Override public ConnectionType getConnectionType() { return null; @@ -187,30 +248,21 @@ public class RpcClientTest { return null; } }; - Assert.assertEquals(RpcClientStatus.INITIALIZED, client2.rpcClientStatus.get()); + assertEquals(RpcClientStatus.INITIALIZED, client2.rpcClientStatus.get()); } @Test public void testLabels() { - rpcClient.labels(Collections.singletonMap("labelKey1", "labelValue1")); + when(rpcClientConfig.labels()).thenReturn(Collections.singletonMap("labelKey1", "labelValue1")); Map.Entry element = rpcClient.getLabels().entrySet().iterator().next(); - Assert.assertEquals("labelKey1", element.getKey()); - Assert.assertEquals("labelValue1", element.getValue()); + assertEquals("labelKey1", element.getKey()); + assertEquals("labelValue1", element.getValue()); // accumulate labels - rpcClient.labels(Collections.singletonMap("labelKey2", "labelValue2")); - Assert.assertEquals(2, rpcClient.getLabels().size()); - } - - @Test - public void testKeepAlive() throws IllegalAccessException { - rpcClient.keepAlive(1, TimeUnit.SECONDS); - long keepAliveTime = (long) keepAliveTimeField.get(rpcClient); - Assert.assertEquals(1000L, keepAliveTime); - - rpcClient.keepAlive(1, TimeUnit.MINUTES); - keepAliveTime = (long) keepAliveTimeField.get(rpcClient); - Assert.assertEquals(60000L, keepAliveTime); + Map map = new HashMap<>(); + map.put("labelKey2", "labelValue2"); + when(rpcClientConfig.labels()).thenReturn(map); + assertEquals(1, rpcClient.getLabels().size()); } @Test @@ -221,7 +273,7 @@ public class RpcClientTest { rpcClient.onServerListChange(); int afterSize = ((Queue) reconnectionSignalField.get(rpcClient)).size(); - Assert.assertEquals(beforeSize, afterSize); + assertEquals(beforeSize, afterSize); } @Test @@ -232,7 +284,7 @@ public class RpcClientTest { rpcClient.onServerListChange(); int afterSize = ((Queue) reconnectionSignalField.get(rpcClient)).size(); - Assert.assertEquals(beforeSize, afterSize); + assertEquals(beforeSize, afterSize); } @Test @@ -242,11 +294,11 @@ public class RpcClientTest { rpcClient.serverListFactory(serverListFactory); rpcClient.currentConnection = new GrpcConnection(new RpcClient.ServerInfo("10.10.10.10", 8848), null); doReturn(Collections.singletonList("")).when(serverListFactory).getServerList(); - + when(serverListFactory.getServerList()).thenReturn(Collections.singletonList("127.0.0.1")); rpcClient.onServerListChange(); int afterSize = ((Queue) reconnectionSignalField.get(rpcClient)).size(); - Assert.assertEquals(beforeSize + 1, afterSize); + assertEquals(beforeSize + 1, afterSize); } @Test @@ -255,35 +307,35 @@ public class RpcClientTest { rpcClient.serverListFactory(serverListFactory); rpcClient.currentConnection = new GrpcConnection(new RpcClient.ServerInfo("10.10.10.10", 8848), null); doReturn(Collections.singletonList("http://10.10.10.10:8848")).when(serverListFactory).getServerList(); - rpcClient.onServerListChange(); int afterSize = ((Queue) reconnectionSignalField.get(rpcClient)).size(); - Assert.assertEquals(beforeSize, afterSize); + assertEquals(beforeSize, afterSize); } @Test public void testResolveServerInfo1() throws InvocationTargetException, IllegalAccessException { - Assert.assertEquals(":8848", - ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "")).getAddress()); - Assert.assertEquals("10.10.10.10:8848", + assertEquals("10.10.10.10:8848", ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "10.10.10.10::8848")).getAddress()); - Assert.assertEquals("10.10.10.10:8848", + assertEquals("10.10.10.10:8848", ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "10.10.10.10:8848")).getAddress()); - Assert.assertEquals("10.10.10.10:8848", ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, - "http://10.10.10.10:8848")).getAddress()); - Assert.assertEquals("10.10.10.10:8848", ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, - "http://10.10.10.10::8848")).getAddress()); - Assert.assertEquals("10.10.10.10:8848", + assertEquals("10.10.10.10:8848", + ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "http://10.10.10.10:8848")) + .getAddress()); + assertEquals("10.10.10.10:8848", + ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "http://10.10.10.10::8848")) + .getAddress()); + assertEquals("10.10.10.10:8848", ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "http://10.10.10.10")).getAddress()); - Assert.assertEquals("10.10.10.10:8848", ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, - "https://10.10.10.10::8848")).getAddress()); + assertEquals("10.10.10.10:8848", + ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "https://10.10.10.10::8848")) + .getAddress()); } @Test public void testResolveServerInfo2() throws InvocationTargetException, IllegalAccessException { System.setProperty("nacos.server.port", "4424"); - Assert.assertEquals("10.10.10.10:4424", + assertEquals("10.10.10.10:4424", ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "http://10.10.10.10")).getAddress()); } @@ -308,7 +360,7 @@ public class RpcClientTest { rpcClient.rpcClientStatus.set(RpcClientStatus.RUNNING); rpcClient.currentConnection = connection; doReturn(new ErrorResponse()).when(connection).request(any(), anyLong()); - + rpcClient.request(null, 10000); } @@ -327,7 +379,7 @@ public class RpcClientTest { exception = e; } - Assert.assertEquals(RpcClientStatus.UNHEALTHY, rpcClient.rpcClientStatus.get()); + assertEquals(RpcClientStatus.UNHEALTHY, rpcClient.rpcClientStatus.get()); verify(rpcClient).switchServerAsync(); Assert.assertNotNull(exception); } @@ -349,7 +401,7 @@ public class RpcClientTest { RequestCallBack requestCallBack = mock(RequestCallBack.class); doReturn(10000L).when(requestCallBack).getTimeout(); Exception exception = null; - + try { rpcClient.asyncRequest(null, requestCallBack); } catch (NacosException e) { @@ -359,7 +411,7 @@ public class RpcClientTest { verify(connection, atLeastOnce()).asyncRequest(any(), any()); verify(rpcClient).switchServerAsyncOnRequestFail(); Assert.assertNotNull(exception); - Assert.assertEquals(RpcClientStatus.UNHEALTHY, rpcClient.rpcClientStatus.get()); + assertEquals(RpcClientStatus.UNHEALTHY, rpcClient.rpcClientStatus.get()); } @Test(expected = NacosException.class) @@ -370,9 +422,10 @@ public class RpcClientTest { } @Test - public void testRequestFutureWhenRetryReachMaxRetryTimesThenSwitchServer() throws NacosException, IllegalAccessException { - timeoutMillsField.set(rpcClient, 5000L); - retryTimesField.set(rpcClient, 3); + public void testRequestFutureWhenRetryReachMaxRetryTimesThenSwitchServer() + throws NacosException, IllegalAccessException { + when(rpcClientConfig.timeOutMills()).thenReturn(5000L); + when(rpcClientConfig.retryTimes()).thenReturn(3); rpcClient.rpcClientStatus.set(RpcClientStatus.RUNNING); rpcClient.currentConnection = connection; doThrow(NacosException.class).when(connection).requestFuture(any()); @@ -387,22 +440,57 @@ public class RpcClientTest { verify(connection, times(3)).requestFuture(any()); verify(rpcClient).switchServerAsyncOnRequestFail(); Assert.assertNotNull(exception); - Assert.assertEquals(RpcClientStatus.UNHEALTHY, rpcClient.rpcClientStatus.get()); + assertEquals(RpcClientStatus.UNHEALTHY, rpcClient.rpcClientStatus.get()); } @Test public void testRpcClientShutdownWhenClientDidntStart() throws NacosException { - RpcClient rpcClient = new RpcClient("test-client") { + RpcClient rpcClient = new RpcClient(new RpcClientConfig() { + @Override + public String name() { + return "test-client"; + } + + @Override + public int retryTimes() { + return 3; + } + + @Override + public long timeOutMills() { + return 3000L; + } + + @Override + public long connectionKeepAlive() { + return 5000L; + } + + @Override + public int healthCheckRetryTimes() { + return 1; + } + + @Override + public long healthCheckTimeOut() { + return 3000L; + } + + @Override + public Map labels() { + return new HashMap<>(); + } + }) { @Override public ConnectionType getConnectionType() { return null; } - + @Override public int rpcPortOffset() { return 0; } - + @Override public Connection connectToServer(ServerInfo serverInfo) throws Exception { return null; @@ -411,4 +499,140 @@ public class RpcClientTest { rpcClient.shutdown(); } -} \ No newline at end of file + + @Test + public void testHealthCheck() throws IllegalAccessException, NacosException { + Random random = new Random(); + int retry = random.nextInt(10); + when(rpcClientConfig.healthCheckRetryTimes()).thenReturn(retry); + rpcClient.rpcClientStatus.set(RpcClientStatus.RUNNING); + rpcClient.currentConnection = connection; + doThrow(new NacosException()).when(connection).request(any(), anyLong()); + try { + healthCheck.invoke(rpcClient); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + verify(connection, times(retry + 1)).request(any(), anyLong()); + } + + @Test + public void testNextRpcServerForIpv4WithPort() { + RpcClient rpcClient = buildTestNextRpcServerClient(); + rpcClient.serverListFactory(serverListFactory); + when(serverListFactory.genNextServer()).thenReturn("127.0.0.1:7777"); + RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); + assertEquals("127.0.0.1:7777", actual.getAddress()); + assertEquals("127.0.0.1", actual.getServerIp()); + assertEquals(7777, actual.getServerPort()); + } + + @Test + public void testNextRpcServerForIpv4WithoutPort() { + RpcClient rpcClient = buildTestNextRpcServerClient(); + rpcClient.serverListFactory(serverListFactory); + when(serverListFactory.genNextServer()).thenReturn("127.0.0.1"); + RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); + assertEquals("127.0.0.1:8848", actual.getAddress()); + assertEquals("127.0.0.1", actual.getServerIp()); + assertEquals(8848, actual.getServerPort()); + } + + @Test + public void testNextRpcServerForIpv6WithPort() { + RpcClient rpcClient = buildTestNextRpcServerClient(); + rpcClient.serverListFactory(serverListFactory); + when(serverListFactory.genNextServer()).thenReturn("[fe80::35ba:6827:c5ff:d161%11]:7777"); + RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); + assertEquals("[fe80::35ba:6827:c5ff:d161%11]:7777", actual.getAddress()); + assertEquals("[fe80::35ba:6827:c5ff:d161%11]", actual.getServerIp()); + assertEquals(7777, actual.getServerPort()); + } + + @Test + public void testNextRpcServerForIpv6WithoutPort() { + RpcClient rpcClient = buildTestNextRpcServerClient(); + rpcClient.serverListFactory(serverListFactory); + when(serverListFactory.genNextServer()).thenReturn("[fe80::35ba:6827:c5ff:d161%11]"); + RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); + assertEquals("[fe80::35ba:6827:c5ff:d161%11]:8848", actual.getAddress()); + assertEquals("[fe80::35ba:6827:c5ff:d161%11]", actual.getServerIp()); + assertEquals(8848, actual.getServerPort()); + } + + @Test + public void testNextRpcServerForDomainWithPort() { + RpcClient rpcClient = buildTestNextRpcServerClient(); + rpcClient.serverListFactory(serverListFactory); + when(serverListFactory.genNextServer()).thenReturn("nacos.io:7777"); + RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); + assertEquals("nacos.io:7777", actual.getAddress()); + assertEquals("nacos.io", actual.getServerIp()); + assertEquals(7777, actual.getServerPort()); + } + + @Test + public void testNextRpcServerForDomainWithoutPort() { + RpcClient rpcClient = buildTestNextRpcServerClient(); + rpcClient.serverListFactory(serverListFactory); + when(serverListFactory.genNextServer()).thenReturn("nacos.io"); + RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); + assertEquals("nacos.io:8848", actual.getAddress()); + assertEquals("nacos.io", actual.getServerIp()); + assertEquals(8848, actual.getServerPort()); + } + + @Test + public void testNextRpcServerForLocalhostWithPort() { + RpcClient rpcClient = buildTestNextRpcServerClient(); + rpcClient.serverListFactory(serverListFactory); + when(serverListFactory.genNextServer()).thenReturn("localhost:7777"); + RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); + assertEquals("localhost:7777", actual.getAddress()); + assertEquals("localhost", actual.getServerIp()); + assertEquals(7777, actual.getServerPort()); + } + + @Test + public void testNextRpcServerForLocalhostWithoutPort() { + RpcClient rpcClient = buildTestNextRpcServerClient(); + rpcClient.serverListFactory(serverListFactory); + when(serverListFactory.genNextServer()).thenReturn("localhost"); + RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); + assertEquals("localhost:8848", actual.getAddress()); + assertEquals("localhost", actual.getServerIp()); + assertEquals(8848, actual.getServerPort()); + } + + @Test(expected = IllegalArgumentException.class) + public void testNextRpcServerForEmpty() { + RpcClient rpcClient = buildTestNextRpcServerClient(); + rpcClient.serverListFactory(serverListFactory); + when(serverListFactory.genNextServer()).thenReturn(""); + rpcClient.nextRpcServer(); + } + + private RpcClient buildTestNextRpcServerClient() { + return new RpcClient(DefaultGrpcClientConfig.newBuilder().build()) { + @Override + public ConnectionType getConnectionType() { + return null; + } + + @Override + public int rpcPortOffset() { + return 0; + } + + @Override + public Connection connectToServer(ServerInfo serverInfo) throws Exception { + return null; + } + + @Override + public ServerInfo nextRpcServer() { + return super.nextRpcServer(); + } + }; + } +} diff --git a/common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClientTest.java b/common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClientTest.java index 5a81ab3ed..8b67b574a 100644 --- a/common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClientTest.java +++ b/common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClientTest.java @@ -33,38 +33,41 @@ import static org.mockito.Mockito.spy; @RunWith(MockitoJUnitRunner.class) public class GrpcClientTest { - + GrpcClient grpcClient; - + Method createNewManagedChannelMethod; - + Method createNewChannelStubMethod; - + ManagedChannel managedChannel; - + RpcClient.ServerInfo serverInfo; - + @Before public void setUp() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - grpcClient = spy(new GrpcClient("testClient") { + GrpcClientConfig clientConfig = DefaultGrpcClientConfig.newBuilder().setName("testClient").build(); + grpcClient = spy(new GrpcClient(clientConfig) { @Override public int rpcPortOffset() { return 1000; } }); RpcClient.ServerInfo serverInfo = spy(new RpcClient.ServerInfo("10.10.10.10", 8848)); - createNewManagedChannelMethod = GrpcClient.class.getDeclaredMethod("createNewManagedChannel", String.class, int.class); + createNewManagedChannelMethod = GrpcClient.class.getDeclaredMethod("createNewManagedChannel", String.class, + int.class); createNewManagedChannelMethod.setAccessible(true); int port = serverInfo.getServerPort() + grpcClient.rpcPortOffset(); - managedChannel = (ManagedChannel) createNewManagedChannelMethod.invoke(grpcClient, serverInfo.getServerIp(), port); + managedChannel = (ManagedChannel) createNewManagedChannelMethod.invoke(grpcClient, serverInfo.getServerIp(), + port); } - + @Test public void testCreateNewManagedChannel() throws InvocationTargetException, IllegalAccessException { GrpcConnection grpcConnection = new GrpcConnection(serverInfo, null); grpcConnection.setChannel(managedChannel); } - + @Test public void createNewChannelStub() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { createNewChannelStubMethod = GrpcClient.class.getDeclaredMethod("createNewChannelStub", ManagedChannel.class); @@ -72,10 +75,10 @@ public class GrpcClientTest { Object invoke = createNewChannelStubMethod.invoke(grpcClient, managedChannel); Assert.assertTrue(invoke instanceof RequestGrpc.RequestFutureStub); } - + @After public void close() { managedChannel.shutdownNow(); } - + } diff --git a/common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/GrpcConstantsTest.java b/common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/GrpcConstantsTest.java new file mode 100644 index 000000000..f74adfdd9 --- /dev/null +++ b/common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/GrpcConstantsTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 1999-2020 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.nacos.common.remote.client.grpc; + +import org.junit.Test; + +import java.lang.reflect.Field; + +import static org.junit.Assert.assertEquals; + +public class GrpcConstantsTest { + + @Test + public void testGetRpcParams() { + Class clazz = GrpcConstants.class; + Field[] declaredFields = clazz.getDeclaredFields(); + int i = 0; + for (Field declaredField : declaredFields) { + declaredField.setAccessible(true); + if (declaredField.getType().equals(String.class) && null != declaredField.getAnnotation( + GrpcConstants.GRpcConfigLabel.class)) { + i++; + } + } + assertEquals(i, GrpcConstants.getRpcParams().size()); + } +} diff --git a/common/src/test/java/com/alibaba/nacos/common/utils/ConcurrentHashSetTest.java b/common/src/test/java/com/alibaba/nacos/common/utils/ConcurrentHashSetTest.java index 4a24f2216..752390de8 100644 --- a/common/src/test/java/com/alibaba/nacos/common/utils/ConcurrentHashSetTest.java +++ b/common/src/test/java/com/alibaba/nacos/common/utils/ConcurrentHashSetTest.java @@ -17,12 +17,12 @@ package com.alibaba.nacos.common.utils; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import java.util.ConcurrentModificationException; import java.util.HashSet; import java.util.Random; import java.util.Set; +import java.util.concurrent.TimeUnit; /** * ConcurrentHashSet Test. @@ -32,108 +32,200 @@ import java.util.Set; */ public class ConcurrentHashSetTest { - Set concurrentHashSet; - - @Before - public void setUp() { - concurrentHashSet = new ConcurrentHashSet<>(); - concurrentHashSet.add(1); - concurrentHashSet.add(2); - concurrentHashSet.add(3); - concurrentHashSet.add(4); - concurrentHashSet.add(5); + @Test + public void testBasicOps() { + Set set = new ConcurrentHashSet<>(); + + // addition + Assert.assertTrue(set.add(0)); + Assert.assertTrue(set.add(1)); + Assert.assertTrue(set.contains(0)); + Assert.assertTrue(set.contains(1)); + Assert.assertFalse(set.contains(-1)); + Assert.assertEquals(2, set.size()); + + // iter + for (int i : set) { + Assert.assertTrue(i == 0 || i == 1); + } + + // removal + Assert.assertTrue(set.remove(0)); + Assert.assertFalse(set.remove(0)); + Assert.assertFalse(set.contains(0)); + Assert.assertTrue(set.contains(1)); + Assert.assertEquals(1, set.size()); + + // clear + Assert.assertFalse(set.isEmpty()); + set.clear(); + Assert.assertEquals(0, set.size()); + Assert.assertTrue(set.isEmpty()); + } + + @Test + public void testMultiThread() throws Exception { + int count = 5; + SetMultiThreadChecker hashSetChecker = new SetMultiThreadChecker(new HashSet<>()); + hashSetChecker.start(); + while (!hashSetChecker.hasConcurrentError() && hashSetChecker.isRunning()) { + TimeUnit.SECONDS.sleep(1); + if (count <= 0) { + hashSetChecker.stop(); + } + count--; + } + Assert.assertTrue(hashSetChecker.hasConcurrentError()); + + count = 5; + SetMultiThreadChecker concurrentSetChecker = new SetMultiThreadChecker(new ConcurrentHashSet<>()); + concurrentSetChecker.start(); + while (!concurrentSetChecker.hasConcurrentError() && concurrentSetChecker.isRunning()) { + TimeUnit.SECONDS.sleep(1); + if (count == 0) { + concurrentSetChecker.stop(); + } + count--; + } + Assert.assertFalse(concurrentSetChecker.hasConcurrentError()); + } + + static class SetMultiThreadChecker { + + private final AddDataThread addThread; + + private final DeleteDataThread deleteThread; + + private final IteratorThread iteratorThread; + + public SetMultiThreadChecker(Set setToCheck) { + for (int i = 0; i < 1000; i++) { + setToCheck.add(i); + } + this.addThread = new AddDataThread(setToCheck); + this.deleteThread = new DeleteDataThread(setToCheck); + this.iteratorThread = new IteratorThread(setToCheck); + } + + public void start() { + new Thread(addThread).start(); + new Thread(deleteThread).start(); + new Thread(iteratorThread).start(); + } + + public boolean hasConcurrentError() { + return addThread.hasConcurrentError() || deleteThread.hasConcurrentError() || iteratorThread.hasConcurrentError(); + } + + public boolean isRunning() { + return addThread.isRunning() || deleteThread.isRunning() || iteratorThread.isRunning(); + } + + public void stop() { + addThread.stop(); + deleteThread.stop(); + iteratorThread.stop(); + } } - @Test - public void size() { - Assert.assertEquals(concurrentHashSet.size(), 5); - } + abstract static class ConcurrentCheckThread implements Runnable { - @Test - public void contains() { - Assert.assertTrue(concurrentHashSet.contains(1)); - } + protected final Set hashSet; - @Test - public void testMultithreaded() { - try { - concurrentHashSet = new HashSet<>(); - executeThread(); - } catch (Exception e) { - Assert.assertTrue(e instanceof ConcurrentModificationException); + protected boolean concurrentError = false; + + protected boolean finish = false; + + public ConcurrentCheckThread(Set hashSet) { + this.hashSet = hashSet; + } + + public boolean hasConcurrentError() { + return concurrentError; } - try { - concurrentHashSet = new ConcurrentHashSet<>(); - executeThread(); - } catch (Exception e) { - Assert.assertNull(e); - } - } - - /** - * execute muti thread. - */ - public void executeThread() throws Exception { - for (int i = 0; i < 1000; i++) { - concurrentHashSet.add(i); + public void stop() { + finish = true; } - new Thread(new AddDataThread(concurrentHashSet)).start(); - new Thread(new DeleteDataThread(concurrentHashSet)).start(); - new Thread(new IteratorThread(concurrentHashSet)).start(); + public boolean isRunning() { + return !finish; + } + + @Override + public void run() { + try { + while (isRunning()) { + process(); + } + } catch (ConcurrentModificationException e) { + concurrentError = true; + } finally { + finish = true; + } + } + + protected abstract void process(); } //add data thread - static class AddDataThread implements Runnable { - Set hashSet; + static class AddDataThread extends ConcurrentCheckThread implements Runnable { public AddDataThread(Set hashSet) { - this.hashSet = hashSet; + super(hashSet); } @Override - public void run() { - while (true) { - int random = new Random().nextInt(); - hashSet.add(random); - } + protected void process() { + int random = new Random().nextInt(1000); + hashSet.add(random); } + } // delete data thread - static class DeleteDataThread implements Runnable { - Set hashSet; + static class DeleteDataThread extends ConcurrentCheckThread implements Runnable { public DeleteDataThread(Set hashSet) { - this.hashSet = hashSet; + super(hashSet); } @Override - public void run() { + protected void process() { int random = new Random().nextInt(1000); - while (true) { - hashSet.remove(random); - } + hashSet.remove(random); } + } - static class IteratorThread implements Runnable { - - Set hashSet; + static class IteratorThread extends ConcurrentCheckThread implements Runnable { public IteratorThread(Set hashSet) { - this.hashSet = hashSet; + super(hashSet); } @Override public void run() { System.out.println("start -- hashSet.size() : " + hashSet.size()); - for (Integer str : hashSet) { - System.out.println("value : " + str); + Integer f = null; + try { + while (isRunning()) { + for (Integer i : hashSet) { + f = i; + } + } + } catch (ConcurrentModificationException e) { + concurrentError = true; + } finally { + finish = true; } + System.out.println("finished at " + f); System.out.println("end -- hashSet.size() : " + hashSet.size()); } + + @Override + protected void process() { + } } } diff --git a/common/src/test/java/com/alibaba/nacos/common/utils/JacksonUtilsTest.java b/common/src/test/java/com/alibaba/nacos/common/utils/JacksonUtilsTest.java index b6129d3f3..44e0f29ed 100644 --- a/common/src/test/java/com/alibaba/nacos/common/utils/JacksonUtilsTest.java +++ b/common/src/test/java/com/alibaba/nacos/common/utils/JacksonUtilsTest.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.type.TypeReference; @@ -474,6 +475,7 @@ public class JacksonUtilsTest { Assert.assertEquals(999, restResult.getData().get("integer")); } + @JsonPropertyOrder({ "aLong", "aInteger", "aBoolean"}) static class TestOfAtomicObject { public AtomicLong aLong = new AtomicLong(0); @@ -558,6 +560,7 @@ public class JacksonUtilsTest { } } + @JsonPropertyOrder({ "value", "key" }) static class TestOfGetter { public String getKey() { @@ -749,4 +752,4 @@ public class JacksonUtilsTest { return result; } } -} \ No newline at end of file +} diff --git a/config/pom.xml b/config/pom.xml index ee3356b8a..6dce89741 100644 --- a/config/pom.xml +++ b/config/pom.xml @@ -117,6 +117,10 @@ org.yaml snakeyaml + + com.alibaba.nacos + nacos-datasource-plugin + diff --git a/config/src/main/java/com/alibaba/nacos/config/server/constant/Constants.java b/config/src/main/java/com/alibaba/nacos/config/server/constant/Constants.java index 21600d849..c1324356c 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/constant/Constants.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/constant/Constants.java @@ -37,6 +37,11 @@ public class Constants { */ public static final String BASE_DIR = "config-data"; + /** + * The derby base dir. + */ + public static final String DERBY_BASE_DIR = "derby-data"; + /** * Back up file directory in server side. * @@ -111,6 +116,8 @@ public class Constants { public static final String BASE_PATH = "/v1/cs"; + public static final String BASE_V2_PATH = "/v2/cs"; + public static final String OPS_CONTROLLER_PATH = BASE_PATH + "/ops"; public static final String CAPACITY_CONTROLLER_PATH = BASE_PATH + "/capacity"; @@ -119,10 +126,14 @@ public class Constants { public static final String CONFIG_CONTROLLER_PATH = BASE_PATH + "/configs"; + public static final String CONFIG_CONTROLLER_V2_PATH = BASE_V2_PATH + "/config"; + public static final String HEALTH_CONTROLLER_PATH = BASE_PATH + "/health"; public static final String HISTORY_CONTROLLER_PATH = BASE_PATH + "/history"; + public static final String HISTORY_CONTROLLER_V2_PATH = BASE_V2_PATH + "/history"; + public static final String LISTENER_CONTROLLER_PATH = BASE_PATH + "/listener"; public static final String NAMESPACE_CONTROLLER_PATH = BASE_PATH + "/namespaces"; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java index e238003b4..2d011584f 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java @@ -22,7 +22,6 @@ import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.model.RestResult; import com.alibaba.nacos.common.model.RestResultUtils; import com.alibaba.nacos.common.utils.DateFormatUtils; -import com.alibaba.nacos.common.utils.MapUtil; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.common.utils.Pair; import com.alibaba.nacos.common.utils.StringUtils; @@ -38,10 +37,12 @@ import com.alibaba.nacos.config.server.model.Page; import com.alibaba.nacos.config.server.model.SameConfigPolicy; import com.alibaba.nacos.config.server.model.SampleResult; import com.alibaba.nacos.config.server.model.event.ConfigDataChangeEvent; +import com.alibaba.nacos.config.server.model.ConfigRequestInfo; +import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.monitor.MetricsMonitor; import com.alibaba.nacos.config.server.result.code.ResultCodeEnum; -import com.alibaba.nacos.config.server.service.AggrWhitelist; import com.alibaba.nacos.config.server.service.ConfigChangePublisher; +import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.ConfigSubService; import com.alibaba.nacos.config.server.service.repository.PersistService; import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; @@ -108,12 +109,15 @@ public class ConfigController { private final PersistService persistService; + private final ConfigOperationService configOperationService; + private final ConfigSubService configSubService; - public ConfigController(ConfigServletInner inner, PersistService persistService, + public ConfigController(ConfigServletInner inner, PersistService persistService, ConfigOperationService configOperationService, ConfigSubService configSubService) { this.inner = inner; this.persistService = persistService; + this.configOperationService = configOperationService; this.configSubService = configSubService; } @@ -142,16 +146,6 @@ public class ConfigController { @RequestParam(value = "type", required = false) String type, @RequestParam(value = "schema", required = false) String schema) throws NacosException { - final String srcIp = RequestUtil.getRemoteIp(request); - final String requestIpApp = RequestUtil.getAppName(request); - if (StringUtils.isBlank(srcUser)) { - srcUser = RequestUtil.getSrcUserName(request); - } - //check type - if (!ConfigType.isValidType(type)) { - type = ConfigType.getDefaultType().getType(); - } - // encrypted Pair pair = EncryptionHandler.encryptHandler(dataId, content); content = pair.getSecond(); @@ -160,47 +154,37 @@ public class ConfigController { ParamUtils.checkTenant(tenant); ParamUtils.checkParam(dataId, group, "datumId", content); ParamUtils.checkParam(tag); - Map configAdvanceInfo = new HashMap<>(10); - MapUtil.putIfValNoNull(configAdvanceInfo, "config_tags", configTags); - MapUtil.putIfValNoNull(configAdvanceInfo, "desc", desc); - MapUtil.putIfValNoNull(configAdvanceInfo, "use", use); - MapUtil.putIfValNoNull(configAdvanceInfo, "effect", effect); - MapUtil.putIfValNoNull(configAdvanceInfo, "type", type); - MapUtil.putIfValNoNull(configAdvanceInfo, "schema", schema); - ParamUtils.checkParam(configAdvanceInfo); - - if (AggrWhitelist.isAggrDataId(dataId)) { - LOGGER.warn("[aggr-conflict] {} attempt to publish single data, {}, {}", RequestUtil.getRemoteIp(request), - dataId, group); - throw new NacosException(NacosException.NO_RIGHT, "dataId:" + dataId + " is aggr"); + + ConfigForm configForm = new ConfigForm(); + configForm.setDataId(dataId); + configForm.setGroup(group); + configForm.setNamespaceId(tenant); + configForm.setContent(content); + configForm.setTag(tag); + configForm.setAppName(appName); + configForm.setSrcUser(srcUser); + configForm.setConfigTags(configTags); + configForm.setDesc(desc); + configForm.setUse(use); + configForm.setEffect(effect); + configForm.setType(type); + configForm.setSchema(schema); + + if (StringUtils.isBlank(srcUser)) { + configForm.setSrcUser(RequestUtil.getSrcUserName(request)); } - - final Timestamp time = TimeUtils.getCurrentTime(); - String betaIps = request.getHeader("betaIps"); - ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); - configInfo.setType(type); + if (!ConfigType.isValidType(type)) { + configForm.setType(ConfigType.getDefaultType().getType()); + } + + ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); + configRequestInfo.setSrcIp(RequestUtil.getRemoteIp(request)); + configRequestInfo.setRequestIpApp(RequestUtil.getAppName(request)); + configRequestInfo.setBetaIps(request.getHeader("betaIps")); + String encryptedDataKey = pair.getFirst(); - configInfo.setEncryptedDataKey(encryptedDataKey); - if (StringUtils.isBlank(betaIps)) { - if (StringUtils.isBlank(tag)) { - persistService.insertOrUpdate(srcIp, srcUser, configInfo, time, configAdvanceInfo, false); - ConfigChangePublisher.notifyConfigChange( - new ConfigDataChangeEvent(false, dataId, group, tenant, time.getTime())); - } else { - persistService.insertOrUpdateTag(configInfo, tag, srcIp, srcUser, time, false); - ConfigChangePublisher.notifyConfigChange( - new ConfigDataChangeEvent(false, dataId, group, tenant, tag, time.getTime())); - } - } else { - // beta publish - configInfo.setEncryptedDataKey(encryptedDataKey); - persistService.insertOrUpdateBeta(configInfo, betaIps, srcIp, srcUser, time, false); - ConfigChangePublisher.notifyConfigChange( - new ConfigDataChangeEvent(true, dataId, group, tenant, time.getTime())); - } - ConfigTraceService.logPersistenceEvent(dataId, group, tenant, requestIpApp, time.getTime(), - InetUtils.getSelfIP(), ConfigTraceService.PERSISTENCE_EVENT_PUB, content); - return true; + + return configOperationService.publishConfig(configForm, configRequestInfo, encryptedDataKey); } /** @@ -276,19 +260,11 @@ public class ConfigController { ParamUtils.checkTenant(tenant); ParamUtils.checkParam(dataId, group, "datumId", "rm"); ParamUtils.checkParam(tag); + String clientIp = RequestUtil.getRemoteIp(request); String srcUser = RequestUtil.getSrcUserName(request); - if (StringUtils.isBlank(tag)) { - persistService.removeConfigInfo(dataId, group, tenant, clientIp, srcUser); - } else { - persistService.removeConfigInfoTag(dataId, group, tenant, tag, clientIp, srcUser); - } - final Timestamp time = TimeUtils.getCurrentTime(); - ConfigTraceService.logPersistenceEvent(dataId, group, tenant, null, time.getTime(), clientIp, - ConfigTraceService.PERSISTENCE_EVENT_REMOVE, null); - ConfigChangePublisher.notifyConfigChange( - new ConfigDataChangeEvent(false, dataId, group, tenant, tag, time.getTime())); - return true; + + return configOperationService.deleteConfig(dataId, group, tenant, tag, clientIp, srcUser); } /** @@ -475,10 +451,10 @@ public class ConfigController { Pair pair = EncryptionHandler.decryptHandler(dataId, encryptedDataKey, ci.getContent()); ci.setContent(pair.getSecond()); } - return RestResultUtils.success("stop beta ok", ci); + return RestResultUtils.success("query beta ok", ci); } catch (Throwable e) { - LOGGER.error("remove beta data error", e); - return RestResultUtils.failed("remove beta data error"); + LOGGER.error("query beta data error", e); + return RestResultUtils.failed("query beta data error"); } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigServletInner.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigServletInner.java index cf95b726c..7150e208a 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigServletInner.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigServletInner.java @@ -16,10 +16,14 @@ package com.alibaba.nacos.config.server.controller; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.common.constant.HttpHeaderConsts; +import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.utils.IoUtils; -import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.Pair; +import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.enums.FileTypeEnum; import com.alibaba.nacos.config.server.model.CacheItem; @@ -37,6 +41,8 @@ import com.alibaba.nacos.config.server.utils.Protocol; import com.alibaba.nacos.config.server.utils.RequestUtil; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import javax.servlet.ServletException; @@ -65,6 +71,8 @@ public class ConfigServletInner { private static final int START_LONG_POLLING_VERSION_NUM = 204; + private static final Logger LOGGER = LoggerFactory.getLogger(ConfigServletInner.class); + private final LongPollingService longPollingService; private final PersistService persistService; @@ -116,16 +124,29 @@ public class ConfigServletInner { } /** - * Execute to get config API. + * Execute to get config [API V1]. */ public String doGetConfig(HttpServletRequest request, HttpServletResponse response, String dataId, String group, String tenant, String tag, String isNotify, String clientIp) throws IOException, ServletException { + return doGetConfig(request, response, dataId, group, tenant, tag, isNotify, clientIp, false); + } + + /** + * Execute to get config [API V1] or [API V2]. + */ + public String doGetConfig(HttpServletRequest request, HttpServletResponse response, String dataId, String group, + String tenant, String tag, String isNotify, String clientIp, boolean isV2) + throws IOException, ServletException { boolean notify = false; if (StringUtils.isNotBlank(isNotify)) { notify = Boolean.parseBoolean(isNotify); } + if (isV2) { + response.setHeader(HttpHeaderConsts.CONTENT_TYPE, MediaType.APPLICATION_JSON); + } + final String groupKey = GroupKey2.getKey(dataId, group, tenant); String autoTag = request.getHeader("Vipserver-Tag"); @@ -153,6 +174,10 @@ public class ConfigServletInner { String contentTypeHeader = fileTypeEnum.getContentType(); response.setHeader(HttpHeaderConsts.CONTENT_TYPE, contentTypeHeader); + if (isV2) { + response.setHeader(HttpHeaderConsts.CONTENT_TYPE, MediaType.APPLICATION_JSON); + } + File file = null; ConfigInfoBase configInfoBase = null; PrintWriter out; @@ -200,7 +225,7 @@ public class ConfigServletInner { // no data", // new Object[]{clientIp, groupKey}); - return get404Result(response); + return get404Result(response, isV2); } isSli = true; } @@ -229,9 +254,7 @@ public class ConfigServletInner { // no data", // new Object[]{clientIp, groupKey}); - response.setStatus(HttpServletResponse.SC_NOT_FOUND); - response.getWriter().println("config data not exist"); - return HttpServletResponse.SC_NOT_FOUND + ""; + return get404Result(response, isV2); } } } @@ -250,10 +273,14 @@ public class ConfigServletInner { } if (PropertyUtil.isDirectRead()) { - Pair pair = EncryptionHandler.decryptHandler(dataId, - configInfoBase.getEncryptedDataKey(), configInfoBase.getContent()); + Pair pair = EncryptionHandler + .decryptHandler(dataId, configInfoBase.getEncryptedDataKey(), configInfoBase.getContent()); out = response.getWriter(); - out.print(pair.getSecond()); + if (isV2) { + out.print(JacksonUtils.toJson(Result.success(pair.getSecond()))); + } else { + out.print(pair.getSecond()); + } out.flush(); out.close(); } else { @@ -262,7 +289,11 @@ public class ConfigServletInner { Pair pair = EncryptionHandler.decryptHandler(dataId, encryptedDataKey, fileContent); String decryptContent = pair.getSecond(); out = response.getWriter(); - out.print(decryptContent); + if (isV2) { + out.print(JacksonUtils.toJson(Result.success(decryptContent))); + } else { + out.print(decryptContent); + } out.flush(); out.close(); } @@ -290,15 +321,12 @@ public class ConfigServletInner { .logPullEvent(dataId, group, tenant, requestIpApp, -1, ConfigTraceService.PULL_EVENT_NOTFOUND, -1, requestIp, notify && isSli); - return get404Result(response); + return get404Result(response, isV2); } else { PULL_LOG.info("[client-get] clientIp={}, {}, get data during dump", clientIp, groupKey); - - response.setStatus(HttpServletResponse.SC_CONFLICT); - response.getWriter().println("requested file is being modified, please try later."); - return HttpServletResponse.SC_CONFLICT + ""; + return get409Result(response, isV2); } @@ -309,12 +337,29 @@ public class ConfigServletInner { ConfigCacheService.releaseReadLock(groupKey); } - private String get404Result(HttpServletResponse response) throws IOException { + private String get404Result(HttpServletResponse response, boolean isV2) throws IOException { response.setStatus(HttpServletResponse.SC_NOT_FOUND); - response.getWriter().println("config data not exist"); + PrintWriter writer = response.getWriter(); + if (isV2) { + writer.println(JacksonUtils.toJson(Result.failure(ErrorCode.RESOURCE_NOT_FOUND, "config data not exist"))); + } else { + writer.println("config data not exist"); + } return HttpServletResponse.SC_NOT_FOUND + ""; } + private String get409Result(HttpServletResponse response, boolean isV2) throws IOException { + response.setStatus(HttpServletResponse.SC_CONFLICT); + PrintWriter writer = response.getWriter(); + if (isV2) { + writer.println(JacksonUtils.toJson(Result + .failure(ErrorCode.RESOURCE_CONFLICT, "requested file is being modified, please try later."))); + } else { + writer.println("requested file is being modified, please try later."); + } + return HttpServletResponse.SC_CONFLICT + ""; + } + /** * Try to add read lock. * diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/HistoryController.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/HistoryController.java index 3681b4172..d47b2c4da 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/controller/HistoryController.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/HistoryController.java @@ -18,18 +18,16 @@ package com.alibaba.nacos.config.server.controller; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.NamespaceUtil; -import com.alibaba.nacos.common.utils.Pair; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.Page; -import com.alibaba.nacos.config.server.service.repository.PersistService; +import com.alibaba.nacos.config.server.service.HistoryService; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import com.alibaba.nacos.plugin.auth.exception.AccessException; -import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -37,7 +35,6 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; -import java.util.Objects; /** * History management controller. @@ -48,10 +45,10 @@ import java.util.Objects; @RequestMapping(Constants.HISTORY_CONTROLLER_PATH) public class HistoryController { - private final PersistService persistService; + private final HistoryService historyService; - public HistoryController(PersistService persistService) { - this.persistService = persistService; + public HistoryController(HistoryService historyService) { + this.historyService = historyService; } /** @@ -79,7 +76,7 @@ public class HistoryController { pageSize = null == pageSize ? 100 : pageSize; pageSize = Math.min(500, pageSize); // configInfoBase has no appName field. - return persistService.findConfigHistory(dataId, group, tenant, pageNo, pageSize); + return historyService.listConfigHistory(dataId, group, tenant, pageNo, pageSize); } /** @@ -98,38 +95,7 @@ public class HistoryController { @RequestParam("group") String group, @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant, @RequestParam("nid") Long nid) throws AccessException { - ConfigHistoryInfo configHistoryInfo = persistService.detailConfigHistory(nid); - if (Objects.isNull(configHistoryInfo)) { - return null; - } - // check if history config match the input - checkHistoryInfoPermission(configHistoryInfo, dataId, group, tenant); - - String encryptedDataKey = configHistoryInfo.getEncryptedDataKey(); - Pair pair = EncryptionHandler.decryptHandler(dataId, encryptedDataKey, - configHistoryInfo.getContent()); - configHistoryInfo.setContent(pair.getSecond()); - - return configHistoryInfo; - } - - /** - * Check if the input dataId,group and tenant match the history config. - * - * @param configHistoryInfo history config. - * @param dataId dataId - * @param group group - * @param tenant tenant - * @throws AccessException not match exception. - * @since 2.0.3 - */ - private void checkHistoryInfoPermission(ConfigHistoryInfo configHistoryInfo, String dataId, String group, - String tenant) throws AccessException { - if (!Objects.equals(configHistoryInfo.getDataId(), dataId) - || !Objects.equals(configHistoryInfo.getGroup(), group) - || !Objects.equals(configHistoryInfo.getTenant(), tenant)) { - throw new AccessException("Please check dataId, group or tenant."); - } + return historyService.getConfigHistoryInfo(dataId, group, tenant, nid); } /** @@ -149,13 +115,7 @@ public class HistoryController { @RequestParam("group") String group, @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant, @RequestParam("id") Long id) throws AccessException { - ConfigHistoryInfo configHistoryInfo = persistService.detailPreviousConfigHistory(id); - if (Objects.isNull(configHistoryInfo)) { - return null; - } - // check if history config match the input - checkHistoryInfoPermission(configHistoryInfo, dataId, group, tenant); - return configHistoryInfo; + return historyService.getPreviousConfigHistoryInfo(dataId, group, tenant, id); } /** @@ -171,7 +131,7 @@ public class HistoryController { // check tenant ParamUtils.checkTenant(tenant); tenant = NamespaceUtil.processNamespaceParameter(tenant); - return persistService.queryConfigInfoByNamespace(tenant); + return historyService.getConfigListByNamespace(tenant); } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/v2/ConfigControllerV2.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/v2/ConfigControllerV2.java new file mode 100644 index 000000000..305836950 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/v2/ConfigControllerV2.java @@ -0,0 +1,151 @@ +/* + * Copyright 1999-2022 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.nacos.config.server.controller.v2; + +import com.alibaba.nacos.api.annotation.NacosApi; +import com.alibaba.nacos.api.config.ConfigType; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.common.utils.NamespaceUtil; +import com.alibaba.nacos.common.utils.Pair; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.controller.ConfigServletInner; +import com.alibaba.nacos.config.server.model.ConfigRequestInfo; +import com.alibaba.nacos.config.server.model.form.ConfigForm; +import com.alibaba.nacos.config.server.service.ConfigOperationService; +import com.alibaba.nacos.config.server.utils.ParamUtils; +import com.alibaba.nacos.config.server.utils.RequestUtil; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import com.alibaba.nacos.plugin.auth.constant.SignType; +import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Special controller v2 for soft load client to publish data. + * + * @author dongyafei + * @date 2022/7/22 + */ + +@NacosApi +@RestController +@RequestMapping(Constants.CONFIG_CONTROLLER_V2_PATH) +public class ConfigControllerV2 { + + private final ConfigServletInner inner; + + private final ConfigOperationService configOperationService; + + public ConfigControllerV2(ConfigServletInner inner, ConfigOperationService configOperationService) { + this.inner = inner; + this.configOperationService = configOperationService; + } + + /** + * Get configure board information fail. + * + * @throws ServletException ServletException. + * @throws IOException IOException. + * @throws NacosApiException NacosApiException. + */ + @GetMapping + @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) + public void getConfig(HttpServletRequest request, HttpServletResponse response, + @RequestParam("dataId") String dataId, @RequestParam("group") String group, + @RequestParam(value = "namespaceId", required = false, defaultValue = StringUtils.EMPTY) String namespaceId, + @RequestParam(value = "tag", required = false) String tag) + throws NacosException, IOException, ServletException { + // check namespaceId + ParamUtils.checkTenantV2(namespaceId); + namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); + // check params + ParamUtils.checkParam(dataId, group, "datumId", "content"); + ParamUtils.checkParamV2(tag); + final String clientIp = RequestUtil.getRemoteIp(request); + String isNotify = request.getHeader("notify"); + inner.doGetConfig(request, response, dataId, group, namespaceId, tag, isNotify, clientIp, true); + } + + /** + * Adds or updates non-aggregated data. + * + * @throws NacosException NacosException. + */ + @PostMapping() + @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG) + public Result publishConfig(ConfigForm configForm, HttpServletRequest request) throws NacosException { + // check required field + configForm.validate(); + // encrypted + Pair pair = EncryptionHandler.encryptHandler(configForm.getDataId(), configForm.getContent()); + configForm.setContent(pair.getSecond()); + // check param + ParamUtils.checkTenantV2(configForm.getNamespaceId()); + ParamUtils.checkParam(configForm.getDataId(), configForm.getGroup(), "datumId", configForm.getContent()); + ParamUtils.checkParamV2(configForm.getTag()); + + if (StringUtils.isBlank(configForm.getSrcUser())) { + configForm.setSrcUser(RequestUtil.getSrcUserName(request)); + } + if (!ConfigType.isValidType(configForm.getType())) { + configForm.setType(ConfigType.getDefaultType().getType()); + } + + ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); + configRequestInfo.setSrcIp(RequestUtil.getRemoteIp(request)); + configRequestInfo.setRequestIpApp(RequestUtil.getAppName(request)); + configRequestInfo.setBetaIps(request.getHeader("betaIps")); + + String encryptedDataKey = pair.getFirst(); + + return Result.success(configOperationService.publishConfig(configForm, configRequestInfo, encryptedDataKey)); + } + + /** + * Synchronously delete all pre-aggregation data under a dataId. + * + * @throws NacosApiException NacosApiException. + */ + @DeleteMapping + @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG) + public Result deleteConfig(HttpServletRequest request, @RequestParam("dataId") String dataId, + @RequestParam("group") String group, + @RequestParam(value = "namespaceId", required = false, defaultValue = StringUtils.EMPTY) String namespaceId, + @RequestParam(value = "tag", required = false) String tag) throws NacosException { + // check namespaceId + ParamUtils.checkTenantV2(namespaceId); + ParamUtils.checkParam(dataId, group, "datumId", "rm"); + ParamUtils.checkParamV2(tag); + + String clientIp = RequestUtil.getRemoteIp(request); + String srcUser = RequestUtil.getSrcUserName(request); + return Result.success(configOperationService.deleteConfig(dataId, group, namespaceId, tag, clientIp, srcUser)); + } +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/v2/HistoryControllerV2.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/v2/HistoryControllerV2.java new file mode 100644 index 000000000..c3cf101f8 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/v2/HistoryControllerV2.java @@ -0,0 +1,154 @@ +/* + * Copyright 1999-2022 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.nacos.config.server.controller.v2; + +import com.alibaba.nacos.api.annotation.NacosApi; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.common.utils.NamespaceUtil; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; +import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; +import com.alibaba.nacos.config.server.model.Page; +import com.alibaba.nacos.config.server.service.HistoryService; +import com.alibaba.nacos.config.server.utils.ParamUtils; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import com.alibaba.nacos.plugin.auth.constant.SignType; +import com.alibaba.nacos.plugin.auth.exception.AccessException; +import org.springframework.dao.DataAccessException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * config history management controller [v2]. + * @author dongyafei + * @date 2022/7/25 + */ + +@NacosApi +@RestController +@RequestMapping(Constants.HISTORY_CONTROLLER_V2_PATH) +public class HistoryControllerV2 { + + private final HistoryService historyService; + + public HistoryControllerV2(HistoryService historyService) { + this.historyService = historyService; + } + + /** + * Query the list history config. notes: + * + * @param dataId dataId string value [required]. + * @param group group string value [required]. + * @param namespaceId namespaceId. + * @param pageNo pageNo integer value. + * @param pageSize pageSize integer value. + * @return the page of history config. + * @since 2.0.3 add {@link Secured} for history config permission check. + */ + @GetMapping("/list") + @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) + public Result> listConfigHistory( + @RequestParam("dataId") String dataId, + @RequestParam("group") String group, + @RequestParam(value = "namespaceId", required = false, defaultValue = StringUtils.EMPTY) String namespaceId, + @RequestParam(value = "pageNo", required = false, defaultValue = "1") Integer pageNo, + @RequestParam(value = "pageSize", required = false, defaultValue = "100") Integer pageSize) { + pageSize = Math.min(500, pageSize); + return Result.success(historyService.listConfigHistory(dataId, group, namespaceId, pageNo, pageSize)); + } + + /** + * Query the detailed configuration history information. notes: + * + * @param nid history_config_info nid + * @param dataId dataId @since 2.0.3 + * @param group groupId @since 2.0.3 + * @param namespaceId namespaceId @since 2.0.3 + * @return history config info + * @since 2.0.3 add {@link Secured}, dataId, groupId and tenant for history config permission check. + */ + @GetMapping + @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) + public Result getConfigHistoryInfo( + @RequestParam("dataId") String dataId, + @RequestParam("group") String group, + @RequestParam(value = "namespaceId", required = false, defaultValue = StringUtils.EMPTY) String namespaceId, + @RequestParam("nid") Long nid) throws AccessException, NacosApiException { + ConfigHistoryInfo configHistoryInfo; + try { + configHistoryInfo = historyService.getConfigHistoryInfo(dataId, group, namespaceId, nid); + } catch (DataAccessException e) { + throw new NacosApiException(HttpStatus.NOT_FOUND.value(), ErrorCode.RESOURCE_NOT_FOUND, + "certain config history for nid = " + nid + " not exist"); + } + return Result.success(configHistoryInfo); + } + + /** + * Query previous config history information. notes: + * + * @param id config_info id + * @param dataId dataId @since 2.0.3 + * @param group groupId @since 2.0.3 + * @param namespaceId namespaceId @since 2.0.3 + * @return history config info + * @since 2.0.3 add {@link Secured}, dataId, groupId and tenant for history config permission check. + */ + @GetMapping(value = "/previous") + @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) + public Result getPreviousConfigHistoryInfo( + @RequestParam("dataId") String dataId, + @RequestParam("group") String group, + @RequestParam(value = "namespaceId", required = false, defaultValue = StringUtils.EMPTY) String namespaceId, + @RequestParam("id") Long id) throws AccessException, NacosApiException { + ConfigHistoryInfo configHistoryInfo; + try { + configHistoryInfo = historyService.getPreviousConfigHistoryInfo(dataId, group, namespaceId, id); + } catch (DataAccessException e) { + throw new NacosApiException(HttpStatus.NOT_FOUND.value(), ErrorCode.RESOURCE_NOT_FOUND, + "previous config history for id = " + id + " not exist"); + } + return Result.success(configHistoryInfo); + } + + /** + * Query configs list by namespace. + * + * @param namespaceId config_info namespace + * @return list + * @since 2.1.1 + */ + @GetMapping(value = "/configs") + @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) + public Result> getConfigsByTenant(@RequestParam("namespaceId") String namespaceId) + throws NacosApiException { + // check namespaceId + ParamUtils.checkTenantV2(namespaceId); + namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); + return Result.success(historyService.getConfigListByNamespace(namespaceId)); + } +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/exception/GlobalExceptionHandler.java b/config/src/main/java/com/alibaba/nacos/config/server/exception/GlobalExceptionHandler.java index f3d4b59ef..6497b5bb0 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/exception/GlobalExceptionHandler.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/exception/GlobalExceptionHandler.java @@ -19,6 +19,7 @@ package com.alibaba.nacos.config.server.exception; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.ExceptionUtil; import com.alibaba.nacos.config.server.monitor.MetricsMonitor; +import org.springframework.core.annotation.Order; import org.springframework.dao.DataAccessException; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; @@ -31,6 +32,7 @@ import java.io.IOException; * * @author Nacos */ +@Order(0) @ControllerAdvice public class GlobalExceptionHandler { diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigAdvanceInfo.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigAdvanceInfo.java index 5e65e39ba..f683a2bf1 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigAdvanceInfo.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigAdvanceInfo.java @@ -25,7 +25,7 @@ import java.io.Serializable; */ public class ConfigAdvanceInfo implements Serializable { - static final long serialVersionUID = -1L; + static final long serialVersionUID = 3148031484920416869L; private long createTime; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo.java index 32ff33b6d..b60ce32b1 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo.java @@ -24,7 +24,7 @@ package com.alibaba.nacos.config.server.model; */ public class ConfigInfo extends ConfigInfoBase { - static final long serialVersionUID = -1L; + static final long serialVersionUID = 3115358782431229202L; private String tenant; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBase.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBase.java index 17961df99..58891c87d 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBase.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBase.java @@ -32,7 +32,7 @@ import java.io.Serializable; */ public class ConfigInfoBase implements Serializable, Comparable { - static final long serialVersionUID = -1L; + static final long serialVersionUID = 265316491795790798L; @JsonSerialize(using = ToStringSerializer.class) private long id; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBaseEx.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBaseEx.java index 68d4e9429..3129df3c5 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBaseEx.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBaseEx.java @@ -24,7 +24,7 @@ package com.alibaba.nacos.config.server.model; */ public class ConfigInfoBaseEx extends ConfigInfoBase { - private static final long serialVersionUID = -1L; + private static final long serialVersionUID = 5802322506486922169L; /** * Single message status code, when querying for batch. diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoEx.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoEx.java index 49d2e2082..d88b16130 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoEx.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoEx.java @@ -23,7 +23,7 @@ package com.alibaba.nacos.config.server.model; */ public class ConfigInfoEx extends ConfigInfo { - private static final long serialVersionUID = -1L; + private static final long serialVersionUID = 8905036592920606608L; /** * Single message status code, when querying for batch. diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigRequestInfo.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigRequestInfo.java new file mode 100644 index 000000000..b2ee61b61 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigRequestInfo.java @@ -0,0 +1,93 @@ +/* + * Copyright 1999-2022 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.nacos.config.server.model; + +import java.io.Serializable; +import java.util.Objects; + +/** + * ConfigRequestInfo. + * @author dongyafei + * @date 2022/8/11 + */ +public class ConfigRequestInfo implements Serializable { + + private static final long serialVersionUID = 326726654448860273L; + + private String srcIp; + + private String requestIpApp; + + private String betaIps; + + public ConfigRequestInfo(String srcIp, String requestIpApp, String betaIps) { + this.srcIp = srcIp; + this.requestIpApp = requestIpApp; + this.betaIps = betaIps; + } + + public ConfigRequestInfo() { + } + + public String getSrcIp() { + return srcIp; + } + + public void setSrcIp(String srcIp) { + this.srcIp = srcIp; + } + + public String getRequestIpApp() { + return requestIpApp; + } + + public void setRequestIpApp(String requestIpApp) { + this.requestIpApp = requestIpApp; + } + + public String getBetaIps() { + return betaIps; + } + + public void setBetaIps(String betaIps) { + this.betaIps = betaIps; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ConfigRequestInfo that = (ConfigRequestInfo) o; + return Objects.equals(srcIp, that.srcIp) && Objects.equals(requestIpApp, that.requestIpApp) && Objects + .equals(betaIps, that.betaIps); + } + + @Override + public int hashCode() { + return Objects.hash(srcIp, requestIpApp, betaIps); + } + + @Override + public String toString() { + return "ConfigRequestInfoVo{" + "srcIp='" + srcIp + '\'' + ", requestIpApp='" + requestIpApp + '\'' + + ", betaIps='" + betaIps + '\'' + '}'; + } +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/GroupInfo.java b/config/src/main/java/com/alibaba/nacos/config/server/model/GroupInfo.java index a71f6cb12..594fd27dd 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/GroupInfo.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/GroupInfo.java @@ -27,9 +27,9 @@ import java.io.Serializable; * @author Nacos */ public class GroupInfo implements Serializable { - - static final long serialVersionUID = -1L; - + + static final long serialVersionUID = 3930805434971004186L; + @JsonSerialize(using = ToStringSerializer.class) private long id; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/Page.java b/config/src/main/java/com/alibaba/nacos/config/server/model/Page.java index e1f68c78f..a291b23c9 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/Page.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/Page.java @@ -27,9 +27,9 @@ import java.util.List; * @date 2010-5-6 */ public class Page implements Serializable { - - static final long serialVersionUID = -1L; - + + static final long serialVersionUID = 1234544030560484292L; + /** * totalCount. */ diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/form/ConfigForm.java b/config/src/main/java/com/alibaba/nacos/config/server/model/form/ConfigForm.java new file mode 100644 index 000000000..938d2a833 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/form/ConfigForm.java @@ -0,0 +1,236 @@ +/* + * Copyright 1999-2022 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.nacos.config.server.model.form; + +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.common.utils.StringUtils; +import org.springframework.http.HttpStatus; + +import java.io.Serializable; +import java.util.Objects; + +/** + * ConfigForm. + * + * @author dongyafei + * @date 2022/7/24 + */ +public class ConfigForm implements Serializable { + + private static final long serialVersionUID = 4124932564086863921L; + + private String dataId; + + private String group; + + private String namespaceId; + + private String content; + + private String tag; + + private String appName; + + private String srcUser; + + private String configTags; + + private String desc; + + private String use; + + private String effect; + + private String type; + + private String schema; + + public ConfigForm() { + } + + public ConfigForm(String dataId, String group, String namespaceId, String content, String tag, String appName, + String srcUser, String configTags, String desc, String use, String effect, String type, String schema) { + this.dataId = dataId; + this.group = group; + this.namespaceId = namespaceId; + this.content = content; + this.tag = tag; + this.appName = appName; + this.srcUser = srcUser; + this.configTags = configTags; + this.desc = desc; + this.use = use; + this.effect = effect; + this.type = type; + this.schema = schema; + } + + public String getDataId() { + return dataId; + } + + public void setDataId(String dataId) { + this.dataId = dataId; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public String getNamespaceId() { + return namespaceId; + } + + public void setNamespaceId(String namespaceId) { + this.namespaceId = namespaceId; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getSrcUser() { + return srcUser; + } + + public void setSrcUser(String srcUser) { + this.srcUser = srcUser; + } + + public String getConfigTags() { + return configTags; + } + + public void setConfigTags(String configTags) { + this.configTags = configTags; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getUse() { + return use; + } + + public void setUse(String use) { + this.use = use; + } + + public String getEffect() { + return effect; + } + + public void setEffect(String effect) { + this.effect = effect; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ConfigForm configForm = (ConfigForm) o; + return dataId.equals(configForm.dataId) && group.equals(configForm.group) && Objects.equals(namespaceId, configForm.namespaceId) + && content.equals(configForm.content) && Objects.equals(tag, configForm.tag) && Objects + .equals(appName, configForm.appName) && Objects.equals(srcUser, configForm.srcUser) && Objects + .equals(configTags, configForm.configTags) && Objects.equals(desc, configForm.desc) && Objects + .equals(use, configForm.use) && Objects.equals(effect, configForm.effect) && Objects + .equals(type, configForm.type) && Objects.equals(schema, configForm.schema); + } + + @Override + public int hashCode() { + return Objects.hash(dataId, group, namespaceId, content, tag, appName, srcUser, configTags, desc, use, effect, type, + schema); + } + + @Override + public String toString() { + return "ConfigVo{" + "dataId='" + dataId + '\'' + ", group='" + group + '\'' + ", namespaceId='" + namespaceId + '\'' + + ", content='" + content + '\'' + ", tag='" + tag + '\'' + ", appName='" + appName + '\'' + + ", srcUser='" + srcUser + '\'' + ", configTags='" + configTags + '\'' + ", desc='" + desc + '\'' + + ", use='" + use + '\'' + ", effect='" + effect + '\'' + ", type='" + type + '\'' + ", schema='" + + schema + '\'' + '}'; + } + + /** + * Validate. + * + * @throws NacosApiException NacosApiException. + */ + public void validate() throws NacosApiException { + if (StringUtils.isBlank(dataId)) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'dataId' type String is not present"); + } else if (StringUtils.isBlank(group)) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'group' type String is not present"); + } else if (StringUtils.isBlank(content)) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'content' type String is not present"); + } + } +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/monitor/ThreadTaskQueueMonitorTask.java b/config/src/main/java/com/alibaba/nacos/config/server/monitor/ThreadTaskQueueMonitorTask.java index 72a924977..ae1eac57d 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/monitor/ThreadTaskQueueMonitorTask.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/monitor/ThreadTaskQueueMonitorTask.java @@ -37,7 +37,7 @@ public class ThreadTaskQueueMonitorTask implements Runnable { @Override public void run() { int size = ConfigExecutor.asyncNotifyQueueSize(); - int notifierClientSize = ConfigExecutor.asyncCofigChangeClientNotifyQueueSize(); + int notifierClientSize = ConfigExecutor.asyncConfigChangeClientNotifyQueueSize(); MEMORY_LOG.info("toNotifyTaskSize = {}", size); MEMORY_LOG.info("toClientNotifyTaskSize = {}", notifierClientSize); MetricsMonitor.getNotifyTaskMonitor().set(size); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcConfigChangeNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcConfigChangeNotifier.java index d8033c39e..c8a9860a8 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcConfigChangeNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcConfigChangeNotifier.java @@ -203,11 +203,11 @@ public class RpcConfigChangeNotifier extends Subscriber { retryTask.connectionId); connectionManager.unregister(retryTask.connectionId); } else if (connectionManager.getConnection(retryTask.connectionId) != null) { - // first time :delay 0s; sencond time:delay 2s ;third time :delay 4s + // first time:delay 0s; second time:delay 2s; third time:delay 4s ConfigExecutor.getClientConfigNotifierServiceExecutor() .schedule(retryTask, retryTask.tryTimes * 2, TimeUnit.SECONDS); } else { - // client is already offline,ingnore task. + // client is already offline, ignore task. } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ClientTrackService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ClientTrackService.java index 04448abee..a28f87f4f 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ClientTrackService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ClientTrackService.java @@ -95,12 +95,9 @@ public class ClientTrackService { */ public static Map listSubStatus(String ip) { Map status = new HashMap<>(100); - + + // record here is non-null ClientRecord record = getClientRecord(ip); - if (record == null) { - return status; - } - for (Map.Entry entry : record.getGroupKey2md5Map().entrySet()) { String groupKey = entry.getKey(); String clientMd5 = entry.getValue(); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigOperationService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigOperationService.java new file mode 100644 index 000000000..246757cc0 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigOperationService.java @@ -0,0 +1,144 @@ +/* + * Copyright 1999-2022 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.nacos.config.server.service; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.common.utils.MapUtil; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.config.server.model.ConfigInfo; +import com.alibaba.nacos.config.server.model.event.ConfigDataChangeEvent; +import com.alibaba.nacos.config.server.model.ConfigRequestInfo; +import com.alibaba.nacos.config.server.model.form.ConfigForm; +import com.alibaba.nacos.config.server.service.repository.PersistService; +import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; +import com.alibaba.nacos.config.server.utils.ParamUtils; +import com.alibaba.nacos.config.server.utils.TimeUtils; +import com.alibaba.nacos.sys.utils.InetUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Map; + +/** + * ConfigService. + * + * @author dongyafei + * @date 2022/8/11 + */ + +@Service +public class ConfigOperationService { + + private PersistService persistService; + + private static final Logger LOGGER = LoggerFactory.getLogger(ConfigOperationService.class); + + public ConfigOperationService(PersistService persistService) { + this.persistService = persistService; + } + + /** + * Adds or updates non-aggregated data. + * + * @throws NacosException NacosException. + */ + public Boolean publishConfig(ConfigForm configForm, ConfigRequestInfo configRequestInfo, String encryptedDataKey) + throws NacosException { + + Map configAdvanceInfo = getConfigAdvanceInfo(configForm); + ParamUtils.checkParam(configAdvanceInfo); + + if (AggrWhitelist.isAggrDataId(configForm.getDataId())) { + LOGGER.warn("[aggr-conflict] {} attempt to publish single data, {}, {}", configRequestInfo.getSrcIp(), + configForm.getDataId(), configForm.getGroup()); + throw new NacosApiException(HttpStatus.FORBIDDEN.value(), ErrorCode.INVALID_DATA_ID, + "dataId:" + configForm.getDataId() + " is aggr"); + } + + final Timestamp time = TimeUtils.getCurrentTime(); + ConfigInfo configInfo = new ConfigInfo(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), + configForm.getAppName(), configForm.getContent()); + + configInfo.setType(configForm.getType()); + configInfo.setEncryptedDataKey(encryptedDataKey); + + if (StringUtils.isBlank(configRequestInfo.getBetaIps())) { + if (StringUtils.isBlank(configForm.getTag())) { + persistService.insertOrUpdate(configRequestInfo.getSrcIp(), configForm.getSrcUser(), configInfo, time, + configAdvanceInfo, false); + ConfigChangePublisher.notifyConfigChange( + new ConfigDataChangeEvent(false, configForm.getDataId(), configForm.getGroup(), + configForm.getNamespaceId(), time.getTime())); + } else { + persistService.insertOrUpdateTag(configInfo, configForm.getTag(), configRequestInfo.getSrcIp(), + configForm.getSrcUser(), time, false); + ConfigChangePublisher.notifyConfigChange( + new ConfigDataChangeEvent(false, configForm.getDataId(), configForm.getGroup(), + configForm.getNamespaceId(), configForm.getTag(), time.getTime())); + } + } else { + // beta publish + persistService + .insertOrUpdateBeta(configInfo, configRequestInfo.getBetaIps(), configRequestInfo.getSrcIp(), + configForm.getSrcUser(), time, false); + ConfigChangePublisher.notifyConfigChange( + new ConfigDataChangeEvent(true, configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), + time.getTime())); + } + ConfigTraceService.logPersistenceEvent(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), + configRequestInfo.getRequestIpApp(), time.getTime(), InetUtils.getSelfIP(), + ConfigTraceService.PERSISTENCE_EVENT_PUB, configForm.getContent()); + + return true; + } + + /** + * Synchronously delete all pre-aggregation data under a dataId. + */ + public Boolean deleteConfig(String dataId, String group, String namespaceId, String tag, String clientIp, + String srcUser) { + if (StringUtils.isBlank(tag)) { + persistService.removeConfigInfo(dataId, group, namespaceId, clientIp, srcUser); + } else { + persistService.removeConfigInfoTag(dataId, group, namespaceId, tag, clientIp, srcUser); + } + final Timestamp time = TimeUtils.getCurrentTime(); + ConfigTraceService.logPersistenceEvent(dataId, group, namespaceId, null, time.getTime(), clientIp, + ConfigTraceService.PERSISTENCE_EVENT_REMOVE, null); + ConfigChangePublisher + .notifyConfigChange(new ConfigDataChangeEvent(false, dataId, group, namespaceId, tag, time.getTime())); + + return true; + } + + public Map getConfigAdvanceInfo(ConfigForm configForm) { + Map configAdvanceInfo = new HashMap<>(10); + MapUtil.putIfValNoNull(configAdvanceInfo, "config_tags", configForm.getConfigTags()); + MapUtil.putIfValNoNull(configAdvanceInfo, "desc", configForm.getDesc()); + MapUtil.putIfValNoNull(configAdvanceInfo, "use", configForm.getUse()); + MapUtil.putIfValNoNull(configAdvanceInfo, "effect", configForm.getEffect()); + MapUtil.putIfValNoNull(configAdvanceInfo, "type", configForm.getType()); + MapUtil.putIfValNoNull(configAdvanceInfo, "schema", configForm.getSchema()); + return configAdvanceInfo; + } +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/HistoryService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/HistoryService.java new file mode 100644 index 000000000..c2de73ea0 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/HistoryService.java @@ -0,0 +1,106 @@ +/* + * Copyright 1999-2022 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.nacos.config.server.service; + +import com.alibaba.nacos.common.utils.Pair; +import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; +import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; +import com.alibaba.nacos.config.server.model.Page; +import com.alibaba.nacos.config.server.service.repository.PersistService; +import com.alibaba.nacos.plugin.auth.exception.AccessException; +import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Objects; + +/** + * HistoryService. + * + * @author dongyafei + * @date 2022/8/11 + */ +@Service +public class HistoryService { + + private final PersistService persistService; + + public HistoryService(PersistService persistService) { + this.persistService = persistService; + } + + /** + * Query the list history config. + */ + public Page listConfigHistory(String dataId, String group, String namespaceId, Integer pageNo, + Integer pageSize) { + return persistService.findConfigHistory(dataId, group, namespaceId, pageNo, pageSize); + } + + /** + * Query the detailed configuration history information. + */ + public ConfigHistoryInfo getConfigHistoryInfo(String dataId, String group, String namespaceId, Long nid) + throws AccessException { + ConfigHistoryInfo configHistoryInfo = persistService.detailConfigHistory(nid); + if (Objects.isNull(configHistoryInfo)) { + return null; + } + // check if history config match the input + checkHistoryInfoPermission(configHistoryInfo, dataId, group, namespaceId); + + String encryptedDataKey = configHistoryInfo.getEncryptedDataKey(); + Pair pair = EncryptionHandler + .decryptHandler(dataId, encryptedDataKey, configHistoryInfo.getContent()); + configHistoryInfo.setContent(pair.getSecond()); + + return configHistoryInfo; + } + + /** + * Query previous config history information. + */ + public ConfigHistoryInfo getPreviousConfigHistoryInfo(String dataId, String group, String namespaceId, Long id) + throws AccessException { + ConfigHistoryInfo configHistoryInfo = persistService.detailPreviousConfigHistory(id); + if (Objects.isNull(configHistoryInfo)) { + return null; + } + // check if history config match the input + checkHistoryInfoPermission(configHistoryInfo, dataId, group, namespaceId); + return configHistoryInfo; + } + + /** + * Query configs list by namespace. + */ + public List getConfigListByNamespace(String namespaceId) { + return persistService.queryConfigInfoByNamespace(namespaceId); + } + + /** + * Check if the input dataId,group and namespaceId match the history config. + */ + private void checkHistoryInfoPermission(ConfigHistoryInfo configHistoryInfo, String dataId, String group, + String namespaceId) throws AccessException { + if (!Objects.equals(configHistoryInfo.getDataId(), dataId) || !Objects + .equals(configHistoryInfo.getGroup(), group) || !Objects + .equals(configHistoryInfo.getTenant(), namespaceId)) { + throw new AccessException("Please check dataId, group or namespaceId."); + } + } +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java index 359244687..b0708be2e 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java @@ -185,7 +185,7 @@ public class LongPollingService { } } } - + return mergeSampleResult(sampleResultLst); } @@ -240,8 +240,8 @@ public class LongPollingService { String str = req.getHeader(LongPollingService.LONG_POLLING_HEADER); String noHangUpFlag = req.getHeader(LongPollingService.LONG_POLLING_NO_HANG_UP_HEADER); - String appName = req.getHeader(RequestUtil.CLIENT_APPNAME_HEADER); - String tag = req.getHeader("Vipserver-Tag"); + final String appName = req.getHeader(RequestUtil.CLIENT_APPNAME_HEADER); + final String tag = req.getHeader("Vipserver-Tag"); int delayTime = SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME, 500); // Add delay time for LoadBalance, and one response is returned 500 ms in advance to avoid client timeout. @@ -395,10 +395,10 @@ public class LongPollingService { asyncTimeoutFuture = ConfigExecutor.scheduleLongPolling(() -> { try { getRetainIps().put(ClientLongPolling.this.ip, System.currentTimeMillis()); - + // Delete subscriber's relations. boolean removeFlag = allSubs.remove(ClientLongPolling.this); - + if (removeFlag) { if (isFixedPolling()) { LogUtil.CLIENT_LOG @@ -426,7 +426,7 @@ public class LongPollingService { } catch (Throwable t) { LogUtil.DEFAULT_LOG.error("long polling error:" + t.getMessage(), t.getCause()); } - + }, timeoutTime, TimeUnit.MILLISECONDS); allSubs.add(this); @@ -442,13 +442,13 @@ public class LongPollingService { } void generateResponse(List changedGroups) { + if (null == changedGroups) { - // Tell web container to send http response. asyncContext.complete(); return; } - + HttpServletResponse response = (HttpServletResponse) asyncContext.getResponse(); try { diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/CapacityService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/CapacityService.java index 003f06d26..7b433e972 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/CapacityService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/CapacityService.java @@ -269,10 +269,9 @@ public class CapacityService { private boolean initGroupCapacity(String group, Integer quota, Integer maxSize, Integer maxAggrCount, Integer maxAggrSize) { boolean insertSuccess = insertGroupCapacity(group, quota, maxSize, maxAggrCount, maxAggrSize); - if (quota != null) { - return insertSuccess; + if (quota == null) { + autoExpansion(group, null); } - autoExpansion(group, null); return insertSuccess; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/DataSourcePoolProperties.java b/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/DataSourcePoolProperties.java index 0a6ca12a7..119ffdb68 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/DataSourcePoolProperties.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/DataSourcePoolProperties.java @@ -33,9 +33,11 @@ import java.util.concurrent.TimeUnit; */ public class DataSourcePoolProperties { - public static final long DEFAULT_CONNECTION_TIMEOUT = TimeUnit.SECONDS.toMillis(30L); + public static final long DEFAULT_CONNECTION_TIMEOUT = TimeUnit.SECONDS.toMillis(3L); public static final long DEFAULT_VALIDATION_TIMEOUT = TimeUnit.SECONDS.toMillis(10L); + + public static final long DEFAULT_IDLE_TIMEOUT = TimeUnit.MINUTES.toMillis(10L); public static final int DEFAULT_MAX_POOL_SIZE = 20; @@ -45,6 +47,7 @@ public class DataSourcePoolProperties { private DataSourcePoolProperties() { dataSource = new HikariDataSource(); + dataSource.setIdleTimeout(DEFAULT_IDLE_TIMEOUT); dataSource.setConnectionTimeout(DEFAULT_CONNECTION_TIMEOUT); dataSource.setValidationTimeout(DEFAULT_VALIDATION_TIMEOUT); dataSource.setMaximumPoolSize(DEFAULT_MAX_POOL_SIZE); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/ExternalDataSourceProperties.java b/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/ExternalDataSourceProperties.java index 7ea6055e7..3f6b84529 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/ExternalDataSourceProperties.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/ExternalDataSourceProperties.java @@ -23,7 +23,6 @@ import org.springframework.core.env.Environment; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.common.utils.CollectionUtils.getOrDefault; @@ -85,8 +84,6 @@ public class ExternalDataSourceProperties { poolProperties.setPassword(getOrDefault(password, index, password.get(0)).trim()); HikariDataSource ds = poolProperties.getDataSource(); ds.setConnectionTestQuery(TEST_QUERY); - ds.setIdleTimeout(TimeUnit.MINUTES.toMillis(10L)); - ds.setConnectionTimeout(TimeUnit.SECONDS.toMillis(3L)); dataSources.add(ds); callback.accept(ds); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/ExternalDataSourceServiceImpl.java b/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/ExternalDataSourceServiceImpl.java index 8ac97a99f..ad31e6e3b 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/ExternalDataSourceServiceImpl.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/ExternalDataSourceServiceImpl.java @@ -119,16 +119,37 @@ public class ExternalDataSourceServiceImpl implements DataSourceService { @Override public synchronized void reload() throws IOException { try { - dataSourceList = new ExternalDataSourceProperties() + final List testJtListNew = new ArrayList(); + final List isHealthListNew = new ArrayList(); + + List dataSourceListNew = new ExternalDataSourceProperties() .build(EnvUtil.getEnvironment(), (dataSource) -> { JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setQueryTimeout(queryTimeout); jdbcTemplate.setDataSource(dataSource); - testJtList.add(jdbcTemplate); - isHealthList.add(Boolean.TRUE); + testJtListNew.add(jdbcTemplate); + isHealthListNew.add(Boolean.TRUE); }); + + final List dataSourceListOld = dataSourceList; + final List testJtListOld = testJtList; + dataSourceList = dataSourceListNew; + testJtList = testJtListNew; + isHealthList = isHealthListNew; new SelectMasterTask().run(); new CheckDbHealthTask().run(); + + //close old datasource. + if (dataSourceListOld != null && !dataSourceListOld.isEmpty()) { + for (HikariDataSource dataSource : dataSourceListOld) { + dataSource.close(); + } + } + if (testJtListOld != null && !testJtListOld.isEmpty()) { + for (JdbcTemplate oldJdbc : testJtListOld) { + oldJdbc.setDataSource(null); + } + } } catch (RuntimeException e) { FATAL_LOG.error(DB_LOAD_ERROR_MSG, e); throw new IOException(e); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/LocalDataSourceServiceImpl.java b/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/LocalDataSourceServiceImpl.java index 04a8630c7..925efec7c 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/LocalDataSourceServiceImpl.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/datasource/LocalDataSourceServiceImpl.java @@ -57,7 +57,7 @@ public class LocalDataSourceServiceImpl implements DataSourceService { private final String password = "nacos"; - private final String derbyBaseDir = "data" + File.separator + "derby-data"; + private final String derbyBaseDir = "data" + File.separator + Constants.DERBY_BASE_DIR; private final String derbyShutdownErrMsg = "Derby system shutdown."; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/DerbySnapshotOperation.java b/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/DerbySnapshotOperation.java index 4575b6c7e..00231b951 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/DerbySnapshotOperation.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/DerbySnapshotOperation.java @@ -17,6 +17,7 @@ package com.alibaba.nacos.config.server.service.repository.embedded; import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.event.DerbyLoadEvent; import com.alibaba.nacos.config.server.service.datasource.DataSourceService; import com.alibaba.nacos.config.server.service.datasource.DynamicDataSource; @@ -61,7 +62,7 @@ public class DerbySnapshotOperation implements SnapshotOperation { private final String snapshotArchive = "derby_data.zip"; - private final String derbyBaseDir = Paths.get(EnvUtil.getNacosHome(), "data", "derby-data").toString(); + private final String derbyBaseDir = Paths.get(EnvUtil.getNacosHome(), "data", Constants.DERBY_BASE_DIR).toString(); private final String restoreDB = "jdbc:derby:" + derbyBaseDir; @@ -127,7 +128,7 @@ public class DerbySnapshotOperation implements SnapshotOperation { } } - final String loadPath = Paths.get(readerPath, snapshotDir, "derby-data").toString(); + final String loadPath = Paths.get(readerPath, snapshotDir, Constants.DERBY_BASE_DIR).toString(); LogUtil.FATAL_LOG.info("snapshot load from : {}, and copy to : {}", loadPath, derbyBaseDir); doDerbyRestoreFromBackup(() -> { diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/DistributedDatabaseOperateImpl.java b/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/DistributedDatabaseOperateImpl.java index 374c28a7b..b62dc4a62 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/DistributedDatabaseOperateImpl.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/DistributedDatabaseOperateImpl.java @@ -94,7 +94,7 @@ import java.util.stream.Collectors; * ā”‚ ā”‚ ā”‚ ā”‚ * ā”‚ ā”‚ ā”‚ ā–¼ * ā”‚ ā”‚ ā”‚ ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” - * ā”‚ ā”‚ ā”‚ ā”‚ acquireSnakeflowerId ā”‚ + * ā”‚ ā”‚ ā”‚ ā”‚ acquireSnowFlowerId ā”‚ * ā”‚ ā”‚ ā”‚ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ * ā”‚ ā”‚ ā”‚ ā”‚ * ā”‚ ā”‚ ā”‚ ā”‚ diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedPaginationHelperImpl.java b/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedPaginationHelperImpl.java index 452001c34..c1f06a2a4 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedPaginationHelperImpl.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedPaginationHelperImpl.java @@ -85,10 +85,7 @@ class EmbeddedPaginationHelperImpl implements PaginationHelper { return page; } - final int startRow = (pageNo - 1) * pageSize; - String selectSql = sqlFetchRows + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; - - List result = databaseOperate.queryMany(selectSql, args, rowMapper); + List result = databaseOperate.queryMany(sqlFetchRows, args, rowMapper); for (E item : result) { page.getPageItems().add(item); } @@ -123,8 +120,7 @@ class EmbeddedPaginationHelperImpl implements PaginationHelper { return page; } - String selectSql = sqlFetchRows.replaceAll("(?i)LIMIT \\?,\\?", "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"); - List result = databaseOperate.queryMany(selectSql, args, rowMapper); + List result = databaseOperate.queryMany(sqlFetchRows, args, rowMapper); for (E item : result) { page.getPageItems().add(item); } @@ -159,9 +155,7 @@ class EmbeddedPaginationHelperImpl implements PaginationHelper { return page; } - String selectSql = sqlFetchRows.replaceAll("(?i)LIMIT \\?,\\?", "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"); - - List result = databaseOperate.queryMany(selectSql, args2, rowMapper); + List result = databaseOperate.queryMany(sqlFetchRows, args2, rowMapper); for (E item : result) { page.getPageItems().add(item); } @@ -177,9 +171,7 @@ class EmbeddedPaginationHelperImpl implements PaginationHelper { // Create Page object final Page page = new Page<>(); - String selectSql = sqlFetchRows.replaceAll("(?i)LIMIT \\?,\\?", "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"); - - List result = databaseOperate.queryMany(selectSql, args, rowMapper); + List result = databaseOperate.queryMany(sqlFetchRows, args, rowMapper); for (E item : result) { page.getPageItems().add(item); } @@ -188,9 +180,7 @@ class EmbeddedPaginationHelperImpl implements PaginationHelper { @Override public void updateLimit(final String sql, final Object[] args) { - String sqlUpdate = sql.replaceAll("LIMIT \\?", "OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY"); - - EmbeddedStorageContextUtils.addSqlContext(sqlUpdate, args); + EmbeddedStorageContextUtils.addSqlContext(sql, args); try { databaseOperate.update(EmbeddedStorageContextUtils.getCurrentSqlContext()); } finally { diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedStoragePersistServiceImpl.java b/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedStoragePersistServiceImpl.java index 7ce0361d9..3507af108 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedStoragePersistServiceImpl.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedStoragePersistServiceImpl.java @@ -21,6 +21,7 @@ import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.Pair; +import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.configuration.ConditionOnEmbeddedStorage; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.enums.FileTypeEnum; @@ -49,9 +50,17 @@ import com.alibaba.nacos.config.server.service.sql.EmbeddedStorageContextUtils; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.core.distributed.id.IdGeneratorManager; +import com.alibaba.nacos.plugin.datasource.MapperManager; +import com.alibaba.nacos.plugin.datasource.constants.TableConstant; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoAggrMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoBetaMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoTagMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigTagsRelationMapper; +import com.alibaba.nacos.plugin.datasource.mapper.HistoryConfigInfoMapper; import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler; +import com.alibaba.nacos.sys.env.EnvUtil; import org.apache.commons.collections.CollectionUtils; -import com.alibaba.nacos.common.utils.StringUtils; import org.springframework.context.annotation.Conditional; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; @@ -61,6 +70,7 @@ import javax.annotation.PostConstruct; import java.io.IOException; import java.sql.Timestamp; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -121,6 +131,24 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { private final IdGeneratorManager idGeneratorManager; + private MapperManager mapperManager; + + private final String dataSource; + + private static final String DATASOURCE_PLATFORM_PROPERTY = "spring.datasource.platform"; + + private static final String DEFAULT_DATASOURCE_PLATFORM = "derby"; + + private static final String DATA_ID = "dataId"; + + private static final String GROUP = "group"; + + private static final String APP_NAME = "appName"; + + private static final String CONTENT = "content"; + + private static final String TENANT = "tenant_id"; + /** * The constructor sets the dependency injection order. * @@ -132,6 +160,8 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { this.idGeneratorManager = idGeneratorManager; NotifyCenter.registerToSharePublisher(DerbyImportEvent.class); + mapperManager = MapperManager.instance(); + dataSource = EnvUtil.getProperty(DATASOURCE_PLATFORM_PROPERTY, DEFAULT_DATASOURCE_PLATFORM); } /** @@ -140,9 +170,10 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @PostConstruct public void init() { dataSourceService = DynamicDataSource.getInstance().getDataSource(); - idGeneratorManager.register(RESOURCE_CONFIG_INFO_ID, RESOURCE_CONFIG_HISTORY_ID, - RESOURCE_CONFIG_TAG_RELATION_ID, RESOURCE_APP_CONFIGDATA_RELATION_SUBS, RESOURCE_CONFIG_BETA_ID, - RESOURCE_NAMESPACE_ID, RESOURCE_USER_ID, RESOURCE_ROLE_ID, RESOURCE_PERMISSIONS_ID); + idGeneratorManager + .register(RESOURCE_CONFIG_INFO_ID, RESOURCE_CONFIG_HISTORY_ID, RESOURCE_CONFIG_TAG_RELATION_ID, + RESOURCE_APP_CONFIGDATA_RELATION_SUBS, RESOURCE_CONFIG_BETA_ID, RESOURCE_NAMESPACE_ID, + RESOURCE_USER_ID, RESOURCE_ROLE_ID, RESOURCE_PERMISSIONS_ID); } public boolean checkMasterWritable() { @@ -226,10 +257,11 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { configInfo.setTenant(tenantTmp); try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); - - final String sql = - "INSERT INTO config_info_beta(data_id,group_id,tenant_id,app_name,content,md5,beta_ips,src_ip," - + "src_user,gmt_create,gmt_modified,encrypted_data_key) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)"; + ConfigInfoBetaMapper configInfoBetaMapper = (ConfigInfoBetaMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_BETA).get(); + final String sql = configInfoBetaMapper.insert(Arrays + .asList("data_id", "group_id", "tenant_id", "app_name", "content", "md5", "beta_ips", "src_ip", + "src_user", "gmt_create", "gmt_modified", "encrypted_data_key")); final Object[] args = new Object[] {configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(), md5, betaIps, srcIp, srcUser, time, time, encryptedDataKey}; @@ -253,10 +285,11 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); - - final String sql = - "INSERT INTO config_info_tag(data_id,group_id,tenant_id,tag_id,app_name,content,md5,src_ip,src_user," - + "gmt_create,gmt_modified) VALUES(?,?,?,?,?,?,?,?,?,?,?)"; + ConfigInfoTagMapper configInfoTagMapper = (ConfigInfoTagMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_TAG).get(); + final String sql = configInfoTagMapper.insert(Arrays + .asList("data_id", "group_id", "tenant_id", "tag_id", "app_name", "content", "md5", "src_ip", + "src_user", "gmt_create", "gmt_modified")); final Object[] args = new Object[] {configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp, appNameTmp, configInfo.getContent(), md5, srcIp, srcUser, time, time}; @@ -356,8 +389,11 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { configInfo.setTenant(tenantTmp); try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); - - final String sql = "UPDATE config_info_beta SET content=?,md5=?,beta_ips=?,src_ip=?,src_user=?,gmt_modified=?,app_name=?,encrypted_data_key=? WHERE data_id=? AND group_id=? AND tenant_id=?"; + ConfigInfoBetaMapper configInfoBetaMapper = (ConfigInfoBetaMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_BETA).get(); + final String sql = configInfoBetaMapper.update(Arrays + .asList("content", "md5", "beta_ips", "src_ip", "src_user", "gmt_modified", "app_name", + "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id")); final Object[] args = new Object[] {configInfo.getContent(), md5, betaIps, srcIp, srcUser, time, appNameTmp, encryptedDataKey, configInfo.getDataId(), configInfo.getGroup(), tenantTmp}; @@ -381,7 +417,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); - final String sql = "UPDATE config_info_beta SET content=?,md5=?,beta_ips=?,src_ip=?,src_user=?,gmt_modified=?,app_name=? WHERE data_id=? AND group_id=? AND tenant_id=? AND (md5=? OR md5 IS NULL OR md5='')"; + ConfigInfoBetaMapper configInfoBetaMapper = (ConfigInfoBetaMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_BETA).get(); + final String sql = configInfoBetaMapper.updateConfigInfo4BetaCas(); final Object[] args = new Object[] {configInfo.getContent(), md5, betaIps, srcIp, srcUser, time, appNameTmp, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, configInfo.getMd5()}; @@ -407,7 +445,11 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); - final String sql = "UPDATE config_info_tag SET content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?,app_name=? WHERE data_id=? AND group_id=? AND tenant_id=? AND tag_id=?"; + ConfigInfoTagMapper configInfoTagMapper = (ConfigInfoTagMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_TAG).get(); + final String sql = configInfoTagMapper + .update(Arrays.asList("content", "md5", "src_ip", "src_user", "gmt_modified", "app_name"), + Arrays.asList("data_id", "group_id", "tenant_id", "tag_id")); final Object[] args = new Object[] {configInfo.getContent(), md5, srcIp, srcUser, time, appNameTmp, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp}; @@ -431,8 +473,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); - - final String sql = "UPDATE config_info_tag SET content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?,app_name=? WHERE data_id=? AND group_id=? AND tenant_id=? AND tag_id=? AND (md5=? OR md5 IS NULL OR md5='')"; + ConfigInfoTagMapper configInfoTagMapper = (ConfigInfoTagMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_TAG).get(); + final String sql = configInfoTagMapper.updateConfigInfo4TagCas(); final Object[] args = new Object[] {configInfo.getContent(), md5, srcIp, srcUser, time, appNameTmp, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp, configInfo.getMd5()}; @@ -492,8 +535,10 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { public void updateMd5(String dataId, String group, String tenant, String md5, Timestamp lastTime) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { - - final String sql = "UPDATE config_info SET md5 = ? WHERE data_id=? AND group_id=? AND tenant_id=? AND gmt_modified=?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final String sql = configInfoMapper + .update(Arrays.asList("md5"), Arrays.asList("data_id", "group_id", "tenant_id", "gmt_modified")); final Object[] args = new Object[] {md5, dataId, group, tenantTmp, lastTime}; EmbeddedStorageContextUtils.addSqlContext(sql, args); @@ -551,8 +596,8 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { private boolean isAlreadyExist(SubInfo subInfo) { final String sql = "SELECT * FROM app_configdata_relation_subs WHERE dara_id=? AND group_id=? AND app_name=?"; - Map obj = databaseOperate.queryOne(sql, - new Object[] {subInfo.getDataId(), subInfo.getGroup(), subInfo.getAppName()}, Map.class); + Map obj = databaseOperate + .queryOne(sql, new Object[] {subInfo.getDataId(), subInfo.getGroup(), subInfo.getAppName()}, Map.class); return obj != null; } @@ -617,11 +662,13 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { ConfigInfo configInfo = findConfigInfo4Beta(dataId, group, tenant); if (configInfo != null) { try { - final String sql = "DELETE FROM config_info_beta WHERE data_id=? AND group_id=? AND tenant_id=?"; + ConfigInfoBetaMapper configInfoBetaMapper = (ConfigInfoBetaMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_BETA).get(); + final String sql = configInfoBetaMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id")); final Object[] args = new Object[] {dataId, group, tenantTmp}; - EmbeddedStorageContextUtils.onDeleteConfigBetaInfo(tenantTmp, group, dataId, - System.currentTimeMillis()); + EmbeddedStorageContextUtils + .onDeleteConfigBetaInfo(tenantTmp, group, dataId, System.currentTimeMillis()); EmbeddedStorageContextUtils.addSqlContext(sql, args); boolean result = databaseOperate.update(EmbeddedStorageContextUtils.getCurrentSqlContext()); @@ -643,12 +690,17 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { String contentTmp = StringUtils.isBlank(content) ? StringUtils.EMPTY : content; final Timestamp now = new Timestamp(System.currentTimeMillis()); - final String select = "SELECT content FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND datum_id = ?"; - final String insert = "INSERT INTO config_info_aggr(data_id, group_id, tenant_id, datum_id, app_name, content, gmt_modified) VALUES(?,?,?,?,?,?,?) "; - final String update = "UPDATE config_info_aggr SET content = ? , gmt_modified = ? WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND datum_id = ?"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + final String select = configInfoAggrMapper.select(Collections.singletonList("content"), + Arrays.asList("data_id", "group_id", "tenant_id", "datum_id")); + final String insert = configInfoAggrMapper.insert(Arrays + .asList("data_id", "group_id", "tenant_id", "datum_id", "app_name", "content", "gmt_modified")); + final String update = configInfoAggrMapper.update(Arrays.asList("content", "gmt_modified"), + Arrays.asList("data_id", "group_id", "tenant_id", "datum_id")); - String dbContent = databaseOperate.queryOne(select, new Object[] {dataId, group, tenantTmp, datumId}, - String.class); + String dbContent = databaseOperate + .queryOne(select, new Object[] {dataId, group, tenantTmp, datumId}, String.class); if (Objects.isNull(dbContent)) { final Object[] args = new Object[] {dataId, group, tenantTmp, datumId, appNameTmp, contentTmp, now}; @@ -674,7 +726,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { final String datumId) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - final String sql = "DELETE FROM config_info_aggr WHERE data_id=? AND group_id=? AND tenant_id=? AND datum_id=?"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + final String sql = configInfoAggrMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id", "datum_id")); final Object[] args = new Object[] {dataId, group, tenantTmp, datumId}; EmbeddedStorageContextUtils.addSqlContext(sql, args); @@ -692,7 +746,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { public void removeAggrConfigInfo(final String dataId, final String group, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - final String sql = "DELETE FROM config_info_aggr WHERE data_id=? AND group_id=? AND tenant_id=?"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + final String sql = configInfoAggrMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id")); final Object[] args = new Object[] {dataId, group, tenantTmp}; EmbeddedStorageContextUtils.addSqlContext(sql, args); @@ -715,9 +771,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { datumString.append('\'').append(datum).append("',"); } datumString.deleteCharAt(datumString.length() - 1); - final String sql = - "DELETE FROM config_info_aggr WHERE data_id=? AND group_id=? AND tenant_id=? AND datum_id IN (" - + datumString.toString() + ")"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + final String sql = configInfoAggrMapper.batchRemoveAggr(datumList); final Object[] args = new Object[] {dataId, group, tenantTmp}; EmbeddedStorageContextUtils.addSqlContext(sql, args); @@ -734,15 +790,18 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public void removeConfigHistory(final Timestamp startTime, final int limitSize) { - String sql = "DELETE FROM his_config_info WHERE id IN( " - + "SELECT id FROM his_config_info WHERE gmt_modified < ? OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY)"; + HistoryConfigInfoMapper historyConfigInfoMapper = (HistoryConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.HIS_CONFIG_INFO).get(); + String sql = historyConfigInfoMapper.removeConfigHistory(); PaginationHelper helper = createPaginationHelper(); helper.updateLimit(sql, new Object[] {startTime, limitSize}); } @Override public int findConfigHistoryCountByTime(final Timestamp startTime) { - String sql = "SELECT count(*) FROM his_config_info WHERE gmt_modified < ?"; + HistoryConfigInfoMapper historyConfigInfoMapper = (HistoryConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.HIS_CONFIG_INFO).get(); + String sql = historyConfigInfoMapper.findConfigHistoryCountByTime(); Integer result = databaseOperate.queryOne(sql, new Object[] {startTime}, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoBetaCount error"); @@ -752,7 +811,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public long findConfigMaxId() { - String sql = "SELECT max(id) FROM config_info"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.findConfigMaxId(); return Optional.ofNullable(databaseOperate.queryOne(sql, Long.class)).orElse(0L); } @@ -785,8 +846,10 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { removeAggrConfigInfo(dataId, group, tenant); String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - final String sql = "INSERT INTO config_info_aggr(data_id, group_id, tenant_id, datum_id, app_name, " - + "content, gmt_modified) VALUES(?,?,?,?,?,?,?) "; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + final String sql = configInfoAggrMapper.insert(Arrays + .asList("data_id", "group_id", "tenant_id", "datum_id", "app_name", "content", "gmt_modified")); for (Entry datumEntry : datumMap.entrySet()) { final Object[] args = new Object[] {dataId, group, tenantTmp, datumEntry.getKey(), appNameTmp, datumEntry.getValue(), new Timestamp(System.currentTimeMillis())}; @@ -807,17 +870,23 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public List findAllDataIdAndGroup() { - String sql = "SELECT DISTINCT data_id, group_id FROM config_info"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.findAllDataIdAndGroup(); return databaseOperate.queryMany(sql, EMPTY_ARRAY, CONFIG_INFO_ROW_MAPPER); } @Override public ConfigInfoBetaWrapper findConfigInfo4Beta(final String dataId, final String group, final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - final String sql = "SELECT id,data_id,group_id,tenant_id,app_name,content,beta_ips,encrypted_data_key FROM config_info_beta WHERE data_id=? AND group_id=? AND tenant_id=?"; + ConfigInfoBetaMapper configInfoBetaMapper = (ConfigInfoBetaMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_BETA).get(); + final String sql = configInfoBetaMapper.select(Arrays + .asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "beta_ips", + "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id")); - return databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp}, - CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER); + return databaseOperate + .queryOne(sql, new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER); } @@ -826,18 +895,25 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { final String tag) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); + ConfigInfoTagMapper configInfoTagMapper = (ConfigInfoTagMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_TAG).get(); + final String sql = configInfoTagMapper + .select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "tag_id", "app_name", "content"), + Arrays.asList("data_id", "group_id", "tenant_id", "tag_id")); - final String sql = "SELECT id,data_id,group_id,tenant_id,tag_id,app_name,content FROM config_info_tag WHERE data_id=? AND group_id=? AND tenant_id=? AND tag_id=?"; - - return databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp, tagTmp}, - CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER); + return databaseOperate + .queryOne(sql, new Object[] {dataId, group, tenantTmp, tagTmp}, CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER); } @Override public ConfigInfo findConfigInfoApp(final String dataId, final String group, final String tenant, final String appName) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - final String sql = "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=? AND app_name=?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final String sql = configInfoMapper + .select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content"), + Arrays.asList("data_id", "group_id", "tenant_id", "app_name")); return databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp, appName}, CONFIG_INFO_ROW_MAPPER); @@ -854,32 +930,22 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { paramList.add(group); paramList.add(tenantTmp); - StringBuilder sql = new StringBuilder( - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=? "); + String sql = null; + Map params = new HashMap<>(16); + if (StringUtils.isNotBlank(appName)) { + paramList.add(appName); + params.put(APP_NAME, APP_NAME); + } if (StringUtils.isNotBlank(configTags)) { - sql = new StringBuilder( - "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " - + "config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.group_id=? AND a.tenant_id=? "); - sql.append(" AND b.tag_name IN ("); String[] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sql.append(", "); - } - sql.append('?'); - paramList.add(tagArr[i]); - } - sql.append(") "); - - if (StringUtils.isNotBlank(appName)) { - sql.append(" AND a.app_name=? "); - paramList.add(appName); - } + paramList.addAll(Arrays.asList(tagArr)); + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + sql = configTagsRelationMapper.findConfigInfoAdvanceInfo(params, paramList.size()); } else { - if (StringUtils.isNotBlank(appName)) { - sql.append(" AND app_name=? "); - paramList.add(appName); - } + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + sql = configInfoMapper.findConfigInfoAdvanceInfo(params); } return databaseOperate.queryOne(sql.toString(), paramList.toArray(), CONFIG_INFO_ROW_MAPPER); @@ -887,24 +953,33 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public ConfigInfoBase findConfigInfoBase(final String dataId, final String group) { - final String sql = "SELECT id,data_id,group_id,content FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final String sql = configInfoMapper.select(Arrays.asList("id", "data_id", "group_id", "content"), + Arrays.asList("data_id", "group_id", "tenant_id")); - return databaseOperate.queryOne(sql, new Object[] {dataId, group, StringUtils.EMPTY}, - CONFIG_INFO_BASE_ROW_MAPPER); + return databaseOperate + .queryOne(sql, new Object[] {dataId, group, StringUtils.EMPTY}, CONFIG_INFO_BASE_ROW_MAPPER); } @Override public ConfigInfo findConfigInfo(long id) { - final String sql = "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE id=?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final String sql = configInfoMapper + .select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content"), + Collections.singletonList("id")); return databaseOperate.queryOne(sql, new Object[] {id}, CONFIG_INFO_ROW_MAPPER); } @Override public ConfigInfoWrapper findConfigInfo(final String dataId, final String group, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - final String sql = - "SELECT id,data_id,group_id,tenant_id,app_name,content,md5,type,encrypted_data_key FROM config_info " - + " WHERE data_id=? AND group_id=? AND tenant_id=?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final String sql = configInfoMapper.select(Arrays + .asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "type", + "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id")); final Object[] args = new Object[] {dataId, group, tenantTmp}; return databaseOperate.queryOne(sql, args, CONFIG_INFO_WRAPPER_ROW_MAPPER); @@ -915,9 +990,12 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; PaginationHelper helper = createPaginationHelper(); - return helper.fetchPage("SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=?", - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND " - + "tenant_id=?", new Object[] {dataId, tenantTmp}, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return helper.fetchPage(configInfoMapper.findConfigInfoByDataIdCountRows(), + configInfoMapper.findConfigInfoByDataIdFetchRows(startRow, pageSize), new Object[] {dataId, tenantTmp}, + pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } @Override @@ -925,10 +1003,12 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { final String tenant, final String appName) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; PaginationHelper helper = createPaginationHelper(); - return helper.fetchPage("SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=? AND app_name=?", - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND " - + "tenant_id=? AND app_name=?", new Object[] {dataId, tenantTmp, appName}, pageNo, pageSize, - CONFIG_INFO_ROW_MAPPER); + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return helper.fetchPage(configInfoMapper.findConfigInfoByDataIdAndAppCountRows(), + configInfoMapper.findConfigInfoByDataIdAndAppFetchRows(startRow, pageSize), + new Object[] {dataId, tenantTmp, appName}, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } @Override @@ -937,46 +1017,30 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - StringBuilder sqlCount = new StringBuilder("SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=? "); - StringBuilder sql = new StringBuilder( - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND tenant_id=? "); + String sql = null; + String sqlCount = null; + Map paramsMap = new HashMap<>(16); List paramList = new ArrayList<>(); paramList.add(dataId); paramList.add(tenantTmp); + if (StringUtils.isNotBlank(appName)) { + paramList.add(appName); + paramsMap.put(APP_NAME, APP_NAME); + } + final int startRow = (pageNo - 1) * pageSize; if (StringUtils.isNotBlank(configTags)) { - sqlCount = new StringBuilder( - "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.tenant_id=? "); - - sql = new StringBuilder( - "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " - + "config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.tenant_id=? "); - - sqlCount.append(" AND b.tag_name IN ("); - sql.append(" AND b.tag_name IN ("); String[] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sqlCount.append(", "); - sql.append(", "); - } - sqlCount.append('?'); - sql.append('?'); - paramList.add(tagArr[i]); - } - sqlCount.append(") "); - sql.append(") "); - - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" AND a.app_name=? "); - sql.append(" AND a.app_name=? "); - paramList.add(appName); - } + paramList.addAll(Arrays.asList(tagArr)); + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + sqlCount = configTagsRelationMapper.findConfigInfoByDataIdAndAdvanceCountRows(paramsMap, paramList.size()); + sql = configTagsRelationMapper + .findConfigInfoByDataIdAndAdvanceFetchRows(paramsMap, paramList.size(), startRow, pageSize); } else { - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" AND app_name=? "); - sql.append(" AND app_name=? "); - paramList.add(appName); - } + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + sqlCount = configInfoMapper.findConfigInfoByDataIdAndAdvanceCountRows(paramsMap); + sql = configInfoMapper.findConfigInfoByDataIdAndAdvanceFetchRows(paramsMap, startRow, pageSize); } PaginationHelper helper = createPaginationHelper(); @@ -990,63 +1054,45 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - String sqlCount = "SELECT count(*) FROM config_info"; - String sql = "SELECT id,data_id,group_id,tenant_id,app_name,content,type FROM config_info"; - StringBuilder where = new StringBuilder(" WHERE "); + String sql = null; + String sqlCount = null; List paramList = new ArrayList<>(); paramList.add(tenantTmp); + Map paramsMap = new HashMap<>(16); + if (StringUtils.isNotBlank(dataId)) { + paramList.add(dataId); + paramsMap.put(DATA_ID, DATA_ID); + } + if (StringUtils.isNotBlank(group)) { + paramList.add(group); + paramsMap.put(GROUP, GROUP); + } + if (StringUtils.isNotBlank(appName)) { + paramList.add(appName); + paramsMap.put(APP_NAME, APP_NAME); + } + final int startRow = (pageNo - 1) * pageSize; if (StringUtils.isNotBlank(configTags)) { - sqlCount = "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id"; - sql = "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " - + "config_tags_relation b ON a.id=b.id"; - - where.append(" a.tenant_id=? "); - - if (StringUtils.isNotBlank(dataId)) { - where.append(" AND a.data_id=? "); - paramList.add(dataId); - } - if (StringUtils.isNotBlank(group)) { - where.append(" AND a.group_id=? "); - paramList.add(group); - } - if (StringUtils.isNotBlank(appName)) { - where.append(" AND a.app_name=? "); - paramList.add(appName); - } - - where.append(" AND b.tag_name IN ("); String[] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - where.append(", "); - } - where.append('?'); - paramList.add(tagArr[i]); - } - where.append(") "); + paramList.addAll(Arrays.asList(tagArr)); + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + sqlCount = configTagsRelationMapper.findConfigInfo4PageCountRows(paramsMap, paramList.size()); + sql = configTagsRelationMapper + .findConfigInfo4PageFetchRows(paramsMap, paramList.size(), startRow, pageSize); } else { - where.append(" tenant_id=? "); - if (StringUtils.isNotBlank(dataId)) { - where.append(" AND data_id=? "); - paramList.add(dataId); - } - if (StringUtils.isNotBlank(group)) { - where.append(" AND group_id=? "); - paramList.add(group); - } - if (StringUtils.isNotBlank(appName)) { - where.append(" AND app_name=? "); - paramList.add(appName); - } + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + sqlCount = configInfoMapper.findConfigInfo4PageCountRows(paramsMap); + sql = configInfoMapper.findConfigInfo4PageFetchRows(paramsMap, startRow, pageSize); } PaginationHelper helper = createPaginationHelper(); - Page page = helper.fetchPage(sqlCount + where, sql + where, paramList.toArray(), pageNo, pageSize, - CONFIG_INFO_ROW_MAPPER); + Page page = helper + .fetchPage(sqlCount, sql, paramList.toArray(), pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); for (ConfigInfo configInfo : page.getPageItems()) { - Pair pair = EncryptionHandler.decryptHandler(configInfo.getDataId(), - configInfo.getEncryptedDataKey(), configInfo.getContent()); + Pair pair = EncryptionHandler + .decryptHandler(configInfo.getDataId(), configInfo.getEncryptedDataKey(), configInfo.getContent()); configInfo.setContent(pair.getSecond()); } @@ -1056,8 +1102,11 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public Page findConfigInfoBaseByDataId(final int pageNo, final int pageSize, final String dataId) { PaginationHelper helper = createPaginationHelper(); - return helper.fetchPage("SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=?", - "SELECT id,data_id,group_id,content FROM config_info WHERE data_id=? AND tenant_id=?", + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final int startRow = (pageNo - 1) * pageSize; + return helper.fetchPage(configInfoMapper.findConfigInfoBaseByDataIdCountRows(), + configInfoMapper.findConfigInfoBaseByDataIdFetchRows(startRow, pageSize), new Object[] {dataId, StringUtils.EMPTY}, pageNo, pageSize, CONFIG_INFO_BASE_ROW_MAPPER); } @@ -1067,9 +1116,12 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; PaginationHelper helper = createPaginationHelper(); - return helper.fetchPage("SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=?", - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE group_id=? AND " - + "tenant_id=?", new Object[] {group, tenantTmp}, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return helper.fetchPage(configInfoMapper.findConfigInfoByGroupCountRows(), + configInfoMapper.findConfigInfoByGroupFetchRows(startRow, pageSize), new Object[] {group, tenantTmp}, + pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } @@ -1078,10 +1130,12 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { final String tenant, final String appName) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; PaginationHelper helper = createPaginationHelper(); - return helper.fetchPage("SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=? AND app_name =?", - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE group_id=? AND " - + "tenant_id=? AND app_name =?", new Object[] {group, tenantTmp, appName}, pageNo, pageSize, - CONFIG_INFO_ROW_MAPPER); + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return helper.fetchPage(configInfoMapper.findConfigInfoByGroupAndAppCountRows(), + configInfoMapper.findConfigInfoByGroupAndAppFetchRows(startRow, pageSize), + new Object[] {group, tenantTmp, appName}, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } @@ -1092,50 +1146,34 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - StringBuilder sqlCount = new StringBuilder( - "SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=? "); - StringBuilder sql = new StringBuilder( - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE group_id=? AND tenant_id=? "); + String sqlCount = null; + String sql = null; + Map params = new HashMap<>(16); + List paramList = new ArrayList<>(); paramList.add(group); paramList.add(tenantTmp); + if (StringUtils.isNotBlank(appName)) { + paramList.add(appName); + params.put(APP_NAME, APP_NAME); + } + final int startRow = (pageNo - 1) * pageSize; if (StringUtils.isNotBlank(configTags)) { - sqlCount = new StringBuilder( - "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.group_id=? AND a.tenant_id=? "); - sql = new StringBuilder( - "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " - + "config_tags_relation b ON a.id=b.id WHERE a.group_id=? AND a.tenant_id=? "); - - sqlCount.append(" AND b.tag_name IN ("); - sql.append(" AND b.tag_name IN ("); String[] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sqlCount.append(", "); - sql.append(", "); - } - sqlCount.append('?'); - sql.append('?'); - paramList.add(tagArr[i]); - } - sqlCount.append(") "); - sql.append(") "); - - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" AND a.app_name=? "); - sql.append(" AND a.app_name=? "); - paramList.add(appName); - } + paramList.addAll(Arrays.asList(tagArr)); + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + sqlCount = configTagsRelationMapper.findConfigInfoByGroupAndAdvanceCountRows(params, paramList.size()); + sql = configTagsRelationMapper + .findConfigInfoByGroupAndAdvanceFetchRows(params, paramList.size(), startRow, pageSize); } else { - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" AND app_name=? "); - sql.append(" AND app_name=? "); - paramList.add(appName); - } + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + sqlCount = configInfoMapper.findConfigInfoByGroupAndAdvanceCountRows(params); + sql = configInfoMapper.findConfigInfoByGroupAndAdvanceFetchRows(params, startRow, pageSize); } PaginationHelper helper = createPaginationHelper(); - return helper.fetchPage(sqlCount.toString(), sql.toString(), paramList.toArray(), pageNo, pageSize, - CONFIG_INFO_ROW_MAPPER); + return helper.fetchPage(sqlCount, sql, paramList.toArray(), pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } @@ -1144,10 +1182,12 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { final String appName) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; PaginationHelper helper = createPaginationHelper(); - return helper.fetchPage("SELECT count(*) FROM config_info WHERE tenant_id LIKE ? AND app_name=?", - "SELECT ID,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE tenant_id LIKE ? AND " - + "app_name=?", new Object[] {generateLikeArgument(tenantTmp), appName}, pageNo, pageSize, - CONFIG_INFO_ROW_MAPPER); + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return helper.fetchPage(configInfoMapper.findConfigInfoByAppCountRows(), + configInfoMapper.findConfigInfoByAppFetchRows(startRow, pageSize), + new Object[] {generateLikeArgument(tenantTmp), appName}, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } @@ -1157,45 +1197,30 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - StringBuilder sqlCount = new StringBuilder("SELECT count(*) FROM config_info WHERE tenant_id LIKE ? "); - StringBuilder sql = new StringBuilder( - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info where tenant_id LIKE ? "); + String sqlCount = null; + String sql = null; + Map paramsMap = new HashMap<>(16); + List paramList = new ArrayList<>(); paramList.add(tenantTmp); + if (StringUtils.isNotBlank(appName)) { + paramList.add(appName); + paramsMap.put(APP_NAME, APP_NAME); + } + final int startRow = (pageNo - 1) * pageSize; if (StringUtils.isNotBlank(configTags)) { - sqlCount = new StringBuilder( - "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.tenant_id=? "); - - sql = new StringBuilder( - "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " - + "config_tags_relation b ON a.id=b.id WHERE a.tenant_id=? "); - - sqlCount.append(" AND b.tag_name IN ("); - sql.append(" AND b.tag_name IN ("); String[] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sqlCount.append(", "); - sql.append(", "); - } - sqlCount.append('?'); - sql.append('?'); - paramList.add(tagArr[i]); - } - sqlCount.append(") "); - sql.append(") "); - - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" AND a.app_name=? "); - sql.append(" AND a.app_name=? "); - paramList.add(appName); - } + paramList.addAll(Arrays.asList(tagArr)); + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + sqlCount = configTagsRelationMapper.findConfigInfoByAdvanceCountRows(paramsMap, paramList.size()); + sql = configTagsRelationMapper + .findConfigInfoByAdvanceFetchRows(paramsMap, paramList.size(), startRow, pageSize); } else { - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" AND app_name=? "); - sql.append(" AND app_name=? "); - paramList.add(appName); - } + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + sqlCount = configInfoMapper.findConfigInfoByAdvanceCountRows(paramsMap); + sql = configInfoMapper.findConfigInfoByAdvanceFetchRows(paramsMap, startRow, pageSize); } PaginationHelper helper = createPaginationHelper(); return helper.fetchPage(sqlCount.toString(), sql.toString(), paramList.toArray(), pageNo, pageSize, @@ -1206,14 +1231,19 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public Page findConfigInfoBaseByGroup(final int pageNo, final int pageSize, final String group) { PaginationHelper helper = createPaginationHelper(); - return helper.fetchPage("SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=?", - "SELECT id,data_id,group_id,content FROM config_info WHERE group_id=? AND tenant_id=?", + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return helper.fetchPage(configInfoMapper.findConfigInfoBaseByGroupCountRows(), + configInfoMapper.findConfigInfoBaseByGroupFetchRows(startRow, pageSize), new Object[] {group, StringUtils.EMPTY}, pageNo, pageSize, CONFIG_INFO_BASE_ROW_MAPPER); } @Override public int configInfoCount() { - String sql = " SELECT count(*) FROM config_info "; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.count(); Integer result = databaseOperate.queryOne(sql, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoCount error"); @@ -1223,7 +1253,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public int configInfoCount(String tenant) { - String sql = " SELECT count(*) FROM config_info WHERE tenant_id LIKE ?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.configInfoLikeTenantCount(); Integer result = databaseOperate.queryOne(sql, new Object[] {tenant}, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoCount error"); @@ -1233,7 +1265,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public int configInfoBetaCount() { - String sql = " SELECT count(*) FROM config_info_beta "; + ConfigInfoBetaMapper configInfoBetaMapper = (ConfigInfoBetaMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_BETA).get(); + String sql = configInfoBetaMapper.count(); Integer result = databaseOperate.queryOne(sql, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoBetaCount error"); @@ -1243,7 +1277,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public int configInfoTagCount() { - String sql = " SELECT count(*) FROM config_info_tag "; + ConfigInfoTagMapper configInfoTagMapper = (ConfigInfoTagMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_TAG).get(); + String sql = configInfoTagMapper.configInfoTagCount(); Integer result = databaseOperate.queryOne(sql, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoBetaCount error"); @@ -1255,11 +1291,14 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { public List getTenantIdList(int page, int pageSize) { PaginationHelper> helper = createPaginationHelper(); - String sql = "SELECT tenant_id FROM config_info WHERE tenant_id != '' GROUP BY tenant_id LIMIT ?,?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.getTenantIdList(); + int from = (page - 1) * pageSize; - Page> pageList = helper.fetchPageLimit(sql, new Object[] {from, pageSize}, page, pageSize, - MAP_ROW_MAPPER); + Page> pageList = helper + .fetchPageLimit(sql, new Object[] {from, pageSize}, page, pageSize, MAP_ROW_MAPPER); return pageList.getPageItems().stream().map(map -> String.valueOf(map.get("TENANT_ID"))) .collect(Collectors.toList()); } @@ -1268,11 +1307,13 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { public List getGroupIdList(int page, int pageSize) { PaginationHelper> helper = createPaginationHelper(); - String sql = "SELECT group_id FROM config_info WHERE tenant_id ='' GROUP BY group_id LIMIT ?,?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.getGroupIdList(); int from = (page - 1) * pageSize; - Page> pageList = helper.fetchPageLimit(sql, new Object[] {from, pageSize}, page, pageSize, - MAP_ROW_MAPPER); + Page> pageList = helper + .fetchPageLimit(sql, new Object[] {from, pageSize}, page, pageSize, MAP_ROW_MAPPER); return pageList.getPageItems().stream().map(map -> String.valueOf(map.get("GROUP_ID"))) .collect(Collectors.toList()); } @@ -1280,7 +1321,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public int aggrConfigInfoCount(String dataId, String group, String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = " SELECT count(*) FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ?"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String sql = configInfoAggrMapper.aggrConfigInfoCount(); Integer result = databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp}, Integer.class); if (result == null) { throw new IllegalArgumentException("aggrConfigInfoCount error"); @@ -1294,20 +1337,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { return 0; } final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - StringBuilder sql = new StringBuilder( - " SELECT count(*) FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND datum_id"); - if (isIn) { - sql.append(" IN ("); - } else { - sql.append(" NOT IN ("); - } - for (int i = 0, size = datumIds.size(); i < size; i++) { - if (i > 0) { - sql.append(", "); - } - sql.append('?'); - } - sql.append(')'); + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String sql = configInfoAggrMapper.aggrConfigInfoCount(datumIds.size(), isIn); List objectList = com.alibaba.nacos.common.utils.CollectionUtils.list(dataId, group, tenantTmp); objectList.addAll(datumIds); @@ -1332,10 +1364,11 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public Page findAllConfigInfo(final int pageNo, final int pageSize, final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sqlCountRows = "SELECT count(*) FROM config_info"; - String sqlFetchRows = " SELECT t.id,data_id,group_id,tenant_id,app_name,content,md5 " - + " FROM ( SELECT id FROM config_info WHERE tenant_id LIKE ? ORDER BY id LIMIT ?,? )" - + " g, config_info t WHERE g.id = t.id "; + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sqlCountRows = configInfoMapper.count(); + String sqlFetchRows = configInfoMapper.findAllConfigInfoFetchRows(startRow, pageSize); PaginationHelper helper = createPaginationHelper(); return helper.fetchPageLimit(sqlCountRows, sqlFetchRows, @@ -1347,9 +1380,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public Page findAllConfigKey(final int pageNo, final int pageSize, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - final String select = " SELECT data_id,group_id,app_name FROM " - + " ( SELECT id FROM config_info WHERE tenant_id LIKE ? ORDER BY id LIMIT ?, ? ) " - + "g, config_info t WHERE g.id = t.id "; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final String select = configInfoMapper.findAllConfigKey(); final int totalCount = configInfoCount(tenant); int pageCount = totalCount / pageSize; @@ -1366,10 +1399,10 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { page.setPagesAvailable(pageCount); page.setTotalCount(totalCount); - List result = databaseOperate.queryMany(select, - new Object[] {generateLikeArgument(tenantTmp), (pageNo - 1) * pageSize, pageSize}, - // new Object[0], - CONFIG_KEY_ROW_MAPPER); + List result = databaseOperate + .queryMany(select, new Object[] {generateLikeArgument(tenantTmp), (pageNo - 1) * pageSize, pageSize}, + // new Object[0], + CONFIG_KEY_ROW_MAPPER); for (ConfigKey item : result) { page.getPageItems().add(item); @@ -1379,36 +1412,40 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public Page findAllConfigInfoBase(final int pageNo, final int pageSize) { - String sqlCountRows = "SELECT count(*) FROM config_info"; - String sqlFetchRows = "SELECT t.id,data_id,group_id,content,md5 " - + " FROM ( SELECT id FROM config_info ORDER BY id LIMIT ?,? ) " - + " g, config_info t WHERE g.id = t.id "; + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sqlCountRows = configInfoMapper.count(); + String sqlFetchRows = configInfoMapper.findAllConfigInfoBaseFetchRows(startRow, pageSize); PaginationHelper helper = createPaginationHelper(); - return helper.fetchPageLimit(sqlCountRows, sqlFetchRows, new Object[] {(pageNo - 1) * pageSize, pageSize}, - pageNo, pageSize, CONFIG_INFO_BASE_ROW_MAPPER); + return helper + .fetchPageLimit(sqlCountRows, sqlFetchRows, new Object[] {(pageNo - 1) * pageSize, pageSize}, pageNo, + pageSize, CONFIG_INFO_BASE_ROW_MAPPER); } @Override public Page findAllConfigInfoForDumpAll(final int pageNo, final int pageSize) { - String sqlCountRows = "SELECT count(*) FROM config_info"; - String sqlFetchRows = " SELECT t.id,data_id,group_id,tenant_id,app_name,content,type,md5,gmt_modified " - + " FROM ( SELECT id FROM config_info ORDER BY id LIMIT ?,? )" - + " g, config_info t WHERE g.id = t.id "; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final int startRow = (pageNo - 1) * pageSize; + String sqlCountRows = configInfoMapper.count(); + String sqlFetchRows = configInfoMapper.findAllConfigInfoForDumpAllFetchRows(startRow, pageSize); PaginationHelper helper = createPaginationHelper(); - return helper.fetchPageLimit(sqlCountRows, sqlFetchRows, new Object[] {(pageNo - 1) * pageSize, pageSize}, pageNo, pageSize, - CONFIG_INFO_WRAPPER_ROW_MAPPER); + return helper + .fetchPageLimit(sqlCountRows, sqlFetchRows, new Object[] {(pageNo - 1) * pageSize, pageSize}, pageNo, + pageSize, CONFIG_INFO_WRAPPER_ROW_MAPPER); } @Override public Page findAllConfigInfoFragment(final long lastMaxId, final int pageSize) { - String select = - "SELECT id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified,type FROM config_info WHERE id > ? " - + "ORDER BY id ASC LIMIT ?,?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String select = configInfoMapper.findAllConfigInfoFragment(); PaginationHelper helper = createPaginationHelper(); return helper.fetchPageLimit(select, new Object[] {lastMaxId, 0, pageSize}, 1, pageSize, CONFIG_INFO_WRAPPER_ROW_MAPPER); @@ -1417,25 +1454,30 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public Page findAllConfigInfoBetaForDumpAll(final int pageNo, final int pageSize) { - String sqlCountRows = "SELECT count(*) FROM config_info_beta"; - String sqlFetchRows = " SELECT t.id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified,beta_ips " - + " FROM ( SELECT id FROM config_info_beta ORDER BY id LIMIT ?,? )" - + " g, config_info_beta t WHERE g.id = t.id "; + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoBetaMapper configInfoBetaMapper = (ConfigInfoBetaMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_BETA).get(); + String sqlCountRows = configInfoBetaMapper.count(); + String sqlFetchRows = configInfoBetaMapper.findAllConfigInfoBetaForDumpAllFetchRows(startRow, pageSize); PaginationHelper helper = createPaginationHelper(); - return helper.fetchPageLimit(sqlCountRows, sqlFetchRows, new Object[] {(pageNo - 1) * pageSize, pageSize}, - pageNo, pageSize, CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER); + return helper + .fetchPageLimit(sqlCountRows, sqlFetchRows, new Object[] {(pageNo - 1) * pageSize, pageSize}, pageNo, + pageSize, CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER); } @Override public Page findAllConfigInfoTagForDumpAll(final int pageNo, final int pageSize) { - String sqlCountRows = "SELECT count(*) FROM config_info_tag"; - String sqlFetchRows = " SELECT t.id,data_id,group_id,tenant_id,tag_id,app_name,content,md5,gmt_modified " - + " FROM ( SELECT id FROM config_info_tag ORDER BY id LIMIT ?,? ) " - + " g, config_info_tag t WHERE g.id = t.id "; + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoTagMapper configInfoTagMapper = (ConfigInfoTagMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_TAG).get(); + String sqlCountRows = configInfoTagMapper.count(); + String sqlFetchRows = configInfoTagMapper.findAllConfigInfoTagForDumpAllFetchRows(startRow, pageSize); + PaginationHelper helper = createPaginationHelper(); - return helper.fetchPageLimit(sqlCountRows, sqlFetchRows, new Object[] {(pageNo - 1) * pageSize, pageSize}, - pageNo, pageSize, CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER); + return helper + .fetchPageLimit(sqlCountRows, sqlFetchRows, new Object[] {(pageNo - 1) * pageSize, pageSize}, pageNo, + pageSize, CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER); } @@ -1456,27 +1498,17 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { } List result = new ArrayList<>(dataIds.size()); - String sqlStart = "SELECT data_id, group_id, tenant_id, app_name, content FROM config_info WHERE group_id = ? AND tenant_id = ? AND data_id IN ("; - String sqlEnd = ")"; - StringBuilder subQuerySql = new StringBuilder(); - + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); for (int i = 0; i < dataIds.size(); i += subQueryLimit) { // dataids - List params = new ArrayList<>( - dataIds.subList(i, Math.min(i + subQueryLimit, dataIds.size()))); - - for (int j = 0; j < params.size(); j++) { - subQuerySql.append('?'); - if (j != params.size() - 1) { - subQuerySql.append(','); - } - } + List params = new ArrayList<>(dataIds.subList(i, Math.min(i + subQueryLimit, dataIds.size()))); // group params.add(0, group); params.add(1, tenantTmp); - final String sql = sqlStart + subQuerySql.toString() + sqlEnd; + final String sql = configInfoMapper.findConfigInfoByBatch(params.size()); List r = databaseOperate.queryMany(sql, params.toArray(), CONFIG_INFO_ROW_MAPPER); @@ -1499,35 +1531,34 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { return this.findConfigInfoByApp(pageNo, pageSize, tenantTmp, appName); } } - - final String sqlCountRows = "SELECT count(*) FROM config_info WHERE "; - final String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE "; - String where = " 1=1 "; List params = new ArrayList<>(); - + Map paramsMap = new HashMap<>(16); if (!StringUtils.isBlank(dataId)) { - where += " AND data_id LIKE ? "; + paramsMap.put(DATA_ID, DATA_ID); params.add(generateLikeArgument(dataId)); } if (!StringUtils.isBlank(group)) { - where += " AND group_id LIKE ? "; + paramsMap.put(GROUP, GROUP); params.add(generateLikeArgument(group)); } - where += " AND tenant_id LIKE ? "; params.add(generateLikeArgument(tenantTmp)); if (!StringUtils.isBlank(appName)) { - where += " AND app_name = ? "; + paramsMap.put(APP_NAME, APP_NAME); params.add(appName); } if (!StringUtils.isBlank(content)) { - where += " AND content LIKE ? "; + paramsMap.put(CONTENT, CONTENT); params.add(generateLikeArgument(content)); } + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final int startRow = (pageNo - 1) * pageSize; + String sqlCountRows = configInfoMapper.findConfigInfoLikeCountRows(paramsMap); + String sqlFetchRows = configInfoMapper.findConfigInfoLikeFetchRows(paramsMap, startRow, pageSize); PaginationHelper helper = createPaginationHelper(); - return helper.fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, - CONFIG_INFO_ROW_MAPPER); + return helper.fetchPage(sqlCountRows, sqlFetchRows, params.toArray(), pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } @@ -1620,8 +1651,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { } } PaginationHelper helper = createPaginationHelper(); - return helper.fetchPage(sqlCountRows + where.toString(), sqlFetchRows + where.toString(), params.toArray(), - pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); + return helper + .fetchPage(sqlCountRows + where.toString(), sqlFetchRows + where.toString(), params.toArray(), pageNo, + pageSize, CONFIG_INFO_ROW_MAPPER); } @@ -1632,70 +1664,49 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); final String content = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("content"); final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - String sqlCountRows = "SELECT count(*) FROM config_info"; - String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,app_name,content,encrypted_data_key FROM config_info"; - StringBuilder where = new StringBuilder(" WHERE "); + String sqlCountRows = null; + String sqlFetchRows = null; + Map paramsMap = new HashMap<>(16); + List params = new ArrayList<>(); params.add(generateLikeArgument(tenantTmp)); + if (!StringUtils.isBlank(dataId)) { + params.add(generateLikeArgument(dataId)); + paramsMap.put(DATA_ID, DATA_ID); + } + if (!StringUtils.isBlank(group)) { + params.add(generateLikeArgument(group)); + paramsMap.put(GROUP, GROUP); + } + if (!StringUtils.isBlank(appName)) { + params.add(appName); + paramsMap.put(APP_NAME, APP_NAME); + } + if (!StringUtils.isBlank(content)) { + params.add(generateLikeArgument(content)); + paramsMap.put(CONTENT, CONTENT); + } + final int startRow = (pageNo - 1) * pageSize; if (StringUtils.isNotBlank(configTags)) { - sqlCountRows = "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id "; - sqlFetchRows = - "SELECT a.ID,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " - + "config_tags_relation b ON a.id=b.id "; - - where.append(" a.tenant_id LIKE ? "); - if (!StringUtils.isBlank(dataId)) { - where.append(" AND a.data_id LIKE ? "); - params.add(generateLikeArgument(dataId)); - } - if (!StringUtils.isBlank(group)) { - where.append(" AND a.group_id LIKE ? "); - params.add(generateLikeArgument(group)); - } - if (!StringUtils.isBlank(appName)) { - where.append(" AND a.app_name = ? "); - params.add(appName); - } - if (!StringUtils.isBlank(content)) { - where.append(" AND a.content LIKE ? "); - params.add(generateLikeArgument(content)); - } - - where.append(" AND b.tag_name IN ("); String[] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - where.append(", "); - } - where.append('?'); - params.add(tagArr[i]); - } - where.append(") "); + params.addAll(Arrays.asList(tagArr)); + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + sqlCountRows = configTagsRelationMapper.findConfigInfoLike4PageCountRows(paramsMap, params.size()); + sqlFetchRows = configTagsRelationMapper + .findConfigInfoLike4PageFetchRows(paramsMap, params.size(), startRow, pageSize); } else { - where.append(" tenant_id LIKE ? "); - if (!StringUtils.isBlank(dataId)) { - where.append(" AND data_id LIKE ? "); - params.add(generateLikeArgument(dataId)); - } - if (!StringUtils.isBlank(group)) { - where.append(" AND group_id LIKE ? "); - params.add(generateLikeArgument(group)); - } - if (!StringUtils.isBlank(appName)) { - where.append(" AND app_name = ? "); - params.add(appName); - } - if (!StringUtils.isBlank(content)) { - where.append(" AND content LIKE ? "); - params.add(generateLikeArgument(content)); - } + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + sqlCountRows = configInfoMapper.findConfigInfoLike4PageCountRows(paramsMap); + sqlFetchRows = configInfoMapper.findConfigInfoLike4PageFetchRows(paramsMap, startRow, pageSize); } PaginationHelper helper = createPaginationHelper(); - Page page = helper.fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, - pageSize, CONFIG_INFO_ROW_MAPPER); + Page page = helper + .fetchPage(sqlCountRows, sqlFetchRows, params.toArray(), pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); for (ConfigInfo configInfo : page.getPageItems()) { - Pair pair = EncryptionHandler.decryptHandler(configInfo.getDataId(), - configInfo.getEncryptedDataKey(), configInfo.getContent()); + Pair pair = EncryptionHandler + .decryptHandler(configInfo.getDataId(), configInfo.getEncryptedDataKey(), configInfo.getContent()); configInfo.setContent(pair.getSecond()); } return page; @@ -1709,47 +1720,52 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { throw new IOException("invalid param"); } - final String sqlCountRows = "SELECT count(*) FROM config_info WHERE "; - final String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,content FROM config_info WHERE "; - String where = " 1=1 AND tenant_id='' "; + Map paramsMap = new HashMap<>(16); List params = new ArrayList<>(); - if (!StringUtils.isBlank(dataId)) { - where += " AND data_id LIKE ? "; params.add(generateLikeArgument(dataId)); + paramsMap.put(DATA_ID, DATA_ID); } if (!StringUtils.isBlank(group)) { - where += " AND group_id LIKE ? "; params.add(generateLikeArgument(group)); + paramsMap.put(GROUP, GROUP); } if (!StringUtils.isBlank(content)) { - where += " AND content LIKE ? "; params.add(generateLikeArgument(content)); + paramsMap.put(CONTENT, CONTENT); } + + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sqlCountRows = configInfoMapper.findConfigInfoBaseLikeCountRows(paramsMap); + String sqlFetchRows = configInfoMapper.findConfigInfoBaseLikeFetchRows(paramsMap, startRow, pageSize); PaginationHelper helper = createPaginationHelper(); - return helper.fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, - CONFIG_INFO_BASE_ROW_MAPPER); + return helper + .fetchPage(sqlCountRows, sqlFetchRows, params.toArray(), pageNo, pageSize, CONFIG_INFO_BASE_ROW_MAPPER); } @Override public ConfigInfoAggr findSingleConfigInfoAggr(String dataId, String group, String tenant, String datumId) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = - "SELECT id,data_id,group_id,tenant_id,datum_id,app_name,content FROM config_info_aggr WHERE data_id=? " - + "AND group_id=? AND tenant_id=? AND datum_id=?"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String sql = configInfoAggrMapper + .select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "datum_id", "app_name", "content"), + Arrays.asList("data_id", "group_id", "tenant_id", "datum_id")); - return databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp, datumId}, - CONFIG_INFO_AGGR_ROW_MAPPER); + return databaseOperate + .queryOne(sql, new Object[] {dataId, group, tenantTmp, datumId}, CONFIG_INFO_AGGR_ROW_MAPPER); } @Override public List findConfigInfoAggr(String dataId, String group, String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = - "SELECT data_id,group_id,tenant_id,datum_id,app_name,content FROM config_info_aggr WHERE data_id=? AND " - + "group_id=? AND tenant_id=? ORDER BY datum_id"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String sql = configInfoAggrMapper.findConfigInfoAggr(); return databaseOperate.queryMany(sql, new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_AGGR_ROW_MAPPER); @@ -1759,10 +1775,13 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { public Page findConfigInfoAggrByPage(String dataId, String group, String tenant, final int pageNo, final int pageSize) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sqlCountRows = "SELECT count(*) FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ?"; - String sqlFetchRows = - "SELECT data_id,group_id,tenant_id,datum_id,app_name,content FROM config_info_aggr WHERE data_id=? AND " - + "group_id=? AND tenant_id=? ORDER BY datum_id LIMIT ?,?"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + final int startRow = (pageNo - 1) * pageSize; + String sqlCountRows = configInfoAggrMapper + .select(Arrays.asList("count(*)"), Arrays.asList("data_id", "group_id", "tenant_id")); + String sqlFetchRows = configInfoAggrMapper.findConfigInfoAggrByPageFetchRows(startRow, pageSize); + PaginationHelper helper = createPaginationHelper(); return helper.fetchPageLimit(sqlCountRows, new Object[] {dataId, group, tenantTmp}, sqlFetchRows, new Object[] {dataId, group, tenantTmp, (pageNo - 1) * pageSize, pageSize}, pageNo, pageSize, @@ -1859,14 +1878,17 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { } } PaginationHelper helper = createPaginationHelper(); - return helper.fetchPage(sqlCountRows + where.toString(), sqlFetchRows + where.toString(), params.toArray(), - pageNo, pageSize, CONFIG_INFO_AGGR_ROW_MAPPER); + return helper + .fetchPage(sqlCountRows + where.toString(), sqlFetchRows + where.toString(), params.toArray(), pageNo, + pageSize, CONFIG_INFO_AGGR_ROW_MAPPER); } @Override public List findAllAggrGroup() { - String sql = "SELECT DISTINCT data_id, group_id, tenant_id FROM config_info_aggr"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String sql = configInfoAggrMapper.findAllAggrGroup(); return databaseOperate.queryMany(sql, EMPTY_ARRAY, CONFIG_INFO_CHANGED_ROW_MAPPER); @@ -1874,17 +1896,20 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public List findDatumIdByContent(String dataId, String groupId, String content) { - String sql = "SELECT datum_id FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND content = ? "; - + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String sql = configInfoAggrMapper + .select(Collections.singletonList("datum_id"), Arrays.asList("data_id", "group_id", "content")); return databaseOperate.queryMany(sql, new Object[] {dataId, groupId, content}, String.class); } @Override public List findChangeConfig(final Timestamp startTime, final Timestamp endTime) { - List> list = databaseOperate.queryMany( - "SELECT data_id, group_id, tenant_id, app_name, content, gmt_modified FROM config_info WHERE " - + "gmt_modified >=? AND gmt_modified <= ?", new Object[] {startTime, endTime}); + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + List> list = databaseOperate + .queryMany(configInfoMapper.findChangeConfig(), new Object[] {startTime, endTime}); return convertChangeConfig(list); } @@ -1894,48 +1919,51 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { final String appName, final Timestamp startTime, final Timestamp endTime, final int pageNo, final int pageSize, final long lastMaxId) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - final String sqlCountRows = "SELECT count(*) FROM config_info WHERE "; - final String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,app_name,content,type,md5,gmt_modified FROM config_info WHERE "; - String where = " 1=1 "; + Map paramsMap = new HashMap<>(16); List params = new ArrayList<>(); - if (!StringUtils.isBlank(dataId)) { - where += " AND data_id LIKE ? "; params.add(generateLikeArgument(dataId)); + paramsMap.put(DATA_ID, DATA_ID); } if (!StringUtils.isBlank(group)) { - where += " AND group_id LIKE ? "; params.add(generateLikeArgument(group)); + paramsMap.put(GROUP, GROUP); } if (!StringUtils.isBlank(tenantTmp)) { - where += " AND tenant_id = ? "; params.add(tenantTmp); + paramsMap.put(TENANT, TENANT); } if (!StringUtils.isBlank(appName)) { - where += " AND app_name = ? "; params.add(appName); + paramsMap.put(APP_NAME, APP_NAME); } if (startTime != null) { - where += " AND gmt_modified >=? "; params.add(startTime); } if (endTime != null) { - where += " AND gmt_modified <=? "; params.add(endTime); } + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sqlCountRows = configInfoMapper.findChangeConfigCountRows(paramsMap, startTime, endTime); + String sqlFetchRows = configInfoMapper + .findChangeConfigFetchRows(paramsMap, startTime, endTime, startRow, pageSize, lastMaxId); + PaginationHelper helper = createPaginationHelper(); - return helper.fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, - lastMaxId, CONFIG_INFO_WRAPPER_ROW_MAPPER); + return helper.fetchPage(sqlCountRows, sqlFetchRows, params.toArray(), pageNo, pageSize, lastMaxId, + CONFIG_INFO_WRAPPER_ROW_MAPPER); } @Override public List findDeletedConfig(final Timestamp startTime, final Timestamp endTime) { - List> list = databaseOperate.queryMany( - "SELECT DISTINCT data_id, group_id, tenant_id FROM his_config_info WHERE op_type = 'D' AND " - + "gmt_modified >=? AND gmt_modified <= ?", new Object[] {startTime, endTime}); + HistoryConfigInfoMapper historyConfigInfoMapper = (HistoryConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.HIS_CONFIG_INFO).get(); + List> list = databaseOperate + .queryMany(historyConfigInfoMapper.findDeletedConfig(), new Object[] {startTime, endTime}); return convertDeletedConfig(list); } @@ -1955,10 +1983,12 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { final String md5Tmp = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); final String encryptedDataKey = configInfo.getEncryptedDataKey() == null ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); - - final String sql = - "INSERT INTO config_info(id, data_id, group_id, tenant_id, app_name, content, md5, src_ip, src_user, gmt_create," - + "gmt_modified, c_desc, c_use, effect, type, c_schema,encrypted_data_key) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final String sql = configInfoMapper.insert(Arrays + .asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "src_ip", "src_user", + "gmt_create", "gmt_modified", "c_desc", "c_use", "effect", "type", "c_schema", + "encrypted_data_key")); final Object[] args = new Object[] {id, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(), md5Tmp, srcIp, srcUser, time, time, desc, use, effect, type, schema, encryptedDataKey}; @@ -1968,8 +1998,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public void addConfigTagRelationAtomic(long configId, String tagName, String dataId, String group, String tenant) { - final String sql = "INSERT INTO config_tags_relation(id,tag_name,tag_type,data_id,group_id,tenant_id) " - + "VALUES(?,?,?,?,?,?)"; + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + final String sql = configTagsRelationMapper.addConfigTagRelationAtomic(); final Object[] args = new Object[] {configId, tagName, StringUtils.EMPTY, dataId, group, tenant}; EmbeddedStorageContextUtils.addSqlContext(sql, args); } @@ -1986,20 +2017,26 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public void removeTagByIdAtomic(long id) { - final String sql = "DELETE FROM config_tags_relation WHERE id=?"; + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + final String sql = configTagsRelationMapper.removeTagByIdAtomic(); final Object[] args = new Object[] {id}; EmbeddedStorageContextUtils.addSqlContext(sql, args); } @Override public List getConfigTagsByTenant(String tenant) { - String sql = "SELECT tag_name FROM config_tags_relation WHERE tenant_id = ? "; + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + String sql = configTagsRelationMapper.getConfigTagsByTenant(); return databaseOperate.queryMany(sql, new Object[] {tenant}, String.class); } @Override public List selectTagByConfig(String dataId, String group, String tenant) { - String sql = "SELECT tag_name FROM config_tags_relation WHERE data_id=? AND group_id=? AND tenant_id = ? "; + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + String sql = configTagsRelationMapper.selectTagByConfig(); return databaseOperate.queryMany(sql, new Object[] {dataId, group, tenant}, String.class); } @@ -2007,8 +2044,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { public void removeConfigInfoAtomic(final String dataId, final String group, final String tenant, final String srcIp, final String srcUser) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - - final String sql = "DELETE FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final String sql = configInfoMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id")); final Object[] args = new Object[] {dataId, group, tenantTmp}; EmbeddedStorageContextUtils.addSqlContext(sql, args); @@ -2019,18 +2057,14 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { if (StringUtils.isBlank(ids)) { return; } - StringBuilder sql = new StringBuilder(SQL_DELETE_CONFIG_INFO_BY_IDS); - sql.append("id IN ("); List paramList = new ArrayList<>(); String[] tagArr = ids.split(","); for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sql.append(", "); - } - sql.append('?'); paramList.add(Long.parseLong(tagArr[i])); } - sql.append(") "); + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.removeConfigInfoByIdsAtomic(paramList.size()); EmbeddedStorageContextUtils.addSqlContext(sql.toString(), paramList.toArray()); } @@ -2040,7 +2074,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag; - final String sql = "DELETE FROM config_info_tag WHERE data_id=? AND group_id=? AND tenant_id=? AND tag_id=?"; + ConfigInfoTagMapper configInfoTagMapper = (ConfigInfoTagMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO_TAG).get(); + final String sql = configInfoTagMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id", "tag_id")); final Object[] args = new Object[] {dataId, group, tenantTmp, tagTmp}; EmbeddedStorageContextUtils.onDeleteConfigTagInfo(tenantTmp, group, dataId, tagTmp, srcIp); @@ -2068,9 +2104,11 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { final String encryptedDataKey = configInfo.getEncryptedDataKey() == null ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); - final String sql = "UPDATE config_info SET content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?,app_name=?," - + "c_desc=?,c_use=?,effect=?,type=?,c_schema=?,encrypted_data_key=? WHERE data_id=? AND group_id=? AND tenant_id=?"; - + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final String sql = configInfoMapper.update(Arrays + .asList("content", "md5", "src_ip", "src_user", "gmt_modified", "app_name", "c_desc", "c_use", "effect", + "type", "c_schema", "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id")); final Object[] args = new Object[] {configInfo.getContent(), md5Tmp, srcIp, srcUser, time, appNameTmp, desc, use, effect, type, schema, encryptedDataKey, configInfo.getDataId(), configInfo.getGroup(), tenantTmp}; @@ -2090,8 +2128,11 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { final String type = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("type"); final String schema = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("schema"); - final String sql = "UPDATE config_info SET content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?,app_name=?," - + "c_desc=?,c_use=?,effect=?,type=?,c_schema=? WHERE data_id=? AND group_id=? AND tenant_id=? AND (md5=? OR md5 IS NULL OR md5='')"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final String sql = configInfoMapper.update(Arrays + .asList("content", "md5", "src_ip", "src_user", "gmt_modified", "app_name", "c_desc", "c_use", "effect", + "type", "c_schema", "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id")); final Object[] args = new Object[] {configInfo.getContent(), md5Tmp, srcIp, srcUser, time, appNameTmp, desc, use, effect, type, schema, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, @@ -2105,19 +2146,15 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { if (StringUtils.isBlank(ids)) { return null; } - StringBuilder sql = new StringBuilder(SQL_FIND_CONFIG_INFO_BY_IDS); - sql.append("id IN ("); List paramList = new ArrayList<>(); String[] tagArr = ids.split(","); for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sql.append(", "); - } - sql.append('?'); paramList.add(Long.parseLong(tagArr[i])); } - sql.append(") "); - return databaseOperate.queryMany(sql.toString(), paramList.toArray(), CONFIG_INFO_ROW_MAPPER); + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return databaseOperate.queryMany(configInfoMapper.findConfigInfosByIds(tagArr.length), paramList.toArray(), + CONFIG_INFO_ROW_MAPPER); } @@ -2126,8 +2163,11 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; List configTagList = this.selectTagByConfig(dataId, group, tenant); - ConfigAdvanceInfo configAdvance = databaseOperate.queryOne( - "SELECT gmt_create,gmt_modified,src_user,src_ip,c_desc,c_use,effect,type,c_schema FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?", + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + ConfigAdvanceInfo configAdvance = databaseOperate.queryOne(configInfoMapper.select(Arrays + .asList("gmt_create", "gmt_modified", "src_user", "src_ip", "c_desc", "c_use", "effect", "type", + "c_schema"), Arrays.asList("data_id", "group_id", "tenant_id")), new Object[] {dataId, group, tenantTmp}, CONFIG_ADVANCE_INFO_ROW_MAPPER); if (CollectionUtils.isNotEmpty(configTagList)) { @@ -2148,14 +2188,17 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { public ConfigAllInfo findConfigAllInfo(final String dataId, final String group, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - final String sql = "SELECT id,data_id,group_id,tenant_id,app_name,content,md5,gmt_create," - + "gmt_modified,src_user,src_ip,c_desc,c_use,effect,type,c_schema,encrypted_data_key FROM config_info " - + "WHERE data_id=? AND group_id=? AND tenant_id=?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final String sql = configInfoMapper.select(Arrays + .asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "gmt_create", + "gmt_modified", "src_user", "src_ip", "c_desc", "c_use", "effect", "type", "c_schema", + "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id")); List configTagList = selectTagByConfig(dataId, group, tenant); - ConfigAllInfo configAdvance = databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp}, - CONFIG_ALL_INFO_ROW_MAPPER); + ConfigAllInfo configAdvance = databaseOperate + .queryOne(sql, new Object[] {dataId, group, tenantTmp}, CONFIG_ALL_INFO_ROW_MAPPER); if (configTagList != null && !configTagList.isEmpty()) { StringBuilder configTagsTmp = new StringBuilder(); @@ -2180,8 +2223,11 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { String encryptedDataKey = StringUtils.isBlank(configInfo.getEncryptedDataKey()) ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); - final String sql = "INSERT INTO his_config_info (id,data_id,group_id,tenant_id,app_name,content,md5," - + "src_ip,src_user,gmt_modified,op_type,encrypted_data_key) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)"; + HistoryConfigInfoMapper historyConfigInfoMapper = (HistoryConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.HIS_CONFIG_INFO).get(); + final String sql = historyConfigInfoMapper.insert(Arrays + .asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "src_ip", "src_user", + "gmt_modified", "op_type", "encrypted_data_key")); final Object[] args = new Object[] {configHistoryId, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(), md5Tmp, srcIp, srcUser, time, ops, encryptedDataKey}; @@ -2192,8 +2238,11 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { public Page findConfigHistory(String dataId, String group, String tenant, int pageNo, int pageSize) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sqlCountRows = "SELECT count(*) FROM his_config_info WHERE data_id = ? AND group_id = ? AND tenant_id = ?"; - String sqlFetchRows = "SELECT nid,data_id,group_id,tenant_id,app_name,src_ip,src_user,op_type,gmt_create,gmt_modified FROM his_config_info WHERE data_id = ? AND group_id = ? AND tenant_id = ? ORDER BY nid DESC"; + + HistoryConfigInfoMapper historyConfigInfoMapper = (HistoryConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.HIS_CONFIG_INFO).get(); + String sqlCountRows = historyConfigInfoMapper.findConfigHistoryCountRows(); + String sqlFetchRows = historyConfigInfoMapper.findConfigHistoryFetchRows(); PaginationHelper helper = createPaginationHelper(); return helper.fetchPage(sqlCountRows, sqlFetchRows, new Object[] {dataId, group, tenantTmp}, pageNo, pageSize, @@ -2235,13 +2284,20 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public ConfigHistoryInfo detailConfigHistory(Long nid) { - String sqlFetchRows = "SELECT nid,data_id,group_id,tenant_id,app_name,content,md5,src_user,src_ip,op_type,gmt_create,gmt_modified,encrypted_data_key FROM his_config_info WHERE nid = ?"; + HistoryConfigInfoMapper historyConfigInfoMapper = (HistoryConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.HIS_CONFIG_INFO).get(); + String sqlFetchRows = historyConfigInfoMapper.select(Arrays + .asList("nid", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "src_user", "src_ip", + "op_type", "gmt_create", "gmt_modified", "encrypted_data_key"), + Collections.singletonList("nid")); return databaseOperate.queryOne(sqlFetchRows, new Object[] {nid}, HISTORY_DETAIL_ROW_MAPPER); } @Override public ConfigHistoryInfo detailPreviousConfigHistory(Long id) { - String sqlFetchRows = "SELECT nid,data_id,group_id,tenant_id,app_name,content,md5,src_user,src_ip,op_type,gmt_create,gmt_modified FROM his_config_info WHERE nid = (SELECT max(nid) FROM his_config_info WHERE id = ?)"; + HistoryConfigInfoMapper historyConfigInfoMapper = (HistoryConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.HIS_CONFIG_INFO).get(); + String sqlFetchRows = historyConfigInfoMapper.detailPreviousConfigHistory(); return databaseOperate.queryOne(sqlFetchRows, new Object[] {id}, HISTORY_DETAIL_ROW_MAPPER); } @@ -2357,11 +2413,14 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public List listGroupKeyMd5ByPage(int pageNo, int pageSize) { - String sqlCountRows = " SELECT count(*) FROM config_info "; - String sqlFetchRows = " SELECT t.id,data_id,group_id,tenant_id,app_name,type,md5,gmt_modified FROM ( SELECT id FROM config_info ORDER BY id LIMIT ?,? ) g, config_info t WHERE g.id = t.id"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sqlCountRows = configInfoMapper.count(); + String sqlFetchRows = configInfoMapper.listGroupKeyMd5ByPageFetchRows(); PaginationHelper helper = createPaginationHelper(); - Page page = helper.fetchPageLimit(sqlCountRows, sqlFetchRows, - new Object[] {(pageNo - 1) * pageSize, pageSize}, pageNo, pageSize, CONFIG_INFO_WRAPPER_ROW_MAPPER); + Page page = helper + .fetchPageLimit(sqlCountRows, sqlFetchRows, new Object[] {(pageNo - 1) * pageSize, pageSize}, pageNo, + pageSize, CONFIG_INFO_WRAPPER_ROW_MAPPER); return page.getPageItems(); } @@ -2380,7 +2439,11 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { @Override public ConfigInfoWrapper queryConfigInfo(final String dataId, final String group, final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - final String sql = "SELECT id,data_id,group_id,tenant_id,app_name,content,type,gmt_modified,md5 FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final String sql = configInfoMapper.select(Arrays + .asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "type", "gmt_modified", "md5", + "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id")); return databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_WRAPPER_ROW_MAPPER); } @@ -2415,8 +2478,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { try { updateMd5(cf.getDataId(), cf.getGroup(), tenant, md5, new Timestamp(cf.getLastModified())); } catch (Throwable e) { - LogUtil.DEFAULT_LOG.error("[completeMd5-error] datId:{} group:{} lastModified:{}", - cf.getDataId(), cf.getGroup(), new Timestamp(cf.getLastModified())); + LogUtil.DEFAULT_LOG + .error("[completeMd5-error] datId:{} group:{} lastModified:{}", cf.getDataId(), + cf.getGroup(), new Timestamp(cf.getLastModified())); } } else { if (!md5InDb.equals(md5)) { @@ -2424,8 +2488,9 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { updateMd5(cf.getDataId(), cf.getGroup(), tenant, md5, new Timestamp(cf.getLastModified())); } catch (Throwable e) { - LogUtil.DEFAULT_LOG.error("[completeMd5-error] datId:{} group:{} lastModified:{}", - cf.getDataId(), cf.getGroup(), new Timestamp(cf.getLastModified())); + LogUtil.DEFAULT_LOG + .error("[completeMd5-error] datId:{} group:{} lastModified:{}", cf.getDataId(), + cf.getGroup(), new Timestamp(cf.getLastModified())); } } } @@ -2442,36 +2507,29 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { public List findAllConfigInfo4Export(final String dataId, final String group, final String tenant, final String appName, final List ids) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - StringBuilder where = new StringBuilder(" WHERE "); + Map params = new HashMap<>(16); List paramList = new ArrayList<>(); if (!CollectionUtils.isEmpty(ids)) { - where.append(" id IN ("); - for (int i = 0; i < ids.size(); i++) { - if (i != 0) { - where.append(", "); - } - where.append('?'); - paramList.add(ids.get(i)); - } - where.append(") "); + paramList.addAll(ids); } else { - where.append(" tenant_id=? "); paramList.add(tenantTmp); if (!StringUtils.isBlank(dataId)) { - where.append(" AND data_id LIKE ? "); paramList.add(generateLikeArgument(dataId)); + params.put(DATA_ID, DATA_ID); } if (StringUtils.isNotBlank(group)) { - where.append(" AND group_id=? "); paramList.add(group); + params.put(GROUP, GROUP); } if (StringUtils.isNotBlank(appName)) { - where.append(" AND app_name=? "); paramList.add(appName); + params.put(APP_NAME, APP_NAME); } } - return databaseOperate.queryMany(SQL_FIND_ALL_CONFIG_INFO + where, paramList.toArray(), - CONFIG_ALL_INFO_ROW_MAPPER); + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.findAllConfigInfo4Export(ids, params); + return databaseOperate.queryMany(sql, paramList.toArray(), CONFIG_ALL_INFO_ROW_MAPPER); } @Override @@ -2492,8 +2550,8 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { for (int i = 0; i < configInfoList.size(); i++) { ConfigAllInfo configInfo = configInfoList.get(i); try { - ParamUtils.checkParam(configInfo.getDataId(), configInfo.getGroup(), "datumId", - configInfo.getContent()); + ParamUtils + .checkParam(configInfo.getDataId(), configInfo.getGroup(), "datumId", configInfo.getContent()); } catch (Throwable e) { DEFAULT_LOG.error("data verification failed", e); throw e; @@ -2578,8 +2636,8 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { if (Objects.isNull(tenantId)) { throw new IllegalArgumentException("tenantId can not be null"); } - Integer result = databaseOperate.queryOne(SQL_TENANT_INFO_COUNT_BY_TENANT_ID, new String[] {tenantId}, - Integer.class); + Integer result = databaseOperate + .queryOne(SQL_TENANT_INFO_COUNT_BY_TENANT_ID, new String[] {tenantId}, Integer.class); if (result == null) { return 0; } @@ -2592,7 +2650,11 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService { throw new IllegalArgumentException("tenantId can not be null"); } String tenantTmp = StringUtils.isBlank(tenantId) ? StringUtils.EMPTY : tenantId; - final String sql = "SELECT data_id,group_id,tenant_id,app_name,type FROM config_info WHERE tenant_id=?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager + .findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final String sql = configInfoMapper + .select(Arrays.asList("data_id", "group_id", "tenant_id", "app_name", "type"), + Collections.singletonList("tenant_id")); return databaseOperate.queryMany(sql, new Object[] {tenantTmp}, CONFIG_INFO_WRAPPER_ROW_MAPPER); } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalStoragePaginationHelperImpl.java b/config/src/main/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalStoragePaginationHelperImpl.java index 66dc082d1..a0478feb5 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalStoragePaginationHelperImpl.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalStoragePaginationHelperImpl.java @@ -19,8 +19,6 @@ package com.alibaba.nacos.config.server.service.repository.extrnal; import com.alibaba.nacos.config.server.model.Page; import com.alibaba.nacos.config.server.service.repository.PaginationHelper; import com.alibaba.nacos.config.server.service.sql.EmbeddedStorageContextUtils; -import com.alibaba.nacos.config.server.utils.PropertyUtil; -import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; @@ -86,17 +84,7 @@ class ExternalStoragePaginationHelperImpl implements PaginationHelper { return page; } - final int startRow = (pageNo - 1) * pageSize; - String selectSql = ""; - if (isDerby()) { - selectSql = sqlFetchRows + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; - } else if (lastMaxId != null) { - selectSql = sqlFetchRows + " AND id > " + lastMaxId + " ORDER BY id ASC" + " LIMIT " + 0 + "," + pageSize; - } else { - selectSql = sqlFetchRows + " LIMIT " + startRow + "," + pageSize; - } - - List result = jdbcTemplate.query(selectSql, args, rowMapper); + List result = jdbcTemplate.query(sqlFetchRows, args, rowMapper); for (E item : result) { page.getPageItems().add(item); } @@ -130,13 +118,8 @@ class ExternalStoragePaginationHelperImpl implements PaginationHelper { if (pageNo > pageCount) { return page; } - - String selectSql = sqlFetchRows; - if (isDerby()) { - selectSql = selectSql.replaceAll("(?i)LIMIT \\?,\\?", "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"); - } - - List result = jdbcTemplate.query(selectSql, args, rowMapper); + + List result = jdbcTemplate.query(sqlFetchRows, args, rowMapper); for (E item : result) { page.getPageItems().add(item); } @@ -170,13 +153,7 @@ class ExternalStoragePaginationHelperImpl implements PaginationHelper { if (pageNo > pageCount) { return page; } - - String selectSql = sqlFetchRows; - if (isDerby()) { - selectSql = selectSql.replaceAll("(?i)LIMIT \\?,\\?", "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"); - } - - List result = jdbcTemplate.query(selectSql, args2, rowMapper); + List result = jdbcTemplate.query(sqlFetchRows, args2, rowMapper); for (E item : result) { page.getPageItems().add(item); } @@ -191,13 +168,7 @@ class ExternalStoragePaginationHelperImpl implements PaginationHelper { } // Create Page object final Page page = new Page<>(); - - String selectSql = sqlFetchRows; - if (isDerby()) { - selectSql = selectSql.replaceAll("(?i)LIMIT \\?,\\?", "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"); - } - - List result = jdbcTemplate.query(selectSql, args, rowMapper); + List result = jdbcTemplate.query(sqlFetchRows, args, rowMapper); for (E item : result) { page.getPageItems().add(item); } @@ -206,14 +177,8 @@ class ExternalStoragePaginationHelperImpl implements PaginationHelper { @Override public void updateLimit(final String sql, final Object[] args) { - String sqlUpdate = sql; - - if (isDerby()) { - sqlUpdate = sqlUpdate.replaceAll("LIMIT \\?", "OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY"); - } - try { - jdbcTemplate.update(sqlUpdate, args); + jdbcTemplate.update(sql, args); } finally { EmbeddedStorageContextUtils.cleanAllContext(); } @@ -222,10 +187,6 @@ class ExternalStoragePaginationHelperImpl implements PaginationHelper { public int updateLimitWithResponse(final String sql, final Object[] args) { String sqlUpdate = sql; - if (isDerby()) { - sqlUpdate = sqlUpdate.replaceAll("LIMIT \\?", "OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY"); - } - try { return jdbcTemplate.update(sqlUpdate, args); } finally { @@ -233,9 +194,4 @@ class ExternalStoragePaginationHelperImpl implements PaginationHelper { } } - private boolean isDerby() { - return (EnvUtil.getStandaloneMode() && !PropertyUtil.isUseExternalDB()) || PropertyUtil - .isEmbeddedStorage(); - } - } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalStoragePersistServiceImpl.java b/config/src/main/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalStoragePersistServiceImpl.java index e05a6f1fb..e2e22f403 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalStoragePersistServiceImpl.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalStoragePersistServiceImpl.java @@ -19,6 +19,7 @@ package com.alibaba.nacos.config.server.service.repository.extrnal; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.Pair; +import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.configuration.ConditionOnExternalStorage; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.enums.FileTypeEnum; @@ -43,9 +44,17 @@ import com.alibaba.nacos.config.server.service.repository.PaginationHelper; import com.alibaba.nacos.config.server.service.repository.PersistService; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.ParamUtils; +import com.alibaba.nacos.plugin.datasource.MapperManager; +import com.alibaba.nacos.plugin.datasource.constants.TableConstant; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoAggrMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoBetaMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoTagMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigTagsRelationMapper; +import com.alibaba.nacos.plugin.datasource.mapper.HistoryConfigInfoMapper; import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler; +import com.alibaba.nacos.sys.env.EnvUtil; import org.apache.commons.collections.CollectionUtils; -import com.alibaba.nacos.common.utils.StringUtils; import org.springframework.context.annotation.Conditional; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataIntegrityViolationException; @@ -72,12 +81,12 @@ import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.config.server.service.repository.RowMapperManager.CONFIG_ADVANCE_INFO_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.RowMapperManager.CONFIG_ALL_INFO_ROW_MAPPER; @@ -122,6 +131,24 @@ public class ExternalStoragePersistServiceImpl implements PersistService { protected TransactionTemplate tjt; + private MapperManager mapperManager; + + private String dataSource; + + private static final String DATASOURCE_PLATFORM_PROPERTY = "spring.datasource.platform"; + + private static final String DEFAULT_DATASOURCE_PLATFORM = "mysql"; + + private static final String DATA_ID = "dataId"; + + private static final String GROUP = "group"; + + private static final String APP_NAME = "appName"; + + private static final String CONTENT = "content"; + + private static final String TENANT = "tenant_id"; + /** * constant variables. */ @@ -136,6 +163,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { jt = getJdbcTemplate(); tjt = getTransactionTemplate(); + mapperManager = MapperManager.instance(); + dataSource = EnvUtil.getProperty(DATASOURCE_PLATFORM_PROPERTY, DEFAULT_DATASOURCE_PLATFORM); } public boolean checkMasterWritable() { @@ -201,8 +230,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String encryptedDataKey = StringUtils.isBlank(configInfo.getEncryptedDataKey()) ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); try { - jt.update("INSERT INTO config_info_beta(data_id,group_id,tenant_id,app_name,content,md5,beta_ips,src_ip," - + "src_user,gmt_create,gmt_modified,encrypted_data_key) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)", + ConfigInfoBetaMapper configInfoBetaMapper = (ConfigInfoBetaMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_BETA).get(); + jt.update(configInfoBetaMapper.insert(Arrays.asList("data_id", "group_id", "tenant_id", "app_name", "content", "md5", "beta_ips", "src_ip", + "src_user", "gmt_create", "gmt_modified", "encrypted_data_key")), configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(), md5, betaIps, srcIp, srcUser, time, time, encryptedDataKey); } catch (CannotGetJdbcConnectionException e) { @@ -219,9 +249,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); try { - jt.update( - "INSERT INTO config_info_tag(data_id,group_id,tenant_id,tag_id,app_name,content,md5,src_ip,src_user," - + "gmt_create,gmt_modified) VALUES(?,?,?,?,?,?,?,?,?,?,?)", configInfo.getDataId(), + ConfigInfoTagMapper configInfoTagMapper = (ConfigInfoTagMapper) mapperManager.findMapper(dataSource, + TableConstant.CONFIG_INFO_TAG).get(); + jt.update(configInfoTagMapper.insert(Arrays.asList("data_id", "group_id", "tenant_id", "tag_id", "app_name", + "content", "md5", "src_ip", "src_user", "gmt_create", "gmt_modified")), configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp, appNameTmp, configInfo.getContent(), md5, srcIp, srcUser, time, time); } catch (CannotGetJdbcConnectionException e) { @@ -306,9 +337,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String encryptedDataKey = StringUtils.isBlank(configInfo.getEncryptedDataKey()) ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); try { - jt.update( - "UPDATE config_info_beta SET content=?, md5=?, beta_ips=?, src_ip=?,src_user=?,gmt_modified=?,app_name=?,encrypted_data_key=? " - + " WHERE data_id=? AND group_id=? AND tenant_id=?", configInfo.getContent(), md5, betaIps, srcIp, srcUser, + ConfigInfoBetaMapper configInfoBetaMapper = (ConfigInfoBetaMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_BETA).get(); + jt.update(configInfoBetaMapper.update(Arrays.asList("content", "md5", "beta_ips", "src_ip", "src_user", "gmt_modified", "app_name", "encrypted_data_key"), + Arrays.asList("data_id", "group_id", "tenant_id")), configInfo.getContent(), md5, betaIps, srcIp, srcUser, time, appNameTmp, encryptedDataKey, configInfo.getDataId(), configInfo.getGroup(), tenantTmp); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -323,11 +354,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); try { - return jt - .update("UPDATE config_info_beta SET content=?, md5=?, beta_ips=?, src_ip=?,src_user=?,gmt_modified=?,app_name=? " - + " WHERE data_id=? AND group_id=? AND tenant_id=? AND (md5=? or md5 is null or md5='')", - configInfo.getContent(), md5, betaIps, srcIp, srcUser, time, appNameTmp, configInfo.getDataId(), - configInfo.getGroup(), tenantTmp, configInfo.getMd5()) > 0; + ConfigInfoBetaMapper configInfoBetaMapper = (ConfigInfoBetaMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_BETA).get(); + return jt.update(configInfoBetaMapper.updateConfigInfo4BetaCas(), + configInfo.getContent(), md5, betaIps, srcIp, srcUser, time, appNameTmp, configInfo.getDataId(), + configInfo.getGroup(), tenantTmp, configInfo.getMd5()) > 0; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); throw e; @@ -342,9 +372,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); - jt.update( - "UPDATE config_info_tag SET content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?,app_name=? WHERE " - + "data_id=? AND group_id=? AND tenant_id=? AND tag_id=?", configInfo.getContent(), md5, + ConfigInfoTagMapper configInfoTagMapper = (ConfigInfoTagMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_TAG).get(); + jt.update(configInfoTagMapper.update(Arrays.asList("content", "md5", "src_ip", "src_user", "gmt_modified", "app_name"), + Arrays.asList("data_id", "group_id", "tenant_id", "tag_id")), configInfo.getContent(), md5, srcIp, srcUser, time, appNameTmp, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -360,9 +390,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); + ConfigInfoTagMapper configInfoTagMapper = (ConfigInfoTagMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_TAG).get(); return jt - .update("UPDATE config_info_tag SET content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?,app_name=? WHERE " - + "data_id=? AND group_id=? AND tenant_id=? AND tag_id=? AND (md5=? or md5 is null or md5='')", + .update(configInfoTagMapper.updateConfigInfo4TagCas(), configInfo.getContent(), md5, srcIp, srcUser, time, appNameTmp, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp, configInfo.getMd5()) > 0; } catch (CannotGetJdbcConnectionException e) { @@ -417,8 +447,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { public void updateMd5(String dataId, String group, String tenant, String md5, Timestamp lastTime) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { - jt.update( - "UPDATE config_info SET md5 = ? WHERE data_id=? AND group_id=? AND tenant_id=? AND gmt_modified=?", + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + jt.update(configInfoMapper.update( + Collections.singletonList("md5"), Arrays.asList("data_id", "group_id", "tenant_id", "gmt_modified")), md5, dataId, group, tenantTmp, lastTime); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -529,7 +560,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { try { ConfigInfo configInfo = findConfigInfo4Beta(dataId, group, tenant); if (configInfo != null) { - jt.update("DELETE FROM config_info_beta WHERE data_id=? AND group_id=? AND tenant_id=?", dataId, + ConfigInfoBetaMapper configInfoBetaMapper = (ConfigInfoBetaMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_BETA).get(); + jt.update(configInfoBetaMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id")), dataId, group, tenantTmp); } } catch (CannotGetJdbcConnectionException e) { @@ -548,9 +580,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String appNameTmp = StringUtils.isBlank(appName) ? StringUtils.EMPTY : appName; String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; final Timestamp now = new Timestamp(System.currentTimeMillis()); - String select = "SELECT content FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND datum_id = ?"; - String insert = "INSERT INTO config_info_aggr(data_id, group_id, tenant_id, datum_id, app_name, content, gmt_modified) VALUES(?,?,?,?,?,?,?) "; - String update = "UPDATE config_info_aggr SET content = ? , gmt_modified = ? WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND datum_id = ?"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String select = configInfoAggrMapper.select(Collections.singletonList("content"), Arrays.asList("data_id", "group_id", "tenant_id", "datum_id")); + String insert = configInfoAggrMapper.insert(Arrays.asList("data_id", "group_id", "tenant_id", "datum_id", "app_name", "content", "gmt_modified")); + String update = configInfoAggrMapper.update(Arrays.asList("content", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id", "datum_id")); try { try { @@ -575,7 +608,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { public void removeSingleAggrConfigInfo(final String dataId, final String group, final String tenant, final String datumId) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = "DELETE FROM config_info_aggr WHERE data_id=? AND group_id=? AND tenant_id=? AND datum_id=?"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String sql = configInfoAggrMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id", "datum_id")); try { this.jt.update(sql, new PreparedStatementSetter() { @@ -597,7 +631,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public void removeAggrConfigInfo(final String dataId, final String group, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = "DELETE FROM config_info_aggr WHERE data_id=? AND group_id=? AND tenant_id=?"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String sql = configInfoAggrMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id")); try { this.jt.update(sql, new PreparedStatementSetter() { @@ -624,9 +659,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { datumString.append('\'').append(datum).append("',"); } datumString.deleteCharAt(datumString.length() - 1); + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); final String sql = - "DELETE FROM config_info_aggr WHERE data_id=? AND group_id=? AND tenant_id=? AND datum_id IN (" - + datumString.toString() + ")"; + configInfoAggrMapper.batchRemoveAggr(datumList); try { jt.update(sql, dataId, group, tenantTmp); } catch (CannotGetJdbcConnectionException e) { @@ -638,28 +673,16 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public void removeConfigHistory(final Timestamp startTime, final int limitSize) { - String sql = "DELETE FROM his_config_info WHERE gmt_modified < ? LIMIT ?"; - ExternalStoragePaginationHelperImpl paginationHelper = (ExternalStoragePaginationHelperImpl) createPaginationHelper(); - int count; - try { - count = paginationHelper.updateLimitWithResponse(sql, new Object[] {startTime, limitSize}); - while (count > 0) { - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - LogUtil.FATAL_LOG.error("[interrupt-error] " + e, e); - } - count = paginationHelper.updateLimitWithResponse(sql, new Object[] {startTime, limitSize}); - } - } catch (CannotGetJdbcConnectionException e) { - LogUtil.FATAL_LOG.error("[db-error] " + e, e); - throw e; - } + HistoryConfigInfoMapper historyConfigInfoMapper = (HistoryConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.HIS_CONFIG_INFO).get(); + String sql = historyConfigInfoMapper.removeConfigHistory(); + PaginationHelper paginationHelper = createPaginationHelper(); + paginationHelper.updateLimit(sql, new Object[] {startTime, limitSize}); } @Override public int findConfigHistoryCountByTime(final Timestamp startTime) { - String sql = "SELECT count(*) FROM his_config_info WHERE gmt_modified < ?"; + HistoryConfigInfoMapper historyConfigInfoMapper = (HistoryConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.HIS_CONFIG_INFO).get(); + String sql = historyConfigInfoMapper.findConfigHistoryCountByTime(); Integer result = jt.queryForObject(sql, Integer.class, new Object[] {startTime}); if (result == null) { throw new IllegalArgumentException("configInfoBetaCount error"); @@ -669,7 +692,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public long findConfigMaxId() { - String sql = "SELECT max(id) FROM config_info"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.findConfigMaxId(); try { return jt.queryForObject(sql, Long.class); } catch (NullPointerException e) { @@ -707,12 +731,13 @@ public class ExternalStoragePersistServiceImpl implements PersistService { public boolean replaceAggr(final String dataId, final String group, final String tenant, final Map datumMap, final String appName) { try { + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); Boolean isReplaceOk = tjt.execute(status -> { try { String appNameTmp = appName == null ? "" : appName; removeAggrConfigInfo(dataId, group, tenant); String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = "INSERT INTO config_info_aggr(data_id, group_id, tenant_id, datum_id, app_name, content, gmt_modified) VALUES(?,?,?,?,?,?,?) "; + String sql = configInfoAggrMapper.insert(Arrays.asList("data_id", "group_id", "tenant_id", "datum_id", "app_name", "content", "gmt_modified")); for (Map.Entry datumEntry : datumMap.entrySet()) { jt.update(sql, dataId, group, tenantTmp, datumEntry.getKey(), appNameTmp, datumEntry.getValue(), new Timestamp(System.currentTimeMillis())); @@ -736,7 +761,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Deprecated @Override public List findAllDataIdAndGroup() { - String sql = "SELECT DISTINCT data_id, group_id FROM config_info"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.findAllDataIdAndGroup(); try { return jt.query(sql, new Object[] {}, CONFIG_INFO_ROW_MAPPER); @@ -755,8 +781,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { public ConfigInfoBetaWrapper findConfigInfo4Beta(final String dataId, final String group, final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { - return this.jt.queryForObject( - "SELECT id,data_id,group_id,tenant_id,app_name,content,beta_ips,encrypted_data_key FROM config_info_beta WHERE data_id=? AND group_id=? AND tenant_id=?", + ConfigInfoBetaMapper configInfoBetaMapper = (ConfigInfoBetaMapper) mapperManager.findMapper(dataSource, TableConstant. CONFIG_INFO_BETA).get(); + return this.jt.queryForObject(configInfoBetaMapper.select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "beta_ips", "encrypted_data_key"), + Arrays.asList("data_id", "group_id", "tenant_id")), new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. return null; @@ -772,8 +799,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); try { - return this.jt.queryForObject( - "SELECT id,data_id,group_id,tenant_id,tag_id,app_name,content FROM config_info_tag WHERE data_id=? AND group_id=? AND tenant_id=? AND tag_id=?", + ConfigInfoTagMapper configInfoTagMapper = (ConfigInfoTagMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_TAG).get(); + return this.jt.queryForObject(configInfoTagMapper.select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "tag_id", "app_name", "content"), + Arrays.asList("data_id", "group_id", "tenant_id", "tag_id")), new Object[] {dataId, group, tenantTmp, tagTmp}, CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. return null; @@ -788,8 +816,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { final String appName) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { - return this.jt.queryForObject( - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=? AND app_name=?", + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return this.jt.queryForObject(configInfoMapper.select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content"), + Arrays.asList("data_id", "group_id", "tenant_id", "app_name")), new Object[] {dataId, group, tenantTmp, appName}, CONFIG_INFO_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. return null; @@ -810,32 +839,20 @@ public class ExternalStoragePersistServiceImpl implements PersistService { paramList.add(group); paramList.add(tenantTmp); - StringBuilder sql = new StringBuilder( - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=? "); + String sql = null; + Map params = new HashMap<>(16); + if (StringUtils.isNotBlank(appName)) { + paramList.add(appName); + params.put(APP_NAME, APP_NAME); + } if (StringUtils.isNotBlank(configTags)) { - sql = new StringBuilder( - "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " - + "config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.group_id=? AND a.tenant_id=? "); - sql.append(" AND b.tag_name IN ("); String[] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sql.append(", "); - } - sql.append('?'); - paramList.add(tagArr[i]); - } - sql.append(") "); - - if (StringUtils.isNotBlank(appName)) { - sql.append(" AND a.app_name=? "); - paramList.add(appName); - } + paramList.addAll(Arrays.asList(tagArr)); + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + sql = configTagsRelationMapper.findConfigInfoAdvanceInfo(params, paramList.size()); } else { - if (StringUtils.isNotBlank(appName)) { - sql.append(" AND app_name=? "); - paramList.add(appName); - } + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + sql = configInfoMapper.findConfigInfoAdvanceInfo(params); } try { @@ -852,8 +869,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public ConfigInfoBase findConfigInfoBase(final String dataId, final String group) { try { - return this.jt.queryForObject( - "SELECT id,data_id,group_id,content FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?", + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return this.jt.queryForObject(configInfoMapper.select(Arrays.asList("id", "data_id", "group_id", "content"), + Arrays.asList("data_id", "group_id", "tenant_id")), new Object[] {dataId, group, StringUtils.EMPTY}, CONFIG_INFO_BASE_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. return null; @@ -866,8 +884,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public ConfigInfo findConfigInfo(long id) { try { + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); return this.jt - .queryForObject("SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE id=?", + .queryForObject(configInfoMapper.select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content"), + Collections.singletonList("id")), new Object[] {id}, CONFIG_INFO_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. return null; @@ -881,8 +901,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { public ConfigInfoWrapper findConfigInfo(final String dataId, final String group, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { - return this.jt.queryForObject( - "SELECT id,data_id,group_id,tenant_id,app_name,content,md5,type,encrypted_data_key FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?", + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return this.jt.queryForObject(configInfoMapper.select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "type", "encrypted_data_key"), + Arrays.asList("data_id", "group_id", "tenant_id")), new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. return null; @@ -898,8 +919,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; PaginationHelper helper = createPaginationHelper(); try { - return helper.fetchPage("SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=?", - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND tenant_id=?", + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return helper.fetchPage(configInfoMapper.findConfigInfoByDataIdCountRows(), + configInfoMapper.findConfigInfoByDataIdFetchRows(startRow, pageSize), new Object[] {dataId, tenantTmp}, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -913,8 +936,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; PaginationHelper helper = createPaginationHelper(); try { - return helper.fetchPage("SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=? AND app_name=?", - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND tenant_id=? AND app_name=?", + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return helper.fetchPage(configInfoMapper.findConfigInfoByDataIdAndAppCountRows(), + configInfoMapper.findConfigInfoByDataIdAndAppFetchRows(startRow, pageSize), new Object[] {dataId, tenantTmp, appName}, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -929,49 +954,30 @@ public class ExternalStoragePersistServiceImpl implements PersistService { PaginationHelper helper = createPaginationHelper(); final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - StringBuilder sqlCount = new StringBuilder("SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=? "); - StringBuilder sql = new StringBuilder( - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND tenant_id=? "); + String sql = null; + String sqlCount = null; + Map paramsMap = new HashMap<>(16); List paramList = new ArrayList<>(); paramList.add(dataId); paramList.add(tenantTmp); + if (StringUtils.isNotBlank(appName)) { + paramList.add(appName); + paramsMap.put(APP_NAME, APP_NAME); + } + final int startRow = (pageNo - 1) * pageSize; if (StringUtils.isNotBlank(configTags)) { - sqlCount = new StringBuilder( - "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.tenant_id=? "); - - sql = new StringBuilder( - "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " - + "config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.tenant_id=? "); - - sqlCount.append(" AND b.tag_name IN ("); - sql.append(" AND b.tag_name IN ("); String[] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sqlCount.append(", "); - sql.append(", "); - } - sqlCount.append('?'); - sql.append('?'); - paramList.add(tagArr[i]); - } - sqlCount.append(") "); - sql.append(") "); - - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" AND a.app_name=? "); - sql.append(" AND a.app_name=? "); - paramList.add(appName); - } + paramList.addAll(Arrays.asList(tagArr)); + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + sqlCount = configTagsRelationMapper.findConfigInfoByDataIdAndAdvanceCountRows(paramsMap, paramList.size()); + sql = configTagsRelationMapper.findConfigInfoByDataIdAndAdvanceFetchRows(paramsMap, paramList.size(), startRow, pageSize); } else { - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" AND app_name=? "); - sql.append(" AND app_name=? "); - paramList.add(appName); - } + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + sqlCount = configInfoMapper.findConfigInfoByDataIdAndAdvanceCountRows(paramsMap); + sql = configInfoMapper.findConfigInfoByDataIdAndAdvanceFetchRows(paramsMap, startRow, pageSize); } try { - return helper.fetchPage(sqlCount.toString(), sql.toString(), paramList.toArray(), pageNo, pageSize, + return helper.fetchPage(sqlCount, sql, paramList.toArray(), pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -986,58 +992,37 @@ public class ExternalStoragePersistServiceImpl implements PersistService { PaginationHelper helper = createPaginationHelper(); final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - String sqlCount = "SELECT count(*) FROM config_info"; - String sql = "SELECT id,data_id,group_id,tenant_id,app_name,content,type,encrypted_data_key FROM config_info"; - StringBuilder where = new StringBuilder(" WHERE "); + String sql = null; + String sqlCount = null; List paramList = new ArrayList<>(); paramList.add(tenantTmp); + Map paramsMap = new HashMap<>(16); + if (StringUtils.isNotBlank(dataId)) { + paramList.add(dataId); + paramsMap.put(DATA_ID, DATA_ID); + } + if (StringUtils.isNotBlank(group)) { + paramList.add(group); + paramsMap.put(GROUP, GROUP); + } + if (StringUtils.isNotBlank(appName)) { + paramList.add(appName); + paramsMap.put(APP_NAME, APP_NAME); + } + final int startRow = (pageNo - 1) * pageSize; if (StringUtils.isNotBlank(configTags)) { - sqlCount = "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id"; - sql = "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " - + "config_tags_relation b ON a.id=b.id"; - - where.append(" a.tenant_id=? "); - - if (StringUtils.isNotBlank(dataId)) { - where.append(" AND a.data_id=? "); - paramList.add(dataId); - } - if (StringUtils.isNotBlank(group)) { - where.append(" AND a.group_id=? "); - paramList.add(group); - } - if (StringUtils.isNotBlank(appName)) { - where.append(" AND a.app_name=? "); - paramList.add(appName); - } - - where.append(" AND b.tag_name IN ("); String[] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - where.append(", "); - } - where.append('?'); - paramList.add(tagArr[i]); - } - where.append(") "); + paramList.addAll(Arrays.asList(tagArr)); + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + sqlCount = configTagsRelationMapper.findConfigInfo4PageCountRows(paramsMap, paramList.size()); + sql = configTagsRelationMapper.findConfigInfo4PageFetchRows(paramsMap, paramList.size(), startRow, pageSize); } else { - where.append(" tenant_id=? "); - if (StringUtils.isNotBlank(dataId)) { - where.append(" AND data_id=? "); - paramList.add(dataId); - } - if (StringUtils.isNotBlank(group)) { - where.append(" AND group_id=? "); - paramList.add(group); - } - if (StringUtils.isNotBlank(appName)) { - where.append(" AND app_name=? "); - paramList.add(appName); - } + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + sqlCount = configInfoMapper.findConfigInfo4PageCountRows(paramsMap); + sql = configInfoMapper.findConfigInfo4PageFetchRows(paramsMap, startRow, pageSize); } try { - Page page = helper.fetchPage(sqlCount + where, sql + where, paramList.toArray(), pageNo, + Page page = helper.fetchPage(sqlCount, sql, paramList.toArray(), pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); for (ConfigInfo configInfo : page.getPageItems()) { Pair pair = EncryptionHandler.decryptHandler(configInfo.getDataId(), @@ -1055,8 +1040,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { public Page findConfigInfoBaseByDataId(final int pageNo, final int pageSize, final String dataId) { PaginationHelper helper = createPaginationHelper(); try { - return helper.fetchPage("SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=?", - "SELECT id,data_id,group_id,content FROM config_info WHERE data_id=? AND tenant_id=?", + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return helper.fetchPage(configInfoMapper.findConfigInfoBaseByDataIdCountRows(), + configInfoMapper.findConfigInfoBaseByDataIdFetchRows(startRow, pageSize), new Object[] {dataId, StringUtils.EMPTY}, pageNo, pageSize, CONFIG_INFO_BASE_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -1070,8 +1057,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; PaginationHelper helper = createPaginationHelper(); try { - return helper.fetchPage("SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=?", - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE group_id=? AND tenant_id=?", + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return helper.fetchPage(configInfoMapper.findConfigInfoByGroupCountRows(), + configInfoMapper.findConfigInfoByGroupFetchRows(startRow, pageSize), new Object[] {group, tenantTmp}, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -1085,8 +1074,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; PaginationHelper helper = createPaginationHelper(); try { - return helper.fetchPage("SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=? AND app_name =?", - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE group_id=? AND tenant_id=? AND app_name =?", + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return helper.fetchPage(configInfoMapper.findConfigInfoByGroupAndAppCountRows(), + configInfoMapper.findConfigInfoByGroupAndAppFetchRows(startRow, pageSize), new Object[] {group, tenantTmp, appName}, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -1102,50 +1093,32 @@ public class ExternalStoragePersistServiceImpl implements PersistService { final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - StringBuilder sqlCount = new StringBuilder( - "SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=? "); - StringBuilder sql = new StringBuilder( - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE group_id=? AND tenant_id=? "); + String sqlCount = null; + String sql = null; + Map params = new HashMap<>(16); + List paramList = new ArrayList<>(); paramList.add(group); paramList.add(tenantTmp); - if (StringUtils.isNotBlank(configTags)) { - sqlCount = new StringBuilder( - "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.group_id=? AND a.tenant_id=? "); - sql = new StringBuilder( - "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " - + "config_tags_relation b ON a.id=b.id WHERE a.group_id=? AND a.tenant_id=? "); - - sqlCount.append(" AND b.tag_name IN ("); - sql.append(" AND b.tag_name IN ("); - String[] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sqlCount.append(", "); - sql.append(", "); - } - sqlCount.append('?'); - sql.append('?'); - paramList.add(tagArr[i]); - } - sqlCount.append(") "); - sql.append(") "); - - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" AND a.app_name=? "); - sql.append(" AND a.app_name=? "); - paramList.add(appName); - } - } else { - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" AND app_name=? "); - sql.append(" AND app_name=? "); - paramList.add(appName); - } + if (StringUtils.isNotBlank(appName)) { + paramList.add(appName); + params.put(APP_NAME, APP_NAME); } - + final int startRow = (pageNo - 1) * pageSize; + if (StringUtils.isNotBlank(configTags)) { + String[] tagArr = configTags.split(","); + paramList.addAll(Arrays.asList(tagArr)); + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + sqlCount = configTagsRelationMapper.findConfigInfoByGroupAndAdvanceCountRows(params, paramList.size()); + sql = configTagsRelationMapper.findConfigInfoByGroupAndAdvanceFetchRows(params, paramList.size(), startRow, pageSize); + } else { + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + sqlCount = configInfoMapper.findConfigInfoByGroupAndAdvanceCountRows(params); + sql = configInfoMapper.findConfigInfoByGroupAndAdvanceFetchRows(params, startRow, pageSize); + } + try { - return helper.fetchPage(sqlCount.toString(), sql.toString(), paramList.toArray(), pageNo, pageSize, + return helper.fetchPage(sqlCount, sql, paramList.toArray(), pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -1159,8 +1132,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; PaginationHelper helper = createPaginationHelper(); try { - return helper.fetchPage("SELECT count(*) FROM config_info WHERE tenant_id LIKE ? AND app_name=?", - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE tenant_id LIKE ? AND app_name=?", + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return helper.fetchPage(configInfoMapper.findConfigInfoByAppCountRows(), + configInfoMapper.findConfigInfoByAppFetchRows(startRow, pageSize), new Object[] {generateLikeArgument(tenantTmp), appName}, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -1175,49 +1150,32 @@ public class ExternalStoragePersistServiceImpl implements PersistService { PaginationHelper helper = createPaginationHelper(); final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - StringBuilder sqlCount = new StringBuilder("SELECT count(*) FROM config_info WHERE tenant_id LIKE ? "); - StringBuilder sql = new StringBuilder( - "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE tenant_id LIKE ? "); + + String sqlCount = null; + String sql = null; + Map paramsMap = new HashMap<>(16); + List paramList = new ArrayList<>(); paramList.add(tenantTmp); + if (StringUtils.isNotBlank(appName)) { + paramList.add(appName); + paramsMap.put(APP_NAME, APP_NAME); + } + final int startRow = (pageNo - 1) * pageSize; if (StringUtils.isNotBlank(configTags)) { - sqlCount = new StringBuilder( - "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.tenant_id=? "); - - sql = new StringBuilder( - "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " - + "config_tags_relation b ON a.id=b.id WHERE a.tenant_id=? "); - - sqlCount.append(" AND b.tag_name IN ("); - sql.append(" AND b.tag_name IN ("); String[] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sqlCount.append(", "); - sql.append(", "); - } - sqlCount.append('?'); - sql.append('?'); - paramList.add(tagArr[i]); - } - sqlCount.append(") "); - sql.append(") "); - - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" AND a.app_name=? "); - sql.append(" AND a.app_name=? "); - paramList.add(appName); - } + paramList.addAll(Arrays.asList(tagArr)); + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + sqlCount = configTagsRelationMapper.findConfigInfoByAdvanceCountRows(paramsMap, paramList.size()); + sql = configTagsRelationMapper.findConfigInfoByAdvanceFetchRows(paramsMap, paramList.size(), startRow, pageSize); } else { - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" AND app_name=? "); - sql.append(" AND app_name=? "); - paramList.add(appName); - } + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + sqlCount = configInfoMapper.findConfigInfoByAdvanceCountRows(paramsMap); + sql = configInfoMapper.findConfigInfoByAdvanceFetchRows(paramsMap, startRow, pageSize); } try { - return helper.fetchPage(sqlCount.toString(), sql.toString(), paramList.toArray(), pageNo, pageSize, + return helper.fetchPage(sqlCount, sql, paramList.toArray(), pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -1230,8 +1188,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { public Page findConfigInfoBaseByGroup(final int pageNo, final int pageSize, final String group) { PaginationHelper helper = createPaginationHelper(); try { - return helper.fetchPage("SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=?", - "SELECT id,data_id,group_id,content FROM config_info WHERE group_id=? AND tenant_id=?", + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return helper.fetchPage(configInfoMapper.findConfigInfoBaseByGroupCountRows(), + configInfoMapper.findConfigInfoBaseByGroupFetchRows(startRow, pageSize), new Object[] {group, StringUtils.EMPTY}, pageNo, pageSize, CONFIG_INFO_BASE_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -1241,7 +1201,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public int configInfoCount() { - String sql = " SELECT count(*) FROM config_info "; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.count(); Integer result = jt.queryForObject(sql, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoCount error"); @@ -1251,7 +1212,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public int configInfoCount(String tenant) { - String sql = " SELECT count(*) FROM config_info WHERE tenant_id LIKE ?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.configInfoLikeTenantCount(); Integer result = jt.queryForObject(sql, new Object[] {tenant}, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoCount error"); @@ -1261,7 +1223,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public int configInfoBetaCount() { - String sql = " SELECT count(*) FROM config_info_beta "; + ConfigInfoBetaMapper configInfoBetaMapper = (ConfigInfoBetaMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_BETA).get(); + String sql = configInfoBetaMapper.count(); Integer result = jt.queryForObject(sql, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoBetaCount error"); @@ -1271,7 +1234,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public int configInfoTagCount() { - String sql = " SELECT count(ID) FROM config_info_tag "; + ConfigInfoTagMapper configInfoTagMapper = (ConfigInfoTagMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_TAG).get(); + String sql = configInfoTagMapper.configInfoTagCount(); Integer result = jt.queryForObject(sql, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoBetaCount error"); @@ -1281,14 +1245,16 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public List getTenantIdList(int page, int pageSize) { - String sql = "SELECT tenant_id FROM config_info WHERE tenant_id != '' GROUP BY tenant_id LIMIT ?, ?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.getTenantIdList(); int from = (page - 1) * pageSize; return jt.queryForList(sql, String.class, from, pageSize); } @Override public List getGroupIdList(int page, int pageSize) { - String sql = "SELECT group_id FROM config_info WHERE tenant_id ='' GROUP BY group_id LIMIT ?, ?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.getGroupIdList(); int from = (page - 1) * pageSize; return jt.queryForList(sql, String.class, from, pageSize); } @@ -1296,7 +1262,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public int aggrConfigInfoCount(String dataId, String group, String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = " SELECT count(*) FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ?"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String sql = configInfoAggrMapper.aggrConfigInfoCount(); Integer result = jt.queryForObject(sql, Integer.class, new Object[] {dataId, group, tenantTmp}); if (result == null) { throw new IllegalArgumentException("aggrConfigInfoCount error"); @@ -1310,24 +1277,11 @@ public class ExternalStoragePersistServiceImpl implements PersistService { return 0; } final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - StringBuilder sql = new StringBuilder( - " SELECT count(*) FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND datum_id"); - if (isIn) { - sql.append(" IN ("); - } else { - sql.append(" NOT IN ("); - } - for (int i = 0, size = datumIds.size(); i < size; i++) { - if (i > 0) { - sql.append(", "); - } - sql.append('?'); - } - sql.append(')'); - + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String sql = configInfoAggrMapper.aggrConfigInfoCount(datumIds.size(), isIn); List objectList = com.alibaba.nacos.common.utils.CollectionUtils.list(dataId, group, tenantTmp); objectList.addAll(datumIds); - Integer result = jt.queryForObject(sql.toString(), Integer.class, objectList.toArray()); + Integer result = jt.queryForObject(sql, Integer.class, objectList.toArray()); if (result == null) { throw new IllegalArgumentException("aggrConfigInfoCount error"); } @@ -1347,10 +1301,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public Page findAllConfigInfo(final int pageNo, final int pageSize, final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sqlCountRows = "SELECT count(*) FROM config_info"; - String sqlFetchRows = " SELECT t.id,data_id,group_id,tenant_id,app_name,content,md5 " - + " FROM ( SELECT id FROM config_info WHERE tenant_id LIKE ? ORDER BY id LIMIT ?,? )" - + " g, config_info t WHERE g.id = t.id "; + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sqlCountRows = configInfoMapper.count(); + String sqlFetchRows = configInfoMapper.findAllConfigInfoFetchRows(startRow, pageSize); PaginationHelper helper = createPaginationHelper(); try { @@ -1366,9 +1320,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public Page findAllConfigKey(final int pageNo, final int pageSize, final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String select = " SELECT data_id,group_id,app_name FROM ( " - + " SELECT id FROM config_info WHERE tenant_id LIKE ? ORDER BY id LIMIT ?, ? )" - + " g, config_info t WHERE g.id = t.id "; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String select = configInfoMapper.findAllConfigKey(); final int totalCount = configInfoCount(tenant); int pageCount = totalCount / pageSize; @@ -1406,10 +1359,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override @Deprecated public Page findAllConfigInfoBase(final int pageNo, final int pageSize) { - String sqlCountRows = "SELECT count(*) FROM config_info"; - String sqlFetchRows = " SELECT t.id,data_id,group_id,content,md5" - + " FROM ( SELECT id FROM config_info ORDER BY id LIMIT ?,? ) " - + " g, config_info t WHERE g.id = t.id "; + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sqlCountRows = configInfoMapper.count(); + String sqlFetchRows = configInfoMapper.findAllConfigInfoBaseFetchRows(startRow, pageSize); PaginationHelper helper = createPaginationHelper(); try { @@ -1423,10 +1376,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public Page findAllConfigInfoForDumpAll(final int pageNo, final int pageSize) { - String sqlCountRows = "SELECT count(*) FROM config_info"; - String sqlFetchRows = " SELECT t.id,type,data_id,group_id,tenant_id,app_name,content,type,md5,gmt_modified " - + " FROM ( SELECT id FROM config_info ORDER BY id LIMIT ?,? )" - + " g, config_info t WHERE g.id = t.id "; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final int startRow = (pageNo - 1) * pageSize; + String sqlCountRows = configInfoMapper.count(); + String sqlFetchRows = configInfoMapper.findAllConfigInfoForDumpAllFetchRows(startRow, pageSize); PaginationHelper helper = createPaginationHelper(); try { @@ -1440,7 +1393,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public Page findAllConfigInfoFragment(final long lastMaxId, final int pageSize) { - String select = "SELECT id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified,type,encrypted_data_key FROM config_info WHERE id > ? ORDER BY id ASC LIMIT ?,?"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String select = configInfoMapper.findAllConfigInfoFragment(); PaginationHelper helper = createPaginationHelper(); try { return helper.fetchPageLimit(select, new Object[] {lastMaxId, 0, pageSize}, 1, pageSize, @@ -1453,11 +1407,11 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public Page findAllConfigInfoBetaForDumpAll(final int pageNo, final int pageSize) { - String sqlCountRows = "SELECT count(*) FROM config_info_beta"; + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoBetaMapper configInfoBetaMapper = (ConfigInfoBetaMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_BETA).get(); + String sqlCountRows = configInfoBetaMapper.count(); String sqlFetchRows = - " SELECT t.id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified,beta_ips,encrypted_data_key " - + " FROM ( SELECT id FROM config_info_beta ORDER BY id LIMIT ?,? )" - + " g, config_info_beta t WHERE g.id = t.id "; + configInfoBetaMapper.findAllConfigInfoBetaForDumpAllFetchRows(startRow, pageSize); PaginationHelper helper = createPaginationHelper(); try { return helper.fetchPageLimit(sqlCountRows, sqlFetchRows, new Object[] {(pageNo - 1) * pageSize, pageSize}, @@ -1471,10 +1425,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public Page findAllConfigInfoTagForDumpAll(final int pageNo, final int pageSize) { - String sqlCountRows = "SELECT count(*) FROM config_info_tag"; - String sqlFetchRows = " SELECT t.id,data_id,group_id,tenant_id,tag_id,app_name,content,md5,gmt_modified " - + " FROM ( SELECT id FROM config_info_tag ORDER BY id LIMIT ?,? ) " - + "g, config_info_tag t WHERE g.id = t.id "; + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoTagMapper configInfoTagMapper = (ConfigInfoTagMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_TAG).get(); + String sqlCountRows = configInfoTagMapper.count(); + String sqlFetchRows = configInfoTagMapper.findAllConfigInfoTagForDumpAllFetchRows(startRow, pageSize); PaginationHelper helper = createPaginationHelper(); try { return helper.fetchPageLimit(sqlCountRows, sqlFetchRows, new Object[] {(pageNo - 1) * pageSize, pageSize}, @@ -1495,37 +1449,27 @@ public class ExternalStoragePersistServiceImpl implements PersistService { if (CollectionUtils.isEmpty(dataIds)) { return Collections.emptyList(); } - + // Batch query limit // The number of in is controlled within 100, the shorter the length of the SQL statement, the better if (subQueryLimit > QUERY_LIMIT_SIZE) { subQueryLimit = 50; } List result = new ArrayList<>(dataIds.size()); - - String sqlStart = "SELECT data_id, group_id, tenant_id, app_name, content FROM config_info WHERE group_id = ? AND tenant_id = ? AND data_id IN ("; - String sqlEnd = ")"; - StringBuilder subQuerySql = new StringBuilder(); - + + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); for (int i = 0; i < dataIds.size(); i += subQueryLimit) { // dataids List params = new ArrayList<>( dataIds.subList(i, i + subQueryLimit < dataIds.size() ? i + subQueryLimit : dataIds.size())); - - for (int j = 0; j < params.size(); j++) { - subQuerySql.append('?'); - if (j != params.size() - 1) { - subQuerySql.append(','); - } - } - + // group params.add(0, group); params.add(1, tenantTmp); - + List r = this.jt - .query(sqlStart + subQuerySql.toString() + sqlEnd, params.toArray(), CONFIG_INFO_ROW_MAPPER); - + .query(configInfoMapper.findConfigInfoByBatch(params.size()), params.toArray(), CONFIG_INFO_ROW_MAPPER); + // assert not null if (r != null && r.size() > 0) { result.addAll(r); @@ -1538,6 +1482,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { public Page findConfigInfoLike(final int pageNo, final int pageSize, final String dataId, final String group, final String tenant, final String appName, final String content) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + Map paramsMap = new HashMap<>(16); + List params = new ArrayList<>(); if (StringUtils.isBlank(dataId) && StringUtils.isBlank(group)) { if (StringUtils.isBlank(appName)) { return this.findAllConfigInfo(pageNo, pageSize, tenantTmp); @@ -1545,37 +1491,34 @@ public class ExternalStoragePersistServiceImpl implements PersistService { return this.findConfigInfoByApp(pageNo, pageSize, tenantTmp, appName); } } - - PaginationHelper helper = createPaginationHelper(); - - String sqlCountRows = "SELECT count(*) FROM config_info WHERE "; - String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE "; - String where = " 1=1 "; - List params = new ArrayList<>(); - if (!StringUtils.isBlank(dataId)) { - where += " AND data_id LIKE ? "; + paramsMap.put(DATA_ID, DATA_ID); params.add(generateLikeArgument(dataId)); } if (!StringUtils.isBlank(group)) { - where += " AND group_id LIKE ? "; + paramsMap.put(GROUP, GROUP); params.add(generateLikeArgument(group)); } - - where += " AND tenant_id LIKE ? "; + params.add(generateLikeArgument(tenantTmp)); - + if (!StringUtils.isBlank(appName)) { - where += " AND app_name = ? "; + paramsMap.put(APP_NAME, APP_NAME); params.add(appName); } if (!StringUtils.isBlank(content)) { - where += " AND content LIKE ? "; + paramsMap.put(CONTENT, CONTENT); params.add(generateLikeArgument(content)); } + PaginationHelper helper = createPaginationHelper(); + + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final int startRow = (pageNo - 1) * pageSize; + String sqlCountRows = configInfoMapper.findConfigInfoLikeCountRows(paramsMap); + String sqlFetchRows = configInfoMapper.findConfigInfoLikeFetchRows(paramsMap, startRow, pageSize); try { - return helper.fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, + return helper.fetchPage(sqlCountRows, sqlFetchRows, params.toArray(), pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -1690,67 +1633,45 @@ public class ExternalStoragePersistServiceImpl implements PersistService { final String content = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("content"); final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); PaginationHelper helper = createPaginationHelper(); - String sqlCountRows = "SELECT count(*) FROM config_info"; - String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,app_name,content,encrypted_data_key FROM config_info"; - StringBuilder where = new StringBuilder(" WHERE "); + String sqlCountRows = null; + String sqlFetchRows = null; + Map paramsMap = new HashMap<>(16); + List params = new ArrayList<>(); params.add(generateLikeArgument(tenantTmp)); - if (StringUtils.isNotBlank(configTags)) { - sqlCountRows = "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id "; - sqlFetchRows = "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id "; - - where.append(" a.tenant_id LIKE ? "); - if (!StringUtils.isBlank(dataId)) { - where.append(" AND a.data_id LIKE ? "); - params.add(generateLikeArgument(dataId)); - } - if (!StringUtils.isBlank(group)) { - where.append(" AND a.group_id LIKE ? "); - params.add(generateLikeArgument(group)); - } - if (!StringUtils.isBlank(appName)) { - where.append(" AND a.app_name = ? "); - params.add(appName); - } - if (!StringUtils.isBlank(content)) { - where.append(" AND a.content LIKE ? "); - params.add(generateLikeArgument(content)); - } - - where.append(" AND b.tag_name IN ("); - String[] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - where.append(", "); - } - where.append('?'); - params.add(tagArr[i]); - } - where.append(") "); - } else { - where.append(" tenant_id LIKE ? "); - if (!StringUtils.isBlank(dataId)) { - where.append(" AND data_id LIKE ? "); - params.add(generateLikeArgument(dataId)); - } - if (!StringUtils.isBlank(group)) { - where.append(" AND group_id LIKE ? "); - params.add(generateLikeArgument(group)); - } - if (!StringUtils.isBlank(appName)) { - where.append(" AND app_name = ? "); - params.add(appName); - } - if (!StringUtils.isBlank(content)) { - where.append(" AND content LIKE ? "); - params.add(generateLikeArgument(content)); - } + if (!StringUtils.isBlank(dataId)) { + params.add(generateLikeArgument(dataId)); + paramsMap.put(DATA_ID, DATA_ID); + } + if (!StringUtils.isBlank(group)) { + params.add(generateLikeArgument(group)); + paramsMap.put(GROUP, GROUP); + } + if (!StringUtils.isBlank(appName)) { + params.add(appName); + paramsMap.put(APP_NAME, APP_NAME); + } + if (!StringUtils.isBlank(content)) { + params.add(generateLikeArgument(content)); + paramsMap.put(CONTENT, CONTENT); + } + final int startRow = (pageNo - 1) * pageSize; + if (StringUtils.isNotBlank(configTags)) { + String[] tagArr = configTags.split(","); + params.addAll(Arrays.asList(tagArr)); + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + sqlCountRows = configTagsRelationMapper.findConfigInfoLike4PageCountRows(paramsMap, params.size()); + sqlFetchRows = configTagsRelationMapper.findConfigInfoLike4PageFetchRows(paramsMap, params.size(), startRow, pageSize); + } else { + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + sqlCountRows = configInfoMapper.findConfigInfoLike4PageCountRows(paramsMap); + sqlFetchRows = configInfoMapper.findConfigInfoLike4PageFetchRows(paramsMap, startRow, pageSize); } - - try { - Page page = helper.fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), - pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); + try { + Page page = helper.fetchPage(sqlCountRows, sqlFetchRows, params.toArray(), + pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); + for (ConfigInfo configInfo : page.getPageItems()) { Pair pair = EncryptionHandler.decryptHandler(configInfo.getDataId(), configInfo.getEncryptedDataKey(), configInfo.getContent()); @@ -1769,29 +1690,31 @@ public class ExternalStoragePersistServiceImpl implements PersistService { if (StringUtils.isBlank(dataId) && StringUtils.isBlank(group)) { throw new IOException("invalid param"); } - + PaginationHelper helper = createPaginationHelper(); - - String sqlCountRows = "SELECT count(*) FROM config_info WHERE "; - String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,content FROM config_info WHERE "; - String where = " 1=1 AND tenant_id='' "; + Map paramsMap = new HashMap<>(16); List params = new ArrayList<>(); - + if (!StringUtils.isBlank(dataId)) { - where += " AND data_id LIKE ? "; params.add(generateLikeArgument(dataId)); + paramsMap.put(DATA_ID, DATA_ID); } if (!StringUtils.isBlank(group)) { - where += " AND group_id LIKE "; params.add(generateLikeArgument(group)); + paramsMap.put(GROUP, GROUP); } if (!StringUtils.isBlank(content)) { - where += " AND content LIKE ? "; params.add(generateLikeArgument(content)); + paramsMap.put(CONTENT, CONTENT); } - + + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sqlCountRows = configInfoMapper.findConfigInfoBaseLikeCountRows(paramsMap); + String sqlFetchRows = configInfoMapper.findConfigInfoBaseLikeFetchRows(paramsMap, startRow, pageSize); + try { - return helper.fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, + return helper.fetchPage(sqlCountRows, sqlFetchRows, params.toArray(), pageNo, pageSize, CONFIG_INFO_BASE_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -1802,7 +1725,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public ConfigInfoAggr findSingleConfigInfoAggr(String dataId, String group, String tenant, String datumId) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = "SELECT id,data_id,group_id,tenant_id,datum_id,app_name,content FROM config_info_aggr WHERE data_id=? AND group_id=? AND tenant_id=? AND datum_id=?"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String sql = configInfoAggrMapper.select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "datum_id", "app_name", "content"), + Arrays.asList("data_id", "group_id", "tenant_id", "datum_id")); try { return this.jt @@ -1822,7 +1747,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public List findConfigInfoAggr(String dataId, String group, String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = "SELECT data_id,group_id,tenant_id,datum_id,app_name,content FROM config_info_aggr WHERE data_id=? AND group_id=? AND tenant_id=? ORDER BY datum_id"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String sql = configInfoAggrMapper.findConfigInfoAggr(); try { return this.jt.query(sql, new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_AGGR_ROW_MAPPER); @@ -1841,16 +1767,16 @@ public class ExternalStoragePersistServiceImpl implements PersistService { public Page findConfigInfoAggrByPage(String dataId, String group, String tenant, final int pageNo, final int pageSize) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sqlCountRows = "SELECT count(*) FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ?"; - String sqlFetchRows = - "SELECT data_id,group_id,tenant_id,datum_id,app_name,content FROM config_info_aggr WHERE data_id=? AND " - + "group_id=? AND tenant_id=? ORDER BY datum_id LIMIT ?,?"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + final int startRow = (pageNo - 1) * pageSize; + String sqlCountRows = configInfoAggrMapper.select(Arrays.asList("count(*)"), Arrays.asList("data_id", "group_id", "tenant_id")); + String sqlFetchRows = configInfoAggrMapper.findConfigInfoAggrByPageFetchRows(startRow, pageSize); PaginationHelper helper = createPaginationHelper(); try { return helper.fetchPageLimit(sqlCountRows, new Object[] {dataId, group, tenantTmp}, sqlFetchRows, new Object[] {dataId, group, tenantTmp, (pageNo - 1) * pageSize, pageSize}, pageNo, pageSize, CONFIG_INFO_AGGR_ROW_MAPPER); - + } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); throw e; @@ -1960,7 +1886,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public List findAllAggrGroup() { - String sql = "SELECT DISTINCT data_id, group_id, tenant_id FROM config_info_aggr"; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String sql = configInfoAggrMapper.findAllAggrGroup(); try { return jt.query(sql, new Object[] {}, CONFIG_INFO_CHANGED_ROW_MAPPER); @@ -1977,7 +1904,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public List findDatumIdByContent(String dataId, String groupId, String content) { - String sql = "SELECT datum_id FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND content = ? "; + ConfigInfoAggrMapper configInfoAggrMapper = (ConfigInfoAggrMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_AGGR).get(); + String sql = configInfoAggrMapper.select( + Collections.singletonList("datum_id"), Arrays.asList("data_id", "group_id", "content")); try { return this.jt.queryForList(sql, new Object[] {dataId, groupId, content}, String.class); @@ -1994,8 +1923,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public List findChangeConfig(final Timestamp startTime, final Timestamp endTime) { try { - List> list = jt.queryForList( - "SELECT data_id, group_id, tenant_id, app_name, content, gmt_modified,encrypted_data_key FROM config_info WHERE gmt_modified >=? AND gmt_modified <= ?", + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + List> list = jt.queryForList(configInfoMapper.findChangeConfig(), new Object[] {startTime, endTime}); return convertChangeConfig(list); } catch (DataAccessException e) { @@ -2009,41 +1938,40 @@ public class ExternalStoragePersistServiceImpl implements PersistService { final String appName, final Timestamp startTime, final Timestamp endTime, final int pageNo, final int pageSize, final long lastMaxId) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sqlCountRows = "SELECT count(*) FROM config_info WHERE "; - String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,app_name,content,type,md5,gmt_modified FROM config_info WHERE "; - String where = " 1=1 "; + Map paramsMap = new HashMap<>(16); List params = new ArrayList<>(); - if (!StringUtils.isBlank(dataId)) { - where += " AND data_id LIKE ? "; params.add(generateLikeArgument(dataId)); + paramsMap.put(DATA_ID, DATA_ID); } if (!StringUtils.isBlank(group)) { - where += " AND group_id LIKE ? "; params.add(generateLikeArgument(group)); + paramsMap.put(GROUP, GROUP); } - + if (!StringUtils.isBlank(tenantTmp)) { - where += " AND tenant_id = ? "; params.add(tenantTmp); + paramsMap.put(TENANT, TENANT); } - + if (!StringUtils.isBlank(appName)) { - where += " AND app_name = ? "; params.add(appName); + paramsMap.put(APP_NAME, APP_NAME); } if (startTime != null) { - where += " AND gmt_modified >=? "; params.add(startTime); } if (endTime != null) { - where += " AND gmt_modified <=? "; params.add(endTime); } - + final int startRow = (pageNo - 1) * pageSize; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sqlCountRows = configInfoMapper.findChangeConfigCountRows(paramsMap, startTime, endTime); + String sqlFetchRows = configInfoMapper.findChangeConfigFetchRows(paramsMap, startTime, endTime, startRow, pageSize, lastMaxId); + PaginationHelper helper = createPaginationHelper(); try { - return helper.fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, + return helper.fetchPage(sqlCountRows, sqlFetchRows, params.toArray(), pageNo, pageSize, lastMaxId, CONFIG_INFO_WRAPPER_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -2054,8 +1982,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public List findDeletedConfig(final Timestamp startTime, final Timestamp endTime) { try { - List> list = jt.queryForList( - "SELECT DISTINCT data_id, group_id, tenant_id FROM his_config_info WHERE op_type = 'D' AND gmt_modified >=? AND gmt_modified <= ?", + HistoryConfigInfoMapper historyConfigInfoMapper = (HistoryConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.HIS_CONFIG_INFO).get(); + List> list = jt.queryForList(historyConfigInfoMapper.findDeletedConfig(), new Object[] {startTime, endTime}); return convertDeletedConfig(list); } catch (DataAccessException e) { @@ -2083,10 +2011,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { final String md5Tmp = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); KeyHolder keyHolder = new GeneratedKeyHolder(); - - final String sql = - "INSERT INTO config_info(data_id,group_id,tenant_id,app_name,content,md5,src_ip,src_user,gmt_create," - + "gmt_modified,c_desc,c_use,effect,type,c_schema,encrypted_data_key) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + final String sql = configInfoMapper.insert(Arrays.asList("data_id", "group_id", "tenant_id", "app_name", "content", "md5", "src_ip", "src_user", "gmt_create", + "gmt_modified", "c_desc", "c_use", "effect", "type", "c_schema", "encrypted_data_key")); try { jt.update(new PreparedStatementCreator() { @@ -2136,8 +2064,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public void addConfigTagRelationAtomic(long configId, String tagName, String dataId, String group, String tenant) { try { - jt.update( - "INSERT INTO config_tags_relation(id,tag_name,tag_type,data_id,group_id,tenant_id) VALUES(?,?,?,?,?,?)", + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + jt.update(configTagsRelationMapper.addConfigTagRelationAtomic(), configId, tagName, StringUtils.EMPTY, dataId, group, tenant); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -2148,7 +2076,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public void removeTagByIdAtomic(long id) { try { - jt.update("DELETE FROM config_tags_relation WHERE id=?", id); + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + jt.update(configTagsRelationMapper.removeTagByIdAtomic(), id); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); throw e; @@ -2157,7 +2086,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public List getConfigTagsByTenant(String tenant) { - String sql = "SELECT tag_name FROM config_tags_relation WHERE tenant_id = ? "; + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + String sql = configTagsRelationMapper.getConfigTagsByTenant(); try { return jt.queryForList(sql, new Object[] {tenant}, String.class); } catch (EmptyResultDataAccessException e) { @@ -2172,7 +2102,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public List selectTagByConfig(String dataId, String group, String tenant) { - String sql = "SELECT tag_name FROM config_tags_relation WHERE data_id=? AND group_id=? AND tenant_id = ? "; + ConfigTagsRelationMapper configTagsRelationMapper = (ConfigTagsRelationMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_TAGS_RELATION).get(); + String sql = configTagsRelationMapper.selectTagByConfig(); try { return jt.queryForList(sql, new Object[] {dataId, group, tenant}, String.class); } catch (EmptyResultDataAccessException e) { @@ -2190,7 +2121,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { final String srcUser) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { - jt.update("DELETE FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?", dataId, group, + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + jt.update(configInfoMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id")), dataId, group, tenantTmp); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -2203,20 +2135,15 @@ public class ExternalStoragePersistServiceImpl implements PersistService { if (StringUtils.isBlank(ids)) { return; } - StringBuilder sql = new StringBuilder(SQL_DELETE_CONFIG_INFO_BY_IDS); - sql.append("id IN ("); List paramList = new ArrayList<>(); String[] tagArr = ids.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sql.append(", "); - } - sql.append('?'); - paramList.add(Long.parseLong(tagArr[i])); + for (String s : tagArr) { + paramList.add(Long.parseLong(s)); } - sql.append(") "); + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.removeConfigInfoByIdsAtomic(paramList.size()); try { - jt.update(sql.toString(), paramList.toArray()); + jt.update(sql, paramList.toArray()); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); throw e; @@ -2229,7 +2156,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag; try { - jt.update("DELETE FROM config_info_tag WHERE data_id=? AND group_id=? AND tenant_id=? AND tag_id=?", dataId, + ConfigInfoTagMapper configInfoTagMapper = (ConfigInfoTagMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO_TAG).get(); + jt.update(configInfoTagMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id", "tag_id")), dataId, group, tenantTmp, tagTmp); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); @@ -2252,9 +2180,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { configInfo.getEncryptedDataKey() == null ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); try { - jt.update("UPDATE config_info SET content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?," - + "app_name=?,c_desc=?,c_use=?,effect=?,type=?,c_schema=?,encrypted_data_key=? " - + " WHERE data_id=? AND group_id=? AND tenant_id=?", configInfo.getContent(), md5Tmp, srcIp, srcUser, + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + jt.update(configInfoMapper.update(Arrays.asList("content", "md5", "src_ip", "src_user", "gmt_modified", "app_name", "c_desc", "c_use", "effect", "type", + "c_schema", "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id")), configInfo.getContent(), md5Tmp, srcIp, srcUser, time, appNameTmp, desc, use, effect, type, schema, encryptedDataKey, configInfo.getDataId(), configInfo.getGroup(), tenantTmp); } catch (CannotGetJdbcConnectionException e) { @@ -2275,9 +2203,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { String schema = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("schema"); try { - return jt.update("UPDATE config_info SET content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?," - + "app_name=?,c_desc=?,c_use=?,effect=?,type=?,c_schema=? " - + "WHERE data_id=? AND group_id=? AND tenant_id=? AND (md5=? OR md5 IS NULL OR md5='')", + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return jt.update(configInfoMapper.update(Arrays.asList("content", "md5", "src_ip", "src_user", "gmt_modified", "app_name", "c_desc", "c_use", "effect", "type", + "c_schema", "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id")), configInfo.getContent(), md5Tmp, srcIp, srcUser, time, appNameTmp, desc, use, effect, type, schema, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, configInfo.getMd5()); } catch (CannotGetJdbcConnectionException e) { @@ -2291,20 +2219,14 @@ public class ExternalStoragePersistServiceImpl implements PersistService { if (StringUtils.isBlank(ids)) { return null; } - StringBuilder sql = new StringBuilder(SQL_FIND_CONFIG_INFO_BY_IDS); - sql.append("id IN ("); List paramList = new ArrayList<>(); String[] tagArr = ids.split(","); for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sql.append(", "); - } - sql.append('?'); paramList.add(Long.parseLong(tagArr[i])); } - sql.append(") "); try { - return this.jt.query(sql.toString(), paramList.toArray(), CONFIG_INFO_ROW_MAPPER); + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return this.jt.query(configInfoMapper.findConfigInfosByIds(tagArr.length), paramList.toArray(), CONFIG_INFO_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null return null; } catch (CannotGetJdbcConnectionException e) { @@ -2318,8 +2240,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { List configTagList = this.selectTagByConfig(dataId, group, tenant); - ConfigAdvanceInfo configAdvance = this.jt.queryForObject( - "SELECT gmt_create,gmt_modified,src_user,src_ip,c_desc,c_use,effect,type,c_schema FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?", + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + ConfigAdvanceInfo configAdvance = this.jt.queryForObject(configInfoMapper.select(Arrays.asList("gmt_create", "gmt_modified", "src_user", "src_ip", "c_desc", "c_use", "effect", "type", "c_schema"), + Arrays.asList("data_id", "group_id", "tenant_id")), new Object[] {dataId, group, tenantTmp}, CONFIG_ADVANCE_INFO_ROW_MAPPER); if (configTagList != null && !configTagList.isEmpty()) { StringBuilder configTagsTmp = new StringBuilder(); @@ -2346,10 +2269,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { List configTagList = this.selectTagByConfig(dataId, group, tenant); - ConfigAllInfo configAdvance = this.jt.queryForObject( - "SELECT id,data_id,group_id,tenant_id,app_name,content,md5," - + "gmt_create,gmt_modified,src_user,src_ip,c_desc,c_use,effect,type,c_schema,encrypted_data_key FROM config_info " - + "WHERE data_id=? AND group_id=? AND tenant_id=?", new Object[] {dataId, group, tenantTmp}, + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + ConfigAllInfo configAdvance = this.jt.queryForObject(configInfoMapper.select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", + "gmt_create", "gmt_modified", "src_user", "src_ip", "c_desc", "c_use", "effect", "type", "c_schema", "encrypted_data_key"), + Arrays.asList("data_id", "group_id", "tenant_id")), new Object[] {dataId, group, tenantTmp}, CONFIG_ALL_INFO_ROW_MAPPER); if (configTagList != null && !configTagList.isEmpty()) { StringBuilder configTagsTmp = new StringBuilder(); @@ -2381,11 +2304,12 @@ public class ExternalStoragePersistServiceImpl implements PersistService { : configInfo.getEncryptedDataKey(); try { + HistoryConfigInfoMapper historyConfigInfoMapper = (HistoryConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.HIS_CONFIG_INFO).get(); jt.update( - "INSERT INTO his_config_info (id,data_id,group_id,tenant_id,app_name,content,md5,src_ip,src_user,gmt_modified,op_type,encrypted_data_key) " - + "VALUES(?,?,?,?,?,?,?,?,?,?,?,?)", id, configInfo.getDataId(), configInfo.getGroup(), - tenantTmp, appNameTmp, configInfo.getContent(), md5Tmp, srcIp, srcUser, time, ops, - encryptedDataKey); + historyConfigInfoMapper.insert(Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", + "src_ip", "src_user", "gmt_modified", "op_type", "encrypted_data_key")), + id, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, + configInfo.getContent(), md5Tmp, srcIp, srcUser, time, ops, encryptedDataKey); } catch (DataAccessException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); throw e; @@ -2397,10 +2321,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { int pageSize) { PaginationHelper helper = createPaginationHelper(); String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sqlCountRows = "SELECT count(*) FROM his_config_info WHERE data_id = ? AND group_id = ? AND tenant_id = ?"; - String sqlFetchRows = - "SELECT nid,data_id,group_id,tenant_id,app_name,src_ip,src_user,op_type,gmt_create,gmt_modified FROM his_config_info " - + "WHERE data_id = ? AND group_id = ? AND tenant_id = ? ORDER BY nid DESC"; + final int startRow = (pageNo - 1) * pageSize; + HistoryConfigInfoMapper historyConfigInfoMapper = (HistoryConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.HIS_CONFIG_INFO).get(); + String sqlCountRows = historyConfigInfoMapper.findConfigHistoryCountRows(); + String sqlFetchRows = historyConfigInfoMapper.findConfigHistoryFetchRows(); Page page = null; try { @@ -2445,7 +2369,10 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public ConfigHistoryInfo detailConfigHistory(Long nid) { - String sqlFetchRows = "SELECT nid,data_id,group_id,tenant_id,app_name,content,md5,src_user,src_ip,op_type,gmt_create,gmt_modified,encrypted_data_key FROM his_config_info WHERE nid = ?"; + HistoryConfigInfoMapper historyConfigInfoMapper = (HistoryConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.HIS_CONFIG_INFO).get(); + String sqlFetchRows = historyConfigInfoMapper.select(Arrays.asList("nid", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", + "src_user", "src_ip", "op_type", "gmt_create", "gmt_modified", "encrypted_data_key"), + Collections.singletonList("nid")); try { ConfigHistoryInfo historyInfo = jt .queryForObject(sqlFetchRows, new Object[] {nid}, HISTORY_DETAIL_ROW_MAPPER); @@ -2458,7 +2385,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public ConfigHistoryInfo detailPreviousConfigHistory(Long id) { - String sqlFetchRows = "SELECT nid,data_id,group_id,tenant_id,app_name,content,md5,src_user,src_ip,op_type,gmt_create,gmt_modified FROM his_config_info WHERE nid = (SELECT max(nid) FROM his_config_info WHERE id = ?) "; + HistoryConfigInfoMapper historyConfigInfoMapper = (HistoryConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.HIS_CONFIG_INFO).get(); + String sqlFetchRows = historyConfigInfoMapper.detailPreviousConfigHistory(); try { ConfigHistoryInfo historyInfo = jt .queryForObject(sqlFetchRows, new Object[] {id}, HISTORY_DETAIL_ROW_MAPPER); @@ -2587,10 +2515,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { @Override public List listGroupKeyMd5ByPage(int pageNo, int pageSize) { - String sqlCountRows = " SELECT count(*) FROM config_info "; - String sqlFetchRows = - " SELECT t.id,data_id,group_id,tenant_id,app_name,md5,type,gmt_modified,encrypted_data_key FROM " - + "( SELECT id FROM config_info ORDER BY id LIMIT ?,? ) g, config_info t WHERE g.id = t.id"; + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sqlCountRows = configInfoMapper.count(); + String sqlFetchRows = configInfoMapper.listGroupKeyMd5ByPageFetchRows(); PaginationHelper helper = createPaginationHelper(); try { Page page = helper @@ -2619,9 +2546,9 @@ public class ExternalStoragePersistServiceImpl implements PersistService { public ConfigInfoWrapper queryConfigInfo(final String dataId, final String group, final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { - return this.jt.queryForObject( - "SELECT id,data_id,group_id,tenant_id,app_name,content,type,gmt_modified,md5,encrypted_data_key FROM config_info " - + "WHERE data_id=? AND group_id=? AND tenant_id=?", new Object[] {dataId, group, tenantTmp}, + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return this.jt.queryForObject(configInfoMapper.select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "type", "gmt_modified", "md5", "encrypted_data_key"), + Arrays.asList("data_id", "group_id", "tenant_id")), new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { return null; @@ -2689,36 +2616,29 @@ public class ExternalStoragePersistServiceImpl implements PersistService { public List findAllConfigInfo4Export(final String dataId, final String group, final String tenant, final String appName, final List ids) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - StringBuilder where = new StringBuilder(" WHERE "); + Map params = new HashMap<>(16); List paramList = new ArrayList<>(); if (!CollectionUtils.isEmpty(ids)) { - where.append(" id IN ("); - for (int i = 0; i < ids.size(); i++) { - if (i != 0) { - where.append(", "); - } - where.append('?'); - paramList.add(ids.get(i)); - } - where.append(") "); + paramList.addAll(ids); } else { - where.append(" tenant_id=? "); paramList.add(tenantTmp); if (!StringUtils.isBlank(dataId)) { - where.append(" AND data_id LIKE ? "); paramList.add(generateLikeArgument(dataId)); + params.put(DATA_ID, DATA_ID); } if (StringUtils.isNotBlank(group)) { - where.append(" AND group_id=? "); paramList.add(group); + params.put(GROUP, GROUP); } if (StringUtils.isNotBlank(appName)) { - where.append(" AND app_name=? "); paramList.add(appName); + params.put(APP_NAME, APP_NAME); } } + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + String sql = configInfoMapper.findAllConfigInfo4Export(ids, params); try { - return this.jt.query(SQL_FIND_ALL_CONFIG_INFO + where, paramList.toArray(), CONFIG_ALL_INFO_ROW_MAPPER); + return this.jt.query(sql, paramList.toArray(), CONFIG_ALL_INFO_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); throw e; @@ -2831,8 +2751,8 @@ public class ExternalStoragePersistServiceImpl implements PersistService { } String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { - return this.jt.query( - "SELECT data_id,group_id,tenant_id,app_name,type FROM config_info WHERE tenant_id=?", + ConfigInfoMapper configInfoMapper = (ConfigInfoMapper) mapperManager.findMapper(dataSource, TableConstant.CONFIG_INFO).get(); + return this.jt.query(configInfoMapper.select(Arrays.asList("data_id", "group_id", "tenant_id", "app_name", "type"), Collections.singletonList("tenant_id")), new Object[]{tenantTmp}, CONFIG_INFO_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/ConfigExecutor.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/ConfigExecutor.java index 3468c1cff..1418725b5 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/ConfigExecutor.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/ConfigExecutor.java @@ -89,7 +89,7 @@ public final class ConfigExecutor { return ((ScheduledThreadPoolExecutor) ASYNC_NOTIFY_EXECUTOR).getQueue().size(); } - public static int asyncCofigChangeClientNotifyQueueSize() { + public static int asyncConfigChangeClientNotifyQueueSize() { return ((ScheduledThreadPoolExecutor) ASYNC_CONFIG_CHANGE_NOTIFY_EXECUTOR).getQueue().size(); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/ParamUtils.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/ParamUtils.java index 5889ab256..5d4f633d7 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/ParamUtils.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/ParamUtils.java @@ -19,7 +19,10 @@ package com.alibaba.nacos.config.server.utils; import java.util.Map; import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; +import org.springframework.http.HttpStatus; /** * Parameter validity check util. @@ -76,25 +79,29 @@ public class ParamUtils { } /** - * Check the parameter. + * Check the parameter for [v1] and [v2]. */ public static void checkParam(String dataId, String group, String datumId, String content) throws NacosException { if (StringUtils.isBlank(dataId) || !isValid(dataId.trim())) { - throw new NacosException(NacosException.INVALID_PARAM, "invalid dataId : " + dataId); + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "invalid dataId : " + dataId); } else if (StringUtils.isBlank(group) || !isValid(group)) { - throw new NacosException(NacosException.INVALID_PARAM, "invalid group : " + group); + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "invalid group : " + group); } else if (StringUtils.isBlank(datumId) || !isValid(datumId)) { - throw new NacosException(NacosException.INVALID_PARAM, "invalid datumId : " + datumId); + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "invalid datumId : " + datumId); } else if (StringUtils.isBlank(content)) { - throw new NacosException(NacosException.INVALID_PARAM, "content is blank : " + content); + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "content is blank : " + content); } else if (content.length() > PropertyUtil.getMaxContent()) { - throw new NacosException(NacosException.INVALID_PARAM, + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "invalid content, over " + PropertyUtil.getMaxContent()); } } /** - * Check the tag. + * Check the tag for [v1]. */ public static void checkParam(String tag) { if (StringUtils.isNotBlank(tag)) { @@ -108,7 +115,7 @@ public class ParamUtils { } /** - * Check the config info. + * Check the config info for [v1] and [v2]. */ public static void checkParam(Map configAdvanceInfo) throws NacosException { for (Map.Entry configAdvanceInfoTmp : configAdvanceInfo.entrySet()) { @@ -116,49 +123,73 @@ public class ParamUtils { if (configAdvanceInfoTmp.getValue() != null) { String[] tagArr = ((String) configAdvanceInfoTmp.getValue()).split(","); if (tagArr.length > 5) { - throw new NacosException(NacosException.INVALID_PARAM, "too much config_tags, over 5"); + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "too much config_tags, over 5"); } for (String tag : tagArr) { if (tag.length() > 64) { - throw new NacosException(NacosException.INVALID_PARAM, "too long tag, over 64"); + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), + ErrorCode.PARAMETER_VALIDATE_ERROR, "too long tag, over 64"); } } } } else if (DESC.equals(configAdvanceInfoTmp.getKey())) { if (configAdvanceInfoTmp.getValue() != null && ((String) configAdvanceInfoTmp.getValue()).length() > 128) { - throw new NacosException(NacosException.INVALID_PARAM, "too long desc, over 128"); + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "too long desc, over 128"); } } else if (USE.equals(configAdvanceInfoTmp.getKey())) { if (configAdvanceInfoTmp.getValue() != null && ((String) configAdvanceInfoTmp.getValue()).length() > 32) { - throw new NacosException(NacosException.INVALID_PARAM, "too long use, over 32"); + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "too long use, over 32"); } } else if (EFFECT.equals(configAdvanceInfoTmp.getKey())) { if (configAdvanceInfoTmp.getValue() != null && ((String) configAdvanceInfoTmp.getValue()).length() > 32) { - throw new NacosException(NacosException.INVALID_PARAM, "too long effect, over 32"); + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "too long effect, over 32"); } } else if (TYPE.equals(configAdvanceInfoTmp.getKey())) { if (configAdvanceInfoTmp.getValue() != null && ((String) configAdvanceInfoTmp.getValue()).length() > 32) { - throw new NacosException(NacosException.INVALID_PARAM, "too long type, over 32"); + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "too long type, over 32"); } } else if (SCHEMA.equals(configAdvanceInfoTmp.getKey())) { if (configAdvanceInfoTmp.getValue() != null && ((String) configAdvanceInfoTmp.getValue()).length() > 32768) { - throw new NacosException(NacosException.INVALID_PARAM, "too long schema, over 32768"); + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "too long schema, over 32768"); } } else if (ENCRYPTED_DATA_KEY.equals(configAdvanceInfoTmp.getKey())) { // No verification required } else { - throw new NacosException(NacosException.INVALID_PARAM, "invalid param"); + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "invalid param"); } } } /** - * Check the tenant. + * Check the tag for [v2]. + */ + public static void checkParamV2(String tag) throws NacosApiException { + if (StringUtils.isNotBlank(tag)) { + if (!isValid(tag.trim())) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "invalid tag : " + tag); + } + if (tag.length() > TAG_MAX_LEN) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "too long tag, over 16"); + } + } + } + + /** + * Check the tenant for [v1]. */ public static void checkTenant(String tenant) { if (StringUtils.isNotBlank(tenant)) { @@ -166,7 +197,23 @@ public class ParamUtils { throw new IllegalArgumentException("invalid tenant"); } if (tenant.length() > TENANT_MAX_LEN) { - throw new IllegalArgumentException("too long tag, over 128"); + throw new IllegalArgumentException("too long tenant, over 128"); + } + } + } + + /** + * Check the namespaceId for [v2]. + */ + public static void checkTenantV2(String namespaceId) throws NacosApiException { + if (StringUtils.isNotBlank(namespaceId)) { + if (!isValid(namespaceId.trim())) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "invalid namespaceId"); + } + if (namespaceId.length() > TENANT_MAX_LEN) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, + "too long namespaceId, over 128"); } } } diff --git a/config/src/test/java/com/alibaba/nacos/config/server/controller/ConfigControllerTest.java b/config/src/test/java/com/alibaba/nacos/config/server/controller/ConfigControllerTest.java index e8e537c88..0eccfe9b9 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/controller/ConfigControllerTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/controller/ConfigControllerTest.java @@ -27,6 +27,7 @@ import com.alibaba.nacos.config.server.model.ConfigInfoBetaWrapper; import com.alibaba.nacos.config.server.model.GroupkeyListenserStatus; import com.alibaba.nacos.config.server.model.Page; import com.alibaba.nacos.config.server.model.SampleResult; +import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.ConfigSubService; import com.alibaba.nacos.config.server.service.repository.PersistService; import com.alibaba.nacos.config.server.utils.ZipUtils; @@ -83,6 +84,9 @@ public class ConfigControllerTest { @Mock private PersistService persistService; + @Mock + private ConfigOperationService configOperationService; + @Mock private ConfigSubService configSubService; @@ -92,6 +96,7 @@ public class ConfigControllerTest { when(servletContext.getContextPath()).thenReturn("/nacos"); ReflectionTestUtils.setField(configController, "configSubService", configSubService); ReflectionTestUtils.setField(configController, "persistService", persistService); + ReflectionTestUtils.setField(configController, "configOperationService", configOperationService); ReflectionTestUtils.setField(configController, "inner", inner); mockmvc = MockMvcBuilders.standaloneSetup(configController).build(); } @@ -99,6 +104,8 @@ public class ConfigControllerTest { @Test public void testPublishConfig() throws Exception { + when(configOperationService.publishConfig(any(), any(), anyString())).thenReturn(true); + MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post(Constants.CONFIG_CONTROLLER_PATH) .param("dataId", "test") .param("group", "test") @@ -159,6 +166,8 @@ public class ConfigControllerTest { @Test public void testDeleteConfig() throws Exception { + when(configOperationService.deleteConfig(anyString(), anyString(), anyString(), anyString(), any(), any())).thenReturn(true); + MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.delete(Constants.CONFIG_CONTROLLER_PATH) .param("dataId", "test") .param("group", "test") diff --git a/config/src/test/java/com/alibaba/nacos/config/server/controller/ConfigServletInnerTest.java b/config/src/test/java/com/alibaba/nacos/config/server/controller/ConfigServletInnerTest.java index d1df6f593..888e4408b 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/controller/ConfigServletInnerTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/controller/ConfigServletInnerTest.java @@ -259,4 +259,5 @@ public class ConfigServletInnerTest { configCacheServiceMockedStatic.close(); } -} \ No newline at end of file + +} diff --git a/config/src/test/java/com/alibaba/nacos/config/server/controller/HistoryControllerTest.java b/config/src/test/java/com/alibaba/nacos/config/server/controller/HistoryControllerTest.java index c4698e5ae..c87d7f142 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/controller/HistoryControllerTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/controller/HistoryControllerTest.java @@ -21,7 +21,7 @@ import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.Page; -import com.alibaba.nacos.config.server.service.repository.PersistService; +import com.alibaba.nacos.config.server.service.HistoryService; import com.alibaba.nacos.sys.env.EnvUtil; import com.fasterxml.jackson.databind.JsonNode; import org.junit.Assert; @@ -64,13 +64,13 @@ public class HistoryControllerTest { private ServletContext servletContext; @Mock - private PersistService persistService; + private HistoryService historyService; @Before public void setUp() { EnvUtil.setEnvironment(new StandardEnvironment()); when(servletContext.getContextPath()).thenReturn("/nacos"); - ReflectionTestUtils.setField(historyController, "persistService", persistService); + ReflectionTestUtils.setField(historyController, "historyService", historyService); mockmvc = MockMvcBuilders.standaloneSetup(historyController).build(); } @@ -92,7 +92,7 @@ public class HistoryControllerTest { page.setPagesAvailable(2); page.setPageItems(configHistoryInfoList); - when(persistService.findConfigHistory("test", "test", "", 1, 10)).thenReturn(page); + when(historyService.listConfigHistory("test", "test", "", 1, 10)).thenReturn(page); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.HISTORY_CONTROLLER_PATH) .param("search", "accurate").param("dataId", "test") @@ -126,7 +126,7 @@ public class HistoryControllerTest { configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); - when(persistService.detailConfigHistory(1L)).thenReturn(configHistoryInfo); + when(historyController.getConfigHistoryInfo("test", "test", "", 1L)).thenReturn(configHistoryInfo); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.HISTORY_CONTROLLER_PATH) .param("dataId", "test") @@ -154,7 +154,7 @@ public class HistoryControllerTest { configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); - when(persistService.detailPreviousConfigHistory(1L)).thenReturn(configHistoryInfo); + when(historyService.getPreviousConfigHistoryInfo("test", "test", "", 1L)).thenReturn(configHistoryInfo); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.HISTORY_CONTROLLER_PATH + "/previous") .param("dataId", "test") @@ -180,7 +180,7 @@ public class HistoryControllerTest { configInfoWrapper.setContent("test"); List configInfoWrappers = new ArrayList<>(); configInfoWrappers.add(configInfoWrapper); - when(persistService.queryConfigInfoByNamespace("test")).thenReturn(configInfoWrappers); + when(historyService.getConfigListByNamespace("test")).thenReturn(configInfoWrappers); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.HISTORY_CONTROLLER_PATH + "/configs") .param("tenant", "test"); @@ -196,4 +196,4 @@ public class HistoryControllerTest { } -} \ No newline at end of file +} diff --git a/config/src/test/java/com/alibaba/nacos/config/server/controller/v2/ConfigControllerV2Test.java b/config/src/test/java/com/alibaba/nacos/config/server/controller/v2/ConfigControllerV2Test.java new file mode 100644 index 000000000..347bdd218 --- /dev/null +++ b/config/src/test/java/com/alibaba/nacos/config/server/controller/v2/ConfigControllerV2Test.java @@ -0,0 +1,138 @@ +/* + * Copyright 1999-2022 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.nacos.config.server.controller.v2; + +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.config.server.controller.ConfigServletInner; +import com.alibaba.nacos.config.server.model.ConfigRequestInfo; +import com.alibaba.nacos.config.server.model.form.ConfigForm; +import com.alibaba.nacos.config.server.service.ConfigOperationService; +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class ConfigControllerV2Test { + + private ConfigControllerV2 configControllerV2; + + @Mock + private ConfigServletInner inner; + + @Mock + private ConfigOperationService configOperationService; + + private static final String TEST_DATA_ID = "test"; + + private static final String TEST_GROUP = "test"; + + private static final String TEST_NAMESPACE_ID = ""; + + private static final String TEST_TAG = ""; + + private static final String TEST_CONTENT = "test config"; + + @Before + public void setUp() { + configControllerV2 = new ConfigControllerV2(inner, configOperationService); + } + + @Test + public void testGetConfig() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + Result stringResult = Result.success(TEST_CONTENT); + + doAnswer(x -> { + x.getArgument(1, HttpServletResponse.class).setStatus(200); + x.getArgument(1, HttpServletResponse.class) + .setContentType(com.alibaba.nacos.common.http.param.MediaType.APPLICATION_JSON); + x.getArgument(1, HttpServletResponse.class).getWriter().print(JacksonUtils.toJson(stringResult)); + return null; + }).when(inner).doGetConfig(any(HttpServletRequest.class), any(HttpServletResponse.class), eq(TEST_DATA_ID), + eq(TEST_GROUP), eq(TEST_NAMESPACE_ID), eq(TEST_TAG), eq(null), anyString(), eq(true)); + + configControllerV2.getConfig(request, response, TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID, TEST_TAG); + + verify(inner) + .doGetConfig(eq(request), eq(response), eq(TEST_DATA_ID), eq(TEST_GROUP), eq(TEST_NAMESPACE_ID), eq(TEST_TAG), + eq(null), anyString(), eq(true)); + JsonNode resNode = JacksonUtils.toObj(response.getContentAsString()); + Integer errCode = JacksonUtils.toObj(resNode.get("code").toString(), Integer.class); + String actContent = JacksonUtils.toObj(resNode.get("data").toString(), String.class); + assertEquals(200, response.getStatus()); + assertEquals(ErrorCode.SUCCESS.getCode(), errCode); + assertEquals(TEST_CONTENT, actContent); + } + + @Test + public void testPublishConfig() throws Exception { + + ConfigForm configForm = new ConfigForm(); + configForm.setDataId(TEST_DATA_ID); + configForm.setGroup(TEST_GROUP); + configForm.setNamespaceId(TEST_NAMESPACE_ID); + configForm.setContent(TEST_CONTENT); + MockHttpServletRequest request = new MockHttpServletRequest(); + + when(configOperationService + .publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), anyString())) + .thenReturn(true); + + Result booleanResult = configControllerV2.publishConfig(configForm, request); + + verify(configOperationService).publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), anyString()); + + assertEquals(ErrorCode.SUCCESS.getCode(), booleanResult.getCode()); + assertEquals(true, booleanResult.getData()); + } + + @Test + public void testDeleteConfig() throws Exception { + + MockHttpServletRequest request = new MockHttpServletRequest(); + + when(configOperationService + .deleteConfig(eq(TEST_DATA_ID), eq(TEST_GROUP), eq(TEST_NAMESPACE_ID), eq(TEST_TAG), any(), any())).thenReturn(true); + + Result booleanResult = configControllerV2 + .deleteConfig(request, TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID, TEST_TAG); + + verify(configOperationService).deleteConfig(eq(TEST_DATA_ID), eq(TEST_GROUP), eq(TEST_NAMESPACE_ID), eq(TEST_TAG), any(), any()); + + assertEquals(ErrorCode.SUCCESS.getCode(), booleanResult.getCode()); + assertEquals(true, booleanResult.getData()); + } +} diff --git a/config/src/test/java/com/alibaba/nacos/config/server/controller/v2/HistoryControllerV2Test.java b/config/src/test/java/com/alibaba/nacos/config/server/controller/v2/HistoryControllerV2Test.java new file mode 100644 index 000000000..70af84e9e --- /dev/null +++ b/config/src/test/java/com/alibaba/nacos/config/server/controller/v2/HistoryControllerV2Test.java @@ -0,0 +1,180 @@ +/* + * Copyright 1999-2022 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.nacos.config.server.controller.v2; + +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; +import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; +import com.alibaba.nacos.config.server.model.Page; +import com.alibaba.nacos.config.server.service.HistoryService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * HistoryV2ControllerTest. + * + * @author dongyafei + * @date 2022/7/25 + */ + +@RunWith(MockitoJUnitRunner.class) +public class HistoryControllerV2Test { + + HistoryControllerV2 historyControllerV2; + + @Mock + private HistoryService historyService; + + private static final String TEST_DATA_ID = "test"; + + private static final String TEST_GROUP = "test"; + + private static final String TEST_NAMESPACE_ID = ""; + + private static final String TEST_CONTENT = "test config"; + + @Before + public void setUp() { + historyControllerV2 = new HistoryControllerV2(historyService); + } + + @Test + public void testListConfigHistory() throws Exception { + + ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); + configHistoryInfo.setDataId(TEST_DATA_ID); + configHistoryInfo.setGroup(TEST_GROUP); + configHistoryInfo.setContent(TEST_CONTENT); + configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); + configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); + List configHistoryInfoList = new ArrayList<>(); + configHistoryInfoList.add(configHistoryInfo); + + Page page = new Page<>(); + page.setTotalCount(15); + page.setPageNumber(1); + page.setPagesAvailable(2); + page.setPageItems(configHistoryInfoList); + + when(historyService.listConfigHistory(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID, 1, 10)).thenReturn(page); + + Result> pageResult = historyControllerV2 + .listConfigHistory(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID, 1, 10); + + verify(historyService).listConfigHistory(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID, 1, 10); + + List resultList = pageResult.getData().getPageItems(); + ConfigHistoryInfo resConfigHistoryInfo = resultList.get(0); + + assertEquals(ErrorCode.SUCCESS.getCode(), pageResult.getCode()); + assertEquals(configHistoryInfoList.size(), resultList.size()); + assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); + assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroup()); + assertEquals(configHistoryInfo.getContent(), resConfigHistoryInfo.getContent()); + + } + + @Test + public void testGetConfigHistoryInfo() throws Exception { + + ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); + configHistoryInfo.setDataId(TEST_DATA_ID); + configHistoryInfo.setGroup(TEST_GROUP); + configHistoryInfo.setContent(TEST_CONTENT); + configHistoryInfo.setTenant(TEST_NAMESPACE_ID); + configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); + configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); + + when(historyService.getConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID, 1L)).thenReturn(configHistoryInfo); + + Result result = historyControllerV2 + .getConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID, 1L); + + verify(historyService).getConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID, 1L); + + ConfigHistoryInfo resConfigHistoryInfo = result.getData(); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); + assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroup()); + assertEquals(configHistoryInfo.getContent(), resConfigHistoryInfo.getContent()); + + } + + @Test + public void testGetPreviousConfigHistoryInfo() throws Exception { + + ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); + configHistoryInfo.setDataId(TEST_DATA_ID); + configHistoryInfo.setGroup(TEST_GROUP); + configHistoryInfo.setContent(TEST_CONTENT); + configHistoryInfo.setTenant(TEST_NAMESPACE_ID); + configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); + configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); + + when(historyService.getPreviousConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID, 1L)).thenReturn(configHistoryInfo); + + Result result = historyControllerV2 + .getPreviousConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID, 1L); + + verify(historyService).getPreviousConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID, 1L); + + ConfigHistoryInfo resConfigHistoryInfo = result.getData(); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); + assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroup()); + assertEquals(configHistoryInfo.getContent(), resConfigHistoryInfo.getContent()); + + } + + @Test + public void testGetConfigListByNamespace() throws NacosApiException { + ConfigInfoWrapper configInfoWrapper = new ConfigInfoWrapper(); + configInfoWrapper.setDataId("test"); + configInfoWrapper.setGroup("test"); + configInfoWrapper.setContent("test"); + List configInfoWrappers = Collections.singletonList(configInfoWrapper); + + when(historyService.getConfigListByNamespace("test")).thenReturn(configInfoWrappers); + Result> result = historyControllerV2.getConfigsByTenant("test"); + verify(historyService).getConfigListByNamespace("test"); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + List actualList = result.getData(); + assertEquals(configInfoWrappers.size(), actualList.size()); + ConfigInfoWrapper actualConfigInfoWrapper = actualList.get(0); + assertEquals(configInfoWrapper.getDataId(), actualConfigInfoWrapper.getDataId()); + assertEquals(configInfoWrapper.getGroup(), actualConfigInfoWrapper.getGroup()); + assertEquals(configInfoWrapper.getContent(), actualConfigInfoWrapper.getContent()); + } +} diff --git a/config/src/test/java/com/alibaba/nacos/config/server/service/ConfigOperationServiceTest.java b/config/src/test/java/com/alibaba/nacos/config/server/service/ConfigOperationServiceTest.java new file mode 100644 index 000000000..8449e1128 --- /dev/null +++ b/config/src/test/java/com/alibaba/nacos/config/server/service/ConfigOperationServiceTest.java @@ -0,0 +1,106 @@ +/* + * Copyright 1999-2022 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.nacos.config.server.service; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.config.server.model.ConfigInfo; +import com.alibaba.nacos.config.server.model.ConfigRequestInfo; +import com.alibaba.nacos.config.server.model.form.ConfigForm; +import com.alibaba.nacos.config.server.service.repository.PersistService; +import com.alibaba.nacos.sys.env.EnvUtil; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.core.env.StandardEnvironment; + +import java.sql.Timestamp; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +/** + * ConfigServiceTest. + * + * @author dongyafei + * @date 2022/8/11 + */ + +@RunWith(MockitoJUnitRunner.class) +public class ConfigOperationServiceTest { + + private ConfigOperationService configOperationService; + + @Mock + private PersistService persistService; + + @Before + public void setUp() throws Exception { + EnvUtil.setEnvironment(new StandardEnvironment()); + this.configOperationService = new ConfigOperationService(persistService); + } + + @Test + public void testPublishConfig() throws NacosException { + ConfigForm configForm = new ConfigForm(); + configForm.setDataId("test"); + configForm.setGroup("test"); + configForm.setContent("test content"); + + ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); + + // if betaIps is blank and tag is blank + Boolean aResult = configOperationService.publishConfig(configForm, configRequestInfo, ""); + verify(persistService) + .insertOrUpdate(any(), any(), any(ConfigInfo.class), any(Timestamp.class), any(), anyBoolean()); + Assert.assertEquals(true, aResult); + + // if betaIps is blank and tag is not blank + configForm.setTag("test tag"); + Boolean bResult = configOperationService.publishConfig(configForm, configRequestInfo, ""); + verify(persistService) + .insertOrUpdateTag(any(ConfigInfo.class), eq("test tag"), any(), any(), any(Timestamp.class), + anyBoolean()); + Assert.assertEquals(true, bResult); + + // if betaIps is not blank + configRequestInfo.setBetaIps("test-betaIps"); + Boolean cResult = configOperationService.publishConfig(configForm, configRequestInfo, ""); + verify(persistService) + .insertOrUpdateBeta(any(ConfigInfo.class), eq("test-betaIps"), any(), any(), any(Timestamp.class), + anyBoolean()); + Assert.assertEquals(true, cResult); + } + + @Test + public void testDeleteConfig() { + + // if tag is blank + Boolean aResult = configOperationService.deleteConfig("test", "test", "", "", "1.1.1.1", "test"); + verify(persistService).removeConfigInfo(eq("test"), eq("test"), eq(""), any(), any()); + Assert.assertEquals(true, aResult); + + // if tag is not blank + Boolean bResult = configOperationService.deleteConfig("test", "test", "", "test", "1.1.1.1", "test"); + verify(persistService).removeConfigInfoTag(eq("test"), eq("test"), eq(""), eq("test"), any(), any()); + Assert.assertEquals(true, bResult); + } +} diff --git a/config/src/test/java/com/alibaba/nacos/config/server/service/HistoryServiceTest.java b/config/src/test/java/com/alibaba/nacos/config/server/service/HistoryServiceTest.java new file mode 100644 index 000000000..6d30aa96e --- /dev/null +++ b/config/src/test/java/com/alibaba/nacos/config/server/service/HistoryServiceTest.java @@ -0,0 +1,167 @@ +/* + * Copyright 1999-2022 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.nacos.config.server.service; + +import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; +import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; +import com.alibaba.nacos.config.server.model.Page; +import com.alibaba.nacos.config.server.service.repository.PersistService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * HistoryServiceTest. + * @author dongyafei + * @date 2022/8/11 + */ + +@RunWith(MockitoJUnitRunner.class) +public class HistoryServiceTest { + + private HistoryService historyService; + + @Mock + private PersistService persistService; + + private static final String TEST_DATA_ID = "test"; + + private static final String TEST_GROUP = "test"; + + private static final String TEST_TENANT = ""; + + private static final String TEST_CONTENT = "test config"; + + @Before + public void setUp() throws Exception { + this.historyService = new HistoryService(persistService); + } + + @Test + public void testListConfigHistory() { + ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); + configHistoryInfo.setDataId(TEST_DATA_ID); + configHistoryInfo.setGroup(TEST_GROUP); + configHistoryInfo.setContent(TEST_CONTENT); + configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); + configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); + List configHistoryInfoList = new ArrayList<>(); + configHistoryInfoList.add(configHistoryInfo); + + Page page = new Page<>(); + page.setTotalCount(15); + page.setPageNumber(1); + page.setPagesAvailable(2); + page.setPageItems(configHistoryInfoList); + + when(persistService.findConfigHistory(TEST_DATA_ID, TEST_GROUP, TEST_TENANT, 1, 10)).thenReturn(page); + + Page pageResult = historyService + .listConfigHistory(TEST_DATA_ID, TEST_GROUP, TEST_TENANT, 1, 10); + + verify(persistService).findConfigHistory(TEST_DATA_ID, TEST_GROUP, TEST_TENANT, 1, 10); + + List resultList = pageResult.getPageItems(); + ConfigHistoryInfo resConfigHistoryInfo = resultList.get(0); + + assertEquals(configHistoryInfoList.size(), resultList.size()); + assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); + assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroup()); + assertEquals(configHistoryInfo.getContent(), resConfigHistoryInfo.getContent()); + } + + @Test + public void testGetConfigHistoryInfo() throws Exception { + + ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); + configHistoryInfo.setDataId(TEST_DATA_ID); + configHistoryInfo.setGroup(TEST_GROUP); + configHistoryInfo.setContent(TEST_CONTENT); + configHistoryInfo.setTenant(TEST_TENANT); + configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); + configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); + + when(persistService.detailConfigHistory(1L)).thenReturn(configHistoryInfo); + + ConfigHistoryInfo resConfigHistoryInfo = historyService + .getConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_TENANT, 1L); + + verify(persistService).detailConfigHistory(1L); + + assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); + assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroup()); + assertEquals(configHistoryInfo.getContent(), resConfigHistoryInfo.getContent()); + + } + + @Test + public void testGetPreviousConfigHistoryInfo() throws Exception { + + ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); + configHistoryInfo.setDataId(TEST_DATA_ID); + configHistoryInfo.setGroup(TEST_GROUP); + configHistoryInfo.setContent(TEST_CONTENT); + configHistoryInfo.setTenant(TEST_TENANT); + configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); + configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); + + when(persistService.detailPreviousConfigHistory(1L)).thenReturn(configHistoryInfo); + + ConfigHistoryInfo resConfigHistoryInfo = historyService + .getPreviousConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_TENANT, 1L); + + verify(persistService).detailPreviousConfigHistory(1L); + + assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); + assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroup()); + assertEquals(configHistoryInfo.getContent(), resConfigHistoryInfo.getContent()); + + } + + @Test + public void testGetConfigListByNamespace() { + ConfigInfoWrapper configInfoWrapper = new ConfigInfoWrapper(); + configInfoWrapper.setDataId("test"); + configInfoWrapper.setGroup("test"); + configInfoWrapper.setContent("test"); + List configInfoWrappers = Collections.singletonList(configInfoWrapper); + + when(persistService.queryConfigInfoByNamespace("test")).thenReturn(configInfoWrappers); + + List actualList = historyService.getConfigListByNamespace("test"); + + verify(persistService).queryConfigInfoByNamespace("test"); + + assertEquals(configInfoWrappers.size(), actualList.size()); + ConfigInfoWrapper actualConfigInfoWrapper = actualList.get(0); + assertEquals(configInfoWrapper.getDataId(), actualConfigInfoWrapper.getDataId()); + assertEquals(configInfoWrapper.getGroup(), actualConfigInfoWrapper.getGroup()); + assertEquals(configInfoWrapper.getContent(), actualConfigInfoWrapper.getContent()); + } +} diff --git a/config/src/test/java/com/alibaba/nacos/config/server/service/datasource/DataSourcePoolPropertiesTest.java b/config/src/test/java/com/alibaba/nacos/config/server/service/datasource/DataSourcePoolPropertiesTest.java index fbc9dcf10..681a158a9 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/service/datasource/DataSourcePoolPropertiesTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/service/datasource/DataSourcePoolPropertiesTest.java @@ -62,6 +62,7 @@ public class DataSourcePoolPropertiesTest { assertEquals(PASSWORD, actual.getPassword()); assertEquals(CONNECTION_TIMEOUT.longValue(), actual.getConnectionTimeout()); assertEquals(DataSourcePoolProperties.DEFAULT_VALIDATION_TIMEOUT, actual.getValidationTimeout()); + assertEquals(DataSourcePoolProperties.DEFAULT_IDLE_TIMEOUT, actual.getIdleTimeout()); assertEquals(MAX_POOL_SIZE.intValue(), actual.getMaximumPoolSize()); assertEquals(DataSourcePoolProperties.DEFAULT_MINIMUM_IDLE, actual.getMinimumIdle()); } diff --git a/config/src/test/java/com/alibaba/nacos/config/server/utils/GroupKeyTest.java b/config/src/test/java/com/alibaba/nacos/config/server/utils/GroupKeyTest.java index 48f678d6f..e7826b597 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/utils/GroupKeyTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/utils/GroupKeyTest.java @@ -35,7 +35,7 @@ public class GroupKeyTest { public void testParseInvalidGroupKey() { String key = "11111+222+333333+444"; try { - GroupKey2.parseKey(key); + GroupKey.parseKey(key); Assert.fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); @@ -43,7 +43,7 @@ public class GroupKeyTest { key = "11111+"; try { - GroupKey2.parseKey(key); + GroupKey.parseKey(key); Assert.fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); @@ -51,7 +51,7 @@ public class GroupKeyTest { key = "11111%29+222"; try { - GroupKey2.parseKey(key); + GroupKey.parseKey(key); Assert.fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); @@ -59,14 +59,14 @@ public class GroupKeyTest { key = "11111%2b+222"; try { - GroupKey2.parseKey(key); + GroupKey.parseKey(key); Assert.fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); } key = "11111%25+222"; - String[] pair = GroupKey2.parseKey(key); + String[] pair = GroupKey.parseKey(key); Assert.assertEquals("11111%", pair[0]); Assert.assertEquals("222", pair[1]); } @@ -188,4 +188,5 @@ public class GroupKeyTest { // Method is not expected to return due to exception thrown } + } diff --git a/console-ui/src/globalLib.js b/console-ui/src/globalLib.js index 90d772cb1..fd78a9327 100644 --- a/console-ui/src/globalLib.js +++ b/console-ui/src/globalLib.js @@ -504,7 +504,6 @@ const request = (function(_global) { const [url, paramsStr] = config.url.split('?'); const params = paramsStr ? paramsStr.split('&') : []; params.push(`accessToken=${accessToken}`); - params.push('message=true'); return $.ajax( Object.assign({}, config, { diff --git a/console-ui/src/locales/en-US.js b/console-ui/src/locales/en-US.js index f7d32694f..b08501421 100644 --- a/console-ui/src/locales/en-US.js +++ b/console-ui/src/locales/en-US.js @@ -35,7 +35,8 @@ const I18N_CONF = { pleaseInputUsername: 'Please input username', pleaseInputPassword: 'Please input password', invalidUsernameOrPassword: 'invalid username or password', - productDesc: 'an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications', + productDesc: + 'an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications', }, MainLayout: { nacosName: 'NACOS', @@ -571,6 +572,9 @@ const I18N_CONF = { password: 'Password', operation: 'Operation', refresh: 'Refresh', + query: 'Search', + defaultFuzzyd: 'Default fuzzy query mode opened', + fuzzyd: "Add wildcard '*' for fuzzy query", }, NewUser: { createUser: 'Create user', @@ -605,6 +609,9 @@ const I18N_CONF = { deleteRole: 'Delete', deleteRoleTip: 'Do you want to delete this role?', refresh: 'Refresh', + defaultFuzzyd: 'Default fuzzy query mode opened', + fuzzyd: "Add wildcard '*' for fuzzy query", + query: 'Search', }, NewRole: { bindingRoles: 'Binding roles', @@ -628,6 +635,9 @@ const I18N_CONF = { writeOnly: 'write only', readWrite: 'Read and write', refresh: 'Refresh', + defaultFuzzyd: 'Default fuzzy query mode opened', + fuzzyd: "Add wildcard '*' for fuzzy query", + query: 'Search', }, NewPermissions: { addPermission: 'Add Permission', diff --git a/console-ui/src/locales/zh-CN.js b/console-ui/src/locales/zh-CN.js index c095b9bc0..b28c39953 100644 --- a/console-ui/src/locales/zh-CN.js +++ b/console-ui/src/locales/zh-CN.js @@ -567,6 +567,9 @@ const I18N_CONF = { password: 'åƆē ', operation: 'ę“ä½œ', refresh: 'åˆ·ę–°', + query: 'ęŸ„čÆ¢', + defaultFuzzyd: '已开åÆ默认ęØ”ē³ŠęŸ„čÆ¢', + fuzzyd: "ę·»åŠ é€šé…ē¬¦'*'čæ›č”ŒęØ”ē³ŠęŸ„čÆ¢", }, NewUser: { createUser: '创å»ŗē”Øꈷ', @@ -601,6 +604,9 @@ const I18N_CONF = { deleteRole: '删除', deleteRoleTip: 'ę˜Æå¦č¦åˆ é™¤čÆ„č§’č‰²ļ¼Ÿ', refresh: 'åˆ·ę–°', + defaultFuzzyd: '已开åÆ默认ęØ”ē³ŠęŸ„čÆ¢', + fuzzyd: "ę·»åŠ é€šé…ē¬¦'*'čæ›č”ŒęØ”ē³ŠęŸ„čÆ¢", + query: 'ęŸ„čÆ¢', }, NewRole: { bindingRoles: 'ē»‘å®šč§’č‰²', @@ -624,6 +630,9 @@ const I18N_CONF = { writeOnly: 'åŖ写', readWrite: 'čƻ写', refresh: 'åˆ·ę–°', + defaultFuzzyd: '已开åÆ默认ęØ”ē³ŠęŸ„čÆ¢', + fuzzyd: "ę·»åŠ é€šé…ē¬¦'*'čæ›č”ŒęØ”ē³ŠęŸ„čÆ¢", + query: 'ęŸ„čÆ¢', }, NewPermissions: { addPermission: 'ę·»åŠ ęƒé™', diff --git a/console-ui/src/pages/AuthorityControl/PermissionsManagement/PermissionsManagement.js b/console-ui/src/pages/AuthorityControl/PermissionsManagement/PermissionsManagement.js index 5218b8fe4..ae1ac575e 100644 --- a/console-ui/src/pages/AuthorityControl/PermissionsManagement/PermissionsManagement.js +++ b/console-ui/src/pages/AuthorityControl/PermissionsManagement/PermissionsManagement.js @@ -16,7 +16,16 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Button, Dialog, Pagination, Table, ConfigProvider } from '@alifd/next'; +import { + Button, + Dialog, + Pagination, + Table, + ConfigProvider, + Form, + Input, + Switch, +} from '@alifd/next'; import { connect } from 'react-redux'; import { getPermissions, createPermission, deletePermission } from '../../../reducers/authority'; import { getNamespaces } from '../../../reducers/namespace'; @@ -51,7 +60,10 @@ class PermissionsManagement extends React.Component { pageNo: 1, pageSize: 9, createPermission: false, + defaultFuzzySearch: true, + role: '', }; + this.handleDefaultFuzzySwitchChange = this.handleDefaultFuzzySwitchChange.bind(this); } componentDidMount() { @@ -62,8 +74,18 @@ class PermissionsManagement extends React.Component { getPermissions() { this.setState({ loading: true }); const { pageNo, pageSize } = this.state; + let role = this.state.role; + let search = 'accurate'; + if (this.state.defaultFuzzySearch) { + if (role && role !== '') { + role = '*' + role + '*'; + } + } + if (role && role.indexOf('*') !== -1) { + search = 'blur'; + } this.props - .getPermissions({ pageNo, pageSize }) + .getPermissions({ pageNo, pageSize, role, search }) .then(() => { if (this.state.loading) { this.setState({ loading: false }); @@ -85,24 +107,57 @@ class PermissionsManagement extends React.Component { }[action]; } + handleDefaultFuzzySwitchChange() { + this.setState({ defaultFuzzySearch: !this.state.defaultFuzzySearch }); + } + render() { const { permissions, namespaces = [], locale } = this.props; const { loading, pageSize, pageNo, createPermissionVisible } = this.state; return ( <> -
- - -
+
+ + { + this.setState({ role }); + }} + /> + + + + + + + + + + +
{ if (this.state.loading) { this.setState({ loading: false }); @@ -65,24 +96,71 @@ class RolesManagement extends React.Component { this.setState({ createRoleVisible: false }); } + handleDefaultFuzzySwitchChange() { + this.setState({ + defaultFuzzySearch: !this.state.defaultFuzzySearch, + }); + } + render() { const { roles, locale } = this.props; const { loading, pageSize, pageNo, createRoleVisible, passwordResetUser } = this.state; return ( <> -
- - -
+ +
+ + { + this.setState({ username }); + }} + /> + + + { + this.setState({ role }); + }} + /> + + + + + + + + + + +
diff --git a/console-ui/src/pages/AuthorityControl/UserManagement/UserManagement.js b/console-ui/src/pages/AuthorityControl/UserManagement/UserManagement.js index a60f5d30c..75669370e 100644 --- a/console-ui/src/pages/AuthorityControl/UserManagement/UserManagement.js +++ b/console-ui/src/pages/AuthorityControl/UserManagement/UserManagement.js @@ -16,7 +16,16 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Button, Dialog, Pagination, Table, ConfigProvider } from '@alifd/next'; +import { + Button, + Dialog, + Pagination, + Table, + ConfigProvider, + Form, + Input, + Switch, +} from '@alifd/next'; import { connect } from 'react-redux'; import { getUsers, createUser, deleteUser, passwordReset } from '../../../reducers/authority'; import RegionGroup from '../../../components/RegionGroup'; @@ -24,6 +33,7 @@ import NewUser from './NewUser'; import PasswordReset from './PasswordReset'; import './UserManagement.scss'; +import { getParams } from '../../../globalLib'; @connect(state => ({ users: state.authority.users }), { getUsers }) @ConfigProvider.config @@ -39,11 +49,15 @@ class UserManagement extends React.Component { constructor(props) { super(props); + this.username = getParams('username'); this.state = { loading: true, pageNo: 1, pageSize: 9, + username: this.username, + defaultFuzzySearch: true, }; + this.handleDefaultFuzzySwitchChange = this.handleDefaultFuzzySwitchChange.bind(this); } componentDidMount() { @@ -52,9 +66,29 @@ class UserManagement extends React.Component { getUsers() { this.setState({ loading: true }); - const { pageNo, pageSize } = this.state; + const params = { + pageNo: this.state.pageNo, + pageSize: this.state.pageSize, + username: this.username, + search: 'blur', + }; + if (this.state.defaultFuzzySearch) { + if (params.username && params.username !== '') { + params.username = '*' + params.username + '*'; + } + } + if (params.username && params.username.indexOf('*') !== -1) { + params.search = 'blur'; + } else { + params.search = 'accurate'; + } this.props - .getUsers({ pageNo, pageSize }) + .getUsers({ + pageNo: params.pageNo, + pageSize: params.pageSize, + username: params.username, + search: params.search, + }) .then(() => { if (this.state.loading) { this.setState({ loading: false }); @@ -67,6 +101,12 @@ class UserManagement extends React.Component { this.setState({ createUserVisible: false }); } + handleDefaultFuzzySwitchChange() { + this.setState({ + defaultFuzzySearch: !this.state.defaultFuzzySearch, + }); + } + render() { const { users, locale } = this.props; const { @@ -80,18 +120,49 @@ class UserManagement extends React.Component { return ( <> -
- - -
+
+ + { + this.username = username; + this.setState({ username }); + }} + /> + + + + + + + + + + + +
{ if (!params) { config.params = {}; } - config.params.message = true; if (!url.includes('auth/users/login')) { let token = {}; try { diff --git a/console/src/main/java/com/alibaba/nacos/console/controller/NamespaceController.java b/console/src/main/java/com/alibaba/nacos/console/controller/NamespaceController.java index 642b8cf4c..342a9629d 100644 --- a/console/src/main/java/com/alibaba/nacos/console/controller/NamespaceController.java +++ b/console/src/main/java/com/alibaba/nacos/console/controller/NamespaceController.java @@ -16,15 +16,15 @@ package com.alibaba.nacos.console.controller; +import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.model.RestResult; import com.alibaba.nacos.common.model.RestResultUtils; import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.config.server.model.TenantInfo; import com.alibaba.nacos.config.server.service.repository.PersistService; -import com.alibaba.nacos.console.enums.NamespaceTypeEnum; import com.alibaba.nacos.console.model.Namespace; import com.alibaba.nacos.console.model.NamespaceAllInfo; +import com.alibaba.nacos.console.service.NamespaceOperationService; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.impl.constant.AuthConstants; import org.springframework.beans.factory.annotation.Autowired; @@ -36,7 +36,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.regex.Pattern; @@ -53,24 +52,13 @@ public class NamespaceController { @Autowired private PersistService persistService; + @Autowired + private NamespaceOperationService namespaceOperationService; + private final Pattern namespaceIdCheckPattern = Pattern.compile("^[\\w-]+"); private static final int NAMESPACE_ID_MAX_LENGTH = 128; - private static final String DEFAULT_NAMESPACE = "public"; - - private static final int DEFAULT_QUOTA = 200; - - private static final String DEFAULT_CREATE_SOURCE = "nacos"; - - private static final String DEFAULT_NAMESPACE_SHOW_NAME = "Public"; - - private static final String DEFAULT_NAMESPACE_DESCRIPTION = "Public Namespace"; - - private static final String DEFAULT_TENANT = ""; - - private static final String DEFAULT_KP = "1"; - /** * Get namespace list. * @@ -78,19 +66,7 @@ public class NamespaceController { */ @GetMapping public RestResult> getNamespaces() { - // TODO čŽ·å–ē”Økp - List tenantInfos = persistService.findTenantByKp(DEFAULT_KP); - Namespace namespace0 = new Namespace("", DEFAULT_NAMESPACE, DEFAULT_QUOTA, - persistService.configInfoCount(DEFAULT_TENANT), NamespaceTypeEnum.GLOBAL.getType()); - List namespaces = new ArrayList<>(); - namespaces.add(namespace0); - for (TenantInfo tenantInfo : tenantInfos) { - int configCount = persistService.configInfoCount(tenantInfo.getTenantId()); - Namespace namespaceTmp = new Namespace(tenantInfo.getTenantId(), tenantInfo.getTenantName(), - tenantInfo.getTenantDesc(), DEFAULT_QUOTA, configCount, NamespaceTypeEnum.CUSTOM.getType()); - namespaces.add(namespaceTmp); - } - return RestResultUtils.success(namespaces); + return RestResultUtils.success(namespaceOperationService.getNamespaceList()); } /** @@ -100,18 +76,8 @@ public class NamespaceController { * @return namespace all info */ @GetMapping(params = "show=all") - public NamespaceAllInfo getNamespace(@RequestParam("namespaceId") String namespaceId) { - // TODO čŽ·å–ē”Økp - if (StringUtils.isBlank(namespaceId)) { - return new NamespaceAllInfo(namespaceId, DEFAULT_NAMESPACE_SHOW_NAME, DEFAULT_QUOTA, - persistService.configInfoCount(DEFAULT_TENANT), NamespaceTypeEnum.GLOBAL.getType(), - DEFAULT_NAMESPACE_DESCRIPTION); - } else { - TenantInfo tenantInfo = persistService.findTenantByKp(DEFAULT_KP, namespaceId); - int configCount = persistService.configInfoCount(namespaceId); - return new NamespaceAllInfo(namespaceId, tenantInfo.getTenantName(), DEFAULT_QUOTA, configCount, - NamespaceTypeEnum.CUSTOM.getType(), tenantInfo.getTenantDesc()); - } + public NamespaceAllInfo getNamespace(@RequestParam("namespaceId") String namespaceId) throws NacosException { + return namespaceOperationService.getNamespace(namespaceId); } /** @@ -126,7 +92,6 @@ public class NamespaceController { public Boolean createNamespace(@RequestParam("customNamespaceId") String namespaceId, @RequestParam("namespaceName") String namespaceName, @RequestParam(value = "namespaceDesc", required = false) String namespaceDesc) { - // TODO čŽ·å–ē”Økp if (StringUtils.isBlank(namespaceId)) { namespaceId = UUID.randomUUID().toString(); } else { @@ -137,13 +102,12 @@ public class NamespaceController { if (namespaceId.length() > NAMESPACE_ID_MAX_LENGTH) { return false; } - if (persistService.tenantInfoCountByTenantId(namespaceId) > 0) { - return false; - } } - persistService.insertTenantInfoAtomic(DEFAULT_KP, namespaceId, namespaceName, namespaceDesc, - DEFAULT_CREATE_SOURCE, System.currentTimeMillis()); - return true; + try { + return namespaceOperationService.createNamespace(namespaceId, namespaceName, namespaceDesc); + } catch (NacosException e) { + return false; + } } /** @@ -173,9 +137,7 @@ public class NamespaceController { public Boolean editNamespace(@RequestParam("namespace") String namespace, @RequestParam("namespaceShowName") String namespaceShowName, @RequestParam(value = "namespaceDesc", required = false) String namespaceDesc) { - // TODO čŽ·å–ē”Økp - persistService.updateTenantNameAtomic(DEFAULT_KP, namespace, namespaceShowName, namespaceDesc); - return true; + return namespaceOperationService.editNamespace(namespace, namespaceShowName, namespaceDesc); } /** @@ -186,9 +148,8 @@ public class NamespaceController { */ @DeleteMapping @Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE) - public Boolean deleteConfig(@RequestParam("namespaceId") String namespaceId) { - persistService.removeTenantInfoAtomic(DEFAULT_KP, namespaceId); - return true; + public Boolean deleteNamespace(@RequestParam("namespaceId") String namespaceId) { + return namespaceOperationService.removeNamespace(namespaceId); } } diff --git a/console/src/main/java/com/alibaba/nacos/console/controller/v2/NamespaceControllerV2.java b/console/src/main/java/com/alibaba/nacos/console/controller/v2/NamespaceControllerV2.java new file mode 100644 index 000000000..6c87d7695 --- /dev/null +++ b/console/src/main/java/com/alibaba/nacos/console/controller/v2/NamespaceControllerV2.java @@ -0,0 +1,144 @@ +/* + * Copyright 1999-2022 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.nacos.console.controller.v2; + +import com.alibaba.nacos.api.annotation.NacosApi; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.console.model.Namespace; +import com.alibaba.nacos.console.model.NamespaceAllInfo; +import com.alibaba.nacos.console.model.form.NamespaceForm; +import com.alibaba.nacos.console.service.NamespaceOperationService; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import com.alibaba.nacos.plugin.auth.impl.constant.AuthConstants; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.UUID; +import java.util.regex.Pattern; + +/** + * NamespaceControllerV2. + * @author dongyafei + * @date 2022/8/16 + */ +@NacosApi +@RestController +@RequestMapping(path = "/v2/console/namespace") +public class NamespaceControllerV2 { + + private final NamespaceOperationService namespaceOperationService; + + public NamespaceControllerV2(NamespaceOperationService namespaceOperationService) { + this.namespaceOperationService = namespaceOperationService; + } + + private final Pattern namespaceIdCheckPattern = Pattern.compile("^[\\w-]+"); + + private static final int NAMESPACE_ID_MAX_LENGTH = 128; + + /** + * Get namespace list. + * + * @return namespace list + */ + @GetMapping("/list") + public Result> getNamespaceList() { + return Result.success(namespaceOperationService.getNamespaceList()); + } + + /** + * get namespace all info by namespace id. + * + * @param namespaceId namespaceId + * @return namespace all info + */ + @GetMapping() + public Result getNamespace(@RequestParam("namespaceId") String namespaceId) + throws NacosException { + return Result.success(namespaceOperationService.getNamespace(namespaceId)); + } + + /** + * create namespace. + * + * @param namespaceForm namespaceForm. + * @return whether create ok + */ + @PostMapping + @Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE) + public Result createNamespace(NamespaceForm namespaceForm) throws NacosException { + + namespaceForm.validate(); + + String namespaceId = namespaceForm.getNamespaceId(); + String namespaceName = namespaceForm.getNamespaceName(); + String namespaceDesc = namespaceForm.getNamespaceDesc(); + + if (StringUtils.isBlank(namespaceId)) { + namespaceId = UUID.randomUUID().toString(); + } else { + namespaceId = namespaceId.trim(); + if (!namespaceIdCheckPattern.matcher(namespaceId).matches()) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.ILLEGAL_NAMESPACE, + "namespaceId [" + namespaceId + "] mismatch the pattern"); + } + if (namespaceId.length() > NAMESPACE_ID_MAX_LENGTH) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.ILLEGAL_NAMESPACE, + "too long namespaceId, over " + NAMESPACE_ID_MAX_LENGTH); + } + } + return Result.success(namespaceOperationService.createNamespace(namespaceId, namespaceName, namespaceDesc)); + } + + /** + * edit namespace. + * + * @param namespaceForm namespace params + * @return whether edit ok + */ + @PutMapping + @Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE) + public Result editNamespace(NamespaceForm namespaceForm) throws NacosException { + namespaceForm.validate(); + return Result.success(namespaceOperationService.editNamespace(namespaceForm.getNamespaceId(), + namespaceForm.getNamespaceName(), namespaceForm.getNamespaceDesc())); + } + + /** + * delete namespace by id. + * + * @param namespaceId namespace ID + * @return whether delete ok + */ + @DeleteMapping + @Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE) + public Result deleteNamespace(@RequestParam("namespaceId") String namespaceId) { + return Result.success(namespaceOperationService.removeNamespace(namespaceId)); + } +} diff --git a/console/src/main/java/com/alibaba/nacos/console/exception/NacosApiExceptionHandler.java b/console/src/main/java/com/alibaba/nacos/console/exception/NacosApiExceptionHandler.java new file mode 100644 index 000000000..df0c1f025 --- /dev/null +++ b/console/src/main/java/com/alibaba/nacos/console/exception/NacosApiExceptionHandler.java @@ -0,0 +1,131 @@ +/* + * Copyright 1999-2022 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.nacos.console.exception; + +import com.alibaba.nacos.api.annotation.NacosApi; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.common.utils.ExceptionUtil; +import com.alibaba.nacos.plugin.auth.exception.AccessException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.Order; +import org.springframework.dao.DataAccessException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageConversionException; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.web.HttpMediaTypeException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import javax.servlet.ServletException; +import java.io.IOException; + +/** + * Exception Handler for Nacos API. + * @author dongyafei + * @date 2022/7/22 + */ + +@Order(-1) +@ControllerAdvice(annotations = {NacosApi.class}) +@ResponseBody +public class NacosApiExceptionHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(NacosApiExceptionHandler.class); + + @ExceptionHandler(NacosApiException.class) + public ResponseEntity> handleNacosApiException(NacosApiException e) { + LOGGER.error("got exception. {} {}", e.getErrAbstract(), e.getErrMsg()); + return ResponseEntity.status(e.getErrCode()).body(new Result<>(e.getDetailErrCode(), e.getErrAbstract(), e.getErrMsg())); + } + + @ExceptionHandler(NacosException.class) + public ResponseEntity> handleNacosException(NacosException e) { + LOGGER.error("got exception. {}", e.getErrMsg()); + return ResponseEntity.status(e.getErrCode()).body(Result.failure(ErrorCode.SERVER_ERROR, e.getErrMsg())); + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(HttpMessageNotReadableException.class) + public Result handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { + LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e)); + return Result.failure(ErrorCode.PARAMETER_MISSING, e.getMessage()); + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(HttpMessageConversionException.class) + public Result handleHttpMessageConversionException(HttpMessageConversionException e) { + LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e)); + return Result.failure(ErrorCode.PARAMETER_VALIDATE_ERROR, e.getMessage()); + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(NumberFormatException.class) + public Result handleNumberFormatException(NumberFormatException e) { + LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e)); + return Result.failure(ErrorCode.PARAMETER_VALIDATE_ERROR, e.getMessage()); + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(IllegalArgumentException.class) + public Result handleIllegalArgumentException(IllegalArgumentException e) { + LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e)); + return Result.failure(ErrorCode.PARAMETER_VALIDATE_ERROR, e.getMessage()); + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(MissingServletRequestParameterException.class) + public Result handleMissingServletRequestParameterException(MissingServletRequestParameterException e) { + LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e)); + return Result.failure(ErrorCode.PARAMETER_MISSING, e.getMessage()); + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(HttpMediaTypeException.class) + public Result handleHttpMediaTypeException(HttpMediaTypeException e) { + LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e)); + return Result.failure(ErrorCode.MEDIA_TYPE_ERROR, e.getMessage()); + } + + @ResponseStatus(HttpStatus.FORBIDDEN) + @ExceptionHandler(AccessException.class) + public Result handleAccessException(AccessException e) { + LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e)); + return Result.failure(ErrorCode.ACCESS_DENIED, e.getErrMsg()); + } + + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ExceptionHandler(value = {DataAccessException.class, ServletException.class, IOException.class}) + public Result handleDataAccessException(Exception e) { + LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e)); + return Result.failure(ErrorCode.DATA_ACCESS_ERROR, e.getMessage()); + } + + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ExceptionHandler(Exception.class) + public Result handleOtherException(Exception e) { + LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e)); + return Result.failure(e.getMessage()); + } +} diff --git a/console/src/main/java/com/alibaba/nacos/console/model/form/NamespaceForm.java b/console/src/main/java/com/alibaba/nacos/console/model/form/NamespaceForm.java new file mode 100644 index 000000000..851429a3d --- /dev/null +++ b/console/src/main/java/com/alibaba/nacos/console/model/form/NamespaceForm.java @@ -0,0 +1,111 @@ +/* + * Copyright 1999-2022 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.nacos.console.model.form; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import org.springframework.http.HttpStatus; + +import java.io.Serializable; +import java.util.Objects; + +/** + * NamespaceForm. + * @author dongyafei + * @date 2022/8/16 + */ +public class NamespaceForm implements Serializable { + + private static final long serialVersionUID = -1078976569495343487L; + + private String namespaceId; + + private String namespaceName; + + private String namespaceDesc; + + public NamespaceForm() { + } + + public NamespaceForm(String namespaceId, String namespaceName, String namespaceDesc) { + this.namespaceId = namespaceId; + this.namespaceName = namespaceName; + this.namespaceDesc = namespaceDesc; + } + + public String getNamespaceId() { + return namespaceId; + } + + public void setNamespaceId(String namespaceId) { + this.namespaceId = namespaceId; + } + + public String getNamespaceName() { + return namespaceName; + } + + public void setNamespaceName(String namespaceName) { + this.namespaceName = namespaceName; + } + + public String getNamespaceDesc() { + return namespaceDesc; + } + + public void setNamespaceDesc(String namespaceDesc) { + this.namespaceDesc = namespaceDesc; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + NamespaceForm that = (NamespaceForm) o; + return Objects.equals(namespaceId, that.namespaceId) && Objects.equals(namespaceName, that.namespaceName) + && Objects.equals(namespaceDesc, that.namespaceDesc); + } + + @Override + public int hashCode() { + return Objects.hash(namespaceId, namespaceName, namespaceDesc); + } + + @Override + public String toString() { + return "NamespaceVo{" + "namespaceId='" + namespaceId + '\'' + ", namespaceName='" + namespaceName + '\'' + + ", namespaceDesc='" + namespaceDesc + '\'' + '}'; + } + + /** + * check required param. + * @throws NacosException NacosException + */ + public void validate() throws NacosException { + if (null == namespaceId) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, "required parameter 'namespaceId' is missing"); + } + if (null == namespaceName) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, "required parameter 'namespaceName' is missing"); + } + } +} diff --git a/console/src/main/java/com/alibaba/nacos/console/service/NamespaceOperationService.java b/console/src/main/java/com/alibaba/nacos/console/service/NamespaceOperationService.java new file mode 100644 index 000000000..a4c6ba8ee --- /dev/null +++ b/console/src/main/java/com/alibaba/nacos/console/service/NamespaceOperationService.java @@ -0,0 +1,144 @@ +/* + * Copyright 1999-2022 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.nacos.console.service; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.config.server.model.TenantInfo; +import com.alibaba.nacos.config.server.service.repository.PersistService; +import com.alibaba.nacos.console.enums.NamespaceTypeEnum; +import com.alibaba.nacos.console.model.Namespace; +import com.alibaba.nacos.console.model.NamespaceAllInfo; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * NamespaceOperationService. + * + * @author dongyafei + * @date 2022/8/16 + */ + +@Service +public class NamespaceOperationService { + + private final PersistService persistService; + + private static final String DEFAULT_NAMESPACE = "public"; + + private static final String DEFAULT_NAMESPACE_SHOW_NAME = "Public"; + + private static final String DEFAULT_NAMESPACE_DESCRIPTION = "Public Namespace"; + + private static final int DEFAULT_QUOTA = 200; + + private static final String DEFAULT_CREATE_SOURCE = "nacos"; + + private static final String DEFAULT_TENANT = ""; + + private static final String DEFAULT_KP = "1"; + + public NamespaceOperationService(PersistService persistService) { + this.persistService = persistService; + } + + public List getNamespaceList() { + // TODO čŽ·å–ē”Økp + List tenantInfos = persistService.findTenantByKp(DEFAULT_KP); + + Namespace namespace0 = new Namespace("", DEFAULT_NAMESPACE, DEFAULT_QUOTA, + persistService.configInfoCount(DEFAULT_TENANT), NamespaceTypeEnum.GLOBAL.getType()); + List namespaceList = new ArrayList<>(); + namespaceList.add(namespace0); + + for (TenantInfo tenantInfo : tenantInfos) { + int configCount = persistService.configInfoCount(tenantInfo.getTenantId()); + Namespace namespaceTmp = new Namespace(tenantInfo.getTenantId(), tenantInfo.getTenantName(), + tenantInfo.getTenantDesc(), DEFAULT_QUOTA, configCount, NamespaceTypeEnum.CUSTOM.getType()); + namespaceList.add(namespaceTmp); + } + return namespaceList; + } + + /** + * query namespace by namespace id. + * + * @param namespaceId namespace Id. + * @return NamespaceAllInfo. + */ + public NamespaceAllInfo getNamespace(String namespaceId) throws NacosException { + // TODO čŽ·å–ē”Økp + if (StringUtils.isBlank(namespaceId)) { + return new NamespaceAllInfo(namespaceId, DEFAULT_NAMESPACE_SHOW_NAME, DEFAULT_QUOTA, + persistService.configInfoCount(DEFAULT_TENANT), NamespaceTypeEnum.GLOBAL.getType(), + DEFAULT_NAMESPACE_DESCRIPTION); + } else { + TenantInfo tenantInfo = persistService.findTenantByKp(DEFAULT_KP, namespaceId); + if (null == tenantInfo) { + throw new NacosApiException(HttpStatus.NOT_FOUND.value(), ErrorCode.NAMESPACE_NOT_EXIST, + "namespaceId [ " + namespaceId + " ] not exist"); + } + int configCount = persistService.configInfoCount(namespaceId); + return new NamespaceAllInfo(namespaceId, tenantInfo.getTenantName(), DEFAULT_QUOTA, configCount, + NamespaceTypeEnum.CUSTOM.getType(), tenantInfo.getTenantDesc()); + } + } + + /** + * create namespace. + * + * @param namespaceId namespace ID + * @param namespaceName namespace Name + * @param namespaceDesc namespace Desc + * @return whether create ok + */ + public Boolean createNamespace(String namespaceId, String namespaceName, String namespaceDesc) + throws NacosException { + // TODO čŽ·å–ē”Økp + if (persistService.tenantInfoCountByTenantId(namespaceId) > 0) { + throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(), ErrorCode.NAMESPACE_ALREADY_EXIST, + "namespaceId [" + namespaceId + "] already exist"); + } + + persistService + .insertTenantInfoAtomic(DEFAULT_KP, namespaceId, namespaceName, namespaceDesc, DEFAULT_CREATE_SOURCE, + System.currentTimeMillis()); + return true; + } + + /** + * edit namespace. + */ + public Boolean editNamespace(String namespaceId, String namespaceName, String namespaceDesc) { + // TODO čŽ·å–ē”Økp + persistService.updateTenantNameAtomic(DEFAULT_KP, namespaceId, namespaceName, namespaceDesc); + return true; + } + + /** + * remove namespace. + */ + public Boolean removeNamespace(String namespaceId) { + persistService.removeTenantInfoAtomic(DEFAULT_KP, namespaceId); + return true; + } +} diff --git a/console/src/main/resources/application.properties b/console/src/main/resources/application.properties index 273d6dcd3..65f9540e0 100644 --- a/console/src/main/resources/application.properties +++ b/console/src/main/resources/application.properties @@ -18,8 +18,9 @@ ### Default web context path: server.servlet.contextPath=/nacos ### Include message field -server.error.include-message=ON_PARAM +server.error.include-message=ALWAYS ### Default web server port: +server.port=8848 #*************** Network Related Configurations ***************# ### If prefer hostname over ip for Nacos server addresses in cluster.conf: @@ -38,18 +39,11 @@ server.error.include-message=ON_PARAM ### Connect URL of DB: # db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC -# db.user.0=nacos -# db.password.0=nacos +# db.user=nacos +# db.password=nacos #*************** Naming Module Related Configurations ***************# ### Data dispatch task execution period in milliseconds: -# nacos.naming.distro.taskDispatchPeriod=200 - -### Data count of batch sync task: -# nacos.naming.distro.batchSyncKeyCount=1000 - -### Retry delay in milliseconds if sync task failed: -# nacos.naming.distro.syncRetryDelay=5000 ### If enable data warmup. If set to false, the server would accept request without local data preparation: # nacos.naming.data.warmup=true @@ -104,11 +98,8 @@ server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Reque ### The directory of access log: server.tomcat.basedir=file:. -#spring.datasource.platform=mysql -#db.num=1 -#db.url.0=jdbc:mysql://10.101.167.27:3306/acm?characterEncoding=utf8&connectTimeout=1000&socketTimeout=10000&autoReconnect=true -#db.user=root -#db.password=root + + #*************** Access Control Related Configurations ***************# ### If enable spring security, this option is deprecated in 1.2.0: #spring.security.enabled=false @@ -145,6 +136,7 @@ nacos.core.auth.plugin.nacos.token.secret.key=SecretKey0123456789012345678901234 #nacos.core.auth.ldap.userDn=cn=admin,${nacos.core.auth.ldap.basedc} #nacos.core.auth.ldap.password=admin #nacos.core.auth.ldap.userdn=cn={0},dc=example,dc=org +#nacos.core.auth.ldap.filter.prefix=uid #*************** Istio Related Configurations ***************# diff --git a/console/src/main/resources/static/index.html b/console/src/main/resources/static/index.html index f9fbcbb13..946b143da 100644 --- a/console/src/main/resources/static/index.html +++ b/console/src/main/resources/static/index.html @@ -35,7 +35,7 @@ - +
@@ -56,6 +56,6 @@ - + diff --git a/console/src/main/resources/static/js/main.js b/console/src/main/resources/static/js/main.js index be3c268b5..381aa917b 100644 --- a/console/src/main/resources/static/js/main.js +++ b/console/src/main/resources/static/js/main.js @@ -1,292 +1,292 @@ -!function(n){var a={};function r(e){if(a[e])return a[e].exports;var t=a[e]={i:e,l:!1,exports:{}};return n[e].call(t.exports,t,t.exports,r),t.l=!0,t.exports}r.m=n,r.c=a,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var a in t)r.d(n,a,function(e){return t[e]}.bind(null,a));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=443)}([function(e,t,n){"use strict";e.exports=n(450)},function(e,t,n){"use strict";n.d(t,"a",function(){return E}),n.d(t,"c",function(){return x}),n.d(t,"b",function(){return C});n(52);var t=n(27),u=n.n(t),s=n(71),r=n(88),d=n(60),c=n(31),t=n(105),f=n.n(t),t=n(64),p=n.n(t);function h(){var e=window.location.href,e=(localStorage.removeItem("token"),e.split("#")[0]);console.log("base_url",e),window.location="".concat(e,"#/login")}var l,m,a,o,i,g,y,v,_,b,w,M,n=window,k=(l={},{once:function(e,t){this.listen.call(this,e,t,!0)},listen:function(e,t){var n=2>>0,a;for(a=0;a0)for(n=0;n=0;return(o?n?"+":"":"-")+Math.pow(10,Math.max(0,r)).toString().substr(1)+a}var ie=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,le=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,se={},ue={};function a(e,t,n,a){var r=a;if(typeof a==="string")r=function(){return this[a]()};if(e)ue[e]=r;if(t)ue[t[0]]=function(){return o(r.apply(this,arguments),t[1],t[2])};if(n)ue[n]=function(){return this.localeData().ordinal(r.apply(this,arguments),e)}}function de(e){if(e.match(/\[[\s\S]/))return e.replace(/^\[|\]$/g,"");return e.replace(/\\/g,"")}function ce(a){var r=a.match(ie),e,o;for(e=0,o=r.length;e=0&&le.test(e)){e=e.replace(le,a);le.lastIndex=0;n-=1}return e}var he={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"};function me(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];if(t||!n)return t;this._longDateFormat[e]=n.match(ie).map(function(e){if(e==="MMMM"||e==="MM"||e==="DD"||e==="dddd")return e.slice(1);return e}).join("");return this._longDateFormat[e]}var ge="Invalid date";function ye(){return this._invalidDate}var ve="%d",_e=/\d{1,2}/;function be(e){return this._ordinal.replace("%d",e)}var we={future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",w:"a week",ww:"%d weeks",M:"a month",MM:"%d months",y:"a year",yy:"%d years"};function Me(e,t,n,a){var r=this._relativeTime[n];return h(r)?r(e,t,n,a):r.replace(/%d/i,e)}function ke(e,t){var n=this._relativeTime[e>0?"future":"past"];return h(n)?n(t):n.replace(/%s/i,t)}var Se={};function t(e,t){var n=e.toLowerCase();Se[n]=Se[n+"s"]=Se[t]=e}function m(e){return typeof e==="string"?Se[e]||Se[e.toLowerCase()]:undefined}function Ee(e){var t={},n,a;for(a in e)if(s(e,a)){n=m(a);if(n)t[n]=e[a]}return t}var xe={};function n(e,t){xe[e]=t}function Ce(e){var t=[],n;for(n in e)if(s(e,n))t.push({unit:n,priority:xe[n]});t.sort(function(e,t){return e.priority-t.priority});return t}function Le(e){return e%4===0&&e%100!==0||e%400===0}function g(e){if(e<0)return Math.ceil(e)||0;else return Math.floor(e)}function y(e){var t=+e,n=0;if(t!==0&&isFinite(t))n=g(t);return n}function Te(t,n){return function(e){if(e!=null){Oe(this,t,e);c.updateOffset(this,n);return this}else return De(this,t)}}function De(e,t){return e.isValid()?e._d["get"+(e._isUTC?"UTC":"")+t]():NaN}function Oe(e,t,n){if(e.isValid()&&!isNaN(n))if(t==="FullYear"&&Le(e.year())&&e.month()===1&&e.date()===29){n=y(n);e._d["set"+(e._isUTC?"UTC":"")+t](n,e.month(),ot(n,e.month()))}else e._d["set"+(e._isUTC?"UTC":"")+t](n)}function Ne(e){e=m(e);if(h(this[e]))return this[e]();return this}function Pe(e,t){if(typeof e==="object"){e=Ee(e);var n=Ce(e),a,r=n.length;for(a=0;a68?1900:2e3)};var Mt=Te("FullYear",true);function kt(){return Le(this.year())}function St(e,t,n,a,r,o,i){var l;if(e<100&&e>=0){l=new Date(e+400,t,n,a,r,o,i);if(isFinite(l.getFullYear()))l.setFullYear(e)}else l=new Date(e,t,n,a,r,o,i);return l}function Et(e){var t,n;if(e<100&&e>=0){n=Array.prototype.slice.call(arguments);n[0]=e+400;t=new Date(Date.UTC.apply(null,n));if(isFinite(t.getUTCFullYear()))t.setUTCFullYear(e)}else t=new Date(Date.UTC.apply(null,arguments));return t}function xt(e,t,n){var a=7+t-n,r=(7+Et(e,0,a).getUTCDay()-t)%7;return-r+a-1}function Ct(e,t,n,a,r){var o=(7+n-a)%7,i=xt(e,a,r),l=1+7*(t-1)+o+i,s,u;if(l<=0){s=e-1;u=wt(s)+l}else if(l>wt(e)){s=e+1;u=l-wt(e)}else{s=e;u=l}return{year:s,dayOfYear:u}}function Lt(e,t,n){var a=xt(e.year(),t,n),r=Math.floor((e.dayOfYear()-a-1)/7)+1,o,i;if(r<1){i=e.year()-1;o=r+T(i,t,n)}else if(r>T(e.year(),t,n)){o=r-T(e.year(),t,n);i=e.year()+1}else{i=e.year();o=r}return{week:o,year:i}}function T(e,t,n){var a=xt(e,t,n),r=xt(e+1,t,n);return(wt(e)-a+r)/7}function Tt(e){return Lt(e,this._week.dow,this._week.doy).week}a("w",["ww",2],"wo","week"),a("W",["WW",2],"Wo","isoWeek"),t("week","w"),t("isoWeek","W"),n("week",5),n("isoWeek",5),_("w",v),_("ww",v,r),_("W",v),_("WW",v,r),Ze(["w","ww","W","WW"],function(e,t,n,a){t[a.substr(0,1)]=y(e)});var Dt={dow:0,doy:6};function Ot(){return this._week.dow}function Nt(){return this._week.doy}function Pt(e){var t=this.localeData().week(this);return e==null?t:this.add((e-t)*7,"d")}function jt(e){var t=Lt(this,1,4).week;return e==null?t:this.add((e-t)*7,"d")}function Yt(e,t){if(typeof e!=="string")return e;if(!isNaN(e))return parseInt(e,10);e=t.weekdaysParse(e);if(typeof e==="number")return e;return null}function It(e,t){if(typeof e==="string")return t.weekdaysParse(e)%7||7;return isNaN(e)?null:e}function Rt(e,t){return e.slice(t,7).concat(e.slice(0,t))}a("d",0,"do","day"),a("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),a("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),a("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),a("e",0,0,"weekday"),a("E",0,0,"isoWeekday"),t("day","d"),t("weekday","e"),t("isoWeekday","E"),n("day",11),n("weekday",11),n("isoWeekday",11),_("d",v),_("e",v),_("E",v),_("dd",function(e,t){return t.weekdaysMinRegex(e)}),_("ddd",function(e,t){return t.weekdaysShortRegex(e)}),_("dddd",function(e,t){return t.weekdaysRegex(e)}),Ze(["dd","ddd","dddd"],function(e,t,n,a){var r=n._locale.weekdaysParse(e,a,n._strict);if(r!=null)t.d=r;else f(n).invalidWeekday=e}),Ze(["d","e","E"],function(e,t,n,a){t[a]=y(e)});var At="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Ht="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Ft="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),zt=qe,Wt=qe,Vt=qe;function Bt(e,t){var n=i(this._weekdays)?this._weekdays:this._weekdays[e&&e!==true&&this._weekdays.isFormat.test(t)?"format":"standalone"];return e===true?Rt(n,this._week.dow):e?n[e.day()]:n}function Ut(e){return e===true?Rt(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort}function Kt(e){return e===true?Rt(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin}function Gt(e,t,n){var a,r,o,i=e.toLocaleLowerCase();if(!this._weekdaysParse){this._weekdaysParse=[];this._shortWeekdaysParse=[];this._minWeekdaysParse=[];for(a=0;a<7;++a){o=d([2e3,1]).day(a);this._minWeekdaysParse[a]=this.weekdaysMin(o,"").toLocaleLowerCase();this._shortWeekdaysParse[a]=this.weekdaysShort(o,"").toLocaleLowerCase();this._weekdaysParse[a]=this.weekdays(o,"").toLocaleLowerCase()}}if(n)if(t==="dddd"){r=L.call(this._weekdaysParse,i);return r!==-1?r:null}else if(t==="ddd"){r=L.call(this._shortWeekdaysParse,i);return r!==-1?r:null}else{r=L.call(this._minWeekdaysParse,i);return r!==-1?r:null}else if(t==="dddd"){r=L.call(this._weekdaysParse,i);if(r!==-1)return r;r=L.call(this._shortWeekdaysParse,i);if(r!==-1)return r;r=L.call(this._minWeekdaysParse,i);return r!==-1?r:null}else if(t==="ddd"){r=L.call(this._shortWeekdaysParse,i);if(r!==-1)return r;r=L.call(this._weekdaysParse,i);if(r!==-1)return r;r=L.call(this._minWeekdaysParse,i);return r!==-1?r:null}else{r=L.call(this._minWeekdaysParse,i);if(r!==-1)return r;r=L.call(this._weekdaysParse,i);if(r!==-1)return r;r=L.call(this._shortWeekdaysParse,i);return r!==-1?r:null}}function qt(e,t,n){var a,r,o;if(this._weekdaysParseExact)return Gt.call(this,e,t,n);if(!this._weekdaysParse){this._weekdaysParse=[];this._minWeekdaysParse=[];this._shortWeekdaysParse=[];this._fullWeekdaysParse=[]}for(a=0;a<7;a++){r=d([2e3,1]).day(a);if(n&&!this._fullWeekdaysParse[a]){this._fullWeekdaysParse[a]=new RegExp("^"+this.weekdays(r,"").replace(".","\\.?")+"$","i");this._shortWeekdaysParse[a]=new RegExp("^"+this.weekdaysShort(r,"").replace(".","\\.?")+"$","i");this._minWeekdaysParse[a]=new RegExp("^"+this.weekdaysMin(r,"").replace(".","\\.?")+"$","i")}if(!this._weekdaysParse[a]){o="^"+this.weekdays(r,"")+"|^"+this.weekdaysShort(r,"")+"|^"+this.weekdaysMin(r,"");this._weekdaysParse[a]=new RegExp(o.replace(".",""),"i")}if(n&&t==="dddd"&&this._fullWeekdaysParse[a].test(e))return a;else if(n&&t==="ddd"&&this._shortWeekdaysParse[a].test(e))return a;else if(n&&t==="dd"&&this._minWeekdaysParse[a].test(e))return a;else if(!n&&this._weekdaysParse[a].test(e))return a}}function $t(e){if(!this.isValid())return e!=null?this:NaN;var t=this._isUTC?this._d.getUTCDay():this._d.getDay();if(e!=null){e=Yt(e,this.localeData());return this.add(e-t,"d")}else return t}function Jt(e){if(!this.isValid())return e!=null?this:NaN;var t=(this.day()+7-this.localeData()._week.dow)%7;return e==null?t:this.add(e-t,"d")}function Xt(e){if(!this.isValid())return e!=null?this:NaN;if(e!=null){var t=It(e,this.localeData());return this.day(this.day()%7?t:t-7)}else return this.day()||7}function Qt(e){if(this._weekdaysParseExact){if(!s(this,"_weekdaysRegex"))tn.call(this);if(e)return this._weekdaysStrictRegex;else return this._weekdaysRegex}else{if(!s(this,"_weekdaysRegex"))this._weekdaysRegex=zt;return this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex}}function Zt(e){if(this._weekdaysParseExact){if(!s(this,"_weekdaysRegex"))tn.call(this);if(e)return this._weekdaysShortStrictRegex;else return this._weekdaysShortRegex}else{if(!s(this,"_weekdaysShortRegex"))this._weekdaysShortRegex=Wt;return this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex}}function en(e){if(this._weekdaysParseExact){if(!s(this,"_weekdaysRegex"))tn.call(this);if(e)return this._weekdaysMinStrictRegex;else return this._weekdaysMinRegex}else{if(!s(this,"_weekdaysMinRegex"))this._weekdaysMinRegex=Vt;return this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex}}function tn(){function e(e,t){return t.length-e.length}var t=[],n=[],a=[],r=[],o,i,l,s,u;for(o=0;o<7;o++){i=d([2e3,1]).day(o);l=b(this.weekdaysMin(i,""));s=b(this.weekdaysShort(i,""));u=b(this.weekdays(i,""));t.push(l);n.push(s);a.push(u);r.push(l);r.push(s);r.push(u)}t.sort(e);n.sort(e);a.sort(e);r.sort(e);this._weekdaysRegex=new RegExp("^("+r.join("|")+")","i");this._weekdaysShortRegex=this._weekdaysRegex;this._weekdaysMinRegex=this._weekdaysRegex;this._weekdaysStrictRegex=new RegExp("^("+a.join("|")+")","i");this._weekdaysShortStrictRegex=new RegExp("^("+n.join("|")+")","i");this._weekdaysMinStrictRegex=new RegExp("^("+t.join("|")+")","i")}function nn(){return this.hours()%12||12}function an(){return this.hours()||24}function rn(e,t){a(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function on(e,t){return t._meridiemParse}function ln(e){return(e+"").toLowerCase().charAt(0)==="p"}a("H",["HH",2],0,"hour"),a("h",["hh",2],0,nn),a("k",["kk",2],0,an),a("hmm",0,0,function(){return""+nn.apply(this)+o(this.minutes(),2)}),a("hmmss",0,0,function(){return""+nn.apply(this)+o(this.minutes(),2)+o(this.seconds(),2)}),a("Hmm",0,0,function(){return""+this.hours()+o(this.minutes(),2)}),a("Hmmss",0,0,function(){return""+this.hours()+o(this.minutes(),2)+o(this.seconds(),2)}),rn("a",true),rn("A",false),t("hour","h"),n("hour",13),_("a",on),_("A",on),_("H",v),_("h",v),_("k",v),_("HH",v,r),_("hh",v,r),_("kk",v,r),_("hmm",Ae),_("hmmss",He),_("Hmm",Ae),_("Hmmss",He),w(["H","HH"],E),w(["k","kk"],function(e,t,n){var a=y(e);t[E]=a===24?0:a}),w(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e);n._meridiem=e}),w(["h","hh"],function(e,t,n){t[E]=y(e);f(n).bigHour=true}),w("hmm",function(e,t,n){var a=e.length-2;t[E]=y(e.substr(0,a));t[x]=y(e.substr(a));f(n).bigHour=true}),w("hmmss",function(e,t,n){var a=e.length-4,r=e.length-2;t[E]=y(e.substr(0,a));t[x]=y(e.substr(a,2));t[C]=y(e.substr(r));f(n).bigHour=true}),w("Hmm",function(e,t,n){var a=e.length-2;t[E]=y(e.substr(0,a));t[x]=y(e.substr(a))}),w("Hmmss",function(e,t,n){var a=e.length-4,r=e.length-2;t[E]=y(e.substr(0,a));t[x]=y(e.substr(a,2));t[C]=y(e.substr(r))});var sn,un=Te("Hours",true);function dn(e,t,n){if(e>11)return n?"pm":"PM";else return n?"am":"AM"}var cn={calendar:re,longDateFormat:he,invalidDate:ge,ordinal:ve,dayOfMonthOrdinalParse:_e,relativeTime:we,months:it,monthsShort:lt,week:Dt,weekdays:At,weekdaysMin:Ft,weekdaysShort:Ht,meridiemParse:/[ap]\.?m?\.?/i},D={},fn={},pn;function hn(e,t){var n,a=Math.min(e.length,t.length);for(n=0;n0){r=vn(o.slice(0,n).join("-"));if(r)return r;if(a&&a.length>=n&&hn(o,a)>=n-1)break;n--}t++}return pn}function yn(e){return e.match("^[^/\\\\]*$")!=null}function vn(t){var e=null,n;if(D[t]===undefined&&typeof di!=="undefined"&&di&&di.exports&&yn(t))try{e=pn._abbr;n=ci;fi(517)("./"+t);_n(e)}catch(e){D[t]=null}return D[t]}function _n(e,t){var n;if(e){if(l(t))n=Mn(e);else n=bn(e,t);if(n)pn=n;else if(typeof console!=="undefined"&&console.warn)console.warn("Locale "+e+" not found. Did you forget to load it?")}return pn._abbr}function bn(e,t){if(t!==null){var n,a=cn;t.abbr=e;if(D[e]!=null){ee("defineLocaleOverride","use moment.updateLocale(localeName, config) to change "+"an existing locale. moment.defineLocale(localeName, "+"config) should only be used for creating a new locale "+"See http://momentjs.com/guides/#/warnings/define-locale/ for more info.");a=D[e]._config}else if(t.parentLocale!=null)if(D[t.parentLocale]!=null)a=D[t.parentLocale]._config;else{n=vn(t.parentLocale);if(n!=null)a=n._config;else{if(!fn[t.parentLocale])fn[t.parentLocale]=[];fn[t.parentLocale].push({name:e,config:t});return null}}D[e]=new ae(ne(a,t));if(fn[e])fn[e].forEach(function(e){bn(e.name,e.config)});_n(e);return D[e]}else{delete D[e];return null}}function wn(e,t){if(t!=null){var n,a,r=cn;if(D[e]!=null&&D[e].parentLocale!=null)D[e].set(ne(D[e]._config,t));else{a=vn(e);if(a!=null)r=a._config;t=ne(r,t);if(a==null)t.abbr=e;n=new ae(t);n.parentLocale=D[e];D[e]=n}_n(e)}else if(D[e]!=null)if(D[e].parentLocale!=null){D[e]=D[e].parentLocale;if(e===_n())_n(e)}else if(D[e]!=null)delete D[e];return D[e]}function Mn(e){var t;if(e&&e._locale&&e._locale._abbr)e=e._locale._abbr;if(!e)return pn;if(!i(e)){t=vn(e);if(t)return t;e=[e]}return gn(e)}function kn(){return Z(D)}function Sn(e){var t,n=e._a;if(n&&f(e).overflow===-2){t=n[k]<0||n[k]>11?k:n[S]<1||n[S]>ot(n[M],n[k])?S:n[E]<0||n[E]>24||n[E]===24&&(n[x]!==0||n[C]!==0||n[tt]!==0)?E:n[x]<0||n[x]>59?x:n[C]<0||n[C]>59?C:n[tt]<0||n[tt]>999?tt:-1;if(f(e)._overflowDayOfYear&&(tS))t=S;if(f(e)._overflowWeeks&&t===-1)t=nt;if(f(e)._overflowWeekday&&t===-1)t=at;f(e).overflow=t}return e}var En=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,xn=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Cn=/Z|[+-]\d\d(?::?\d\d)?/,Ln=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,false],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,false],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,false],["YYYYDDD",/\d{7}/],["YYYYMM",/\d{6}/,false],["YYYY",/\d{4}/,false]],Tn=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Dn=/^\/?Date\((-?\d+)/i,On=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/,Nn={UT:0,GMT:0,EDT:-4*60,EST:-5*60,CDT:-5*60,CST:-6*60,MDT:-6*60,MST:-7*60,PDT:-7*60,PST:-8*60};function Pn(e){var t,n,a=e._i,r=En.exec(a)||xn.exec(a),o,i,l,s,u=Ln.length,d=Tn.length;if(r){f(e).iso=true;for(t=0,n=u;twt(i)||e._dayOfYear===0)f(e)._overflowDayOfYear=true;n=Et(i,0,e._dayOfYear);e._a[k]=n.getUTCMonth();e._a[S]=n.getUTCDate()}for(t=0;t<3&&e._a[t]==null;++t)e._a[t]=a[t]=r[t];for(;t<7;t++)e._a[t]=a[t]=e._a[t]==null?t===2?1:0:e._a[t];if(e._a[E]===24&&e._a[x]===0&&e._a[C]===0&&e._a[tt]===0){e._nextDay=true;e._a[E]=0}e._d=(e._useUTC?Et:St).apply(null,a);o=e._useUTC?e._d.getUTCDay():e._d.getDay();if(e._tzm!=null)e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm);if(e._nextDay)e._a[E]=24;if(e._w&&typeof e._w.d!=="undefined"&&e._w.d!==o)f(e).weekdayMismatch=true}function Bn(e){var t,n,a,r,o,i,l,s,u;t=e._w;if(t.GG!=null||t.W!=null||t.E!=null){o=1;i=4;n=zn(t.GG,e._a[M],Lt(O(),1,4).year);a=zn(t.W,1);r=zn(t.E,1);if(r<1||r>7)s=true}else{o=e._locale._week.dow;i=e._locale._week.doy;u=Lt(O(),o,i);n=zn(t.gg,e._a[M],u.year);a=zn(t.w,u.week);if(t.d!=null){r=t.d;if(r<0||r>6)s=true}else if(t.e!=null){r=t.e+o;if(t.e<0||t.e>6)s=true}else r=o}if(a<1||a>T(n,o,i))f(e)._overflowWeeks=true;else if(s!=null)f(e)._overflowWeekday=true;else{l=Ct(n,a,r,o,i);e._a[M]=l.year;e._dayOfYear=l.dayOfYear}}function Un(e){if(e._f===c.ISO_8601){Pn(e);return}if(e._f===c.RFC_2822){Hn(e);return}e._a=[];f(e).empty=true;var t=""+e._i,n,a,r,o,i,l=t.length,s=0,u,d;r=pe(e._f,e._locale).match(ie)||[];d=r.length;for(n=0;n0)f(e).unusedInput.push(i);t=t.slice(t.indexOf(a)+a.length);s+=a.length}if(ue[o]){if(a)f(e).empty=false;else f(e).unusedTokens.push(o);et(o,a,e)}else if(e._strict&&!a)f(e).unusedTokens.push(o)}f(e).charsLeftOver=l-s;if(t.length>0)f(e).unusedInput.push(t);if(e._a[E]<=12&&f(e).bigHour===true&&e._a[E]>0)f(e).bigHour=undefined;f(e).parsedDateParts=e._a.slice(0);f(e).meridiem=e._meridiem;e._a[E]=Kn(e._locale,e._a[E],e._meridiem);u=f(e).era;if(u!==null)e._a[M]=e._locale.erasConvertYear(u,e._a[M]);Vn(e);Sn(e)}function Kn(e,t,n){var a;if(n==null)return t;if(e.meridiemHour!=null)return e.meridiemHour(t,n);else if(e.isPM!=null){a=e.isPM(n);if(a&&t<12)t+=12;if(!a&&t===12)t=0;return t}else return t}function Gn(e){var t,n,a,r,o,i,l=false,s=e._f.length;if(s===0){f(e).invalidFormat=true;e._d=new Date(NaN);return}for(r=0;rthis?this:e;else return K()});function ta(e,t){var n,a;if(t.length===1&&i(t[0]))t=t[0];if(!t.length)return O();n=t[0];for(a=1;athis.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Ea(){if(!l(this._isDSTShifted))return this._isDSTShifted;var e={},t;$(e,this);e=Jn(e);if(e._a){t=e._isUTC?d(e._a):O(e._a);this._isDSTShifted=this.isValid()&&fa(e._a,t.toArray())>0}else this._isDSTShifted=false;return this._isDSTShifted}function xa(){return this.isValid()?!this._isUTC:false}function Ca(){return this.isValid()?this._isUTC:false}function La(){return this.isValid()?this._isUTC&&this._offset===0:false}c.updateOffset=function(){};var Ta=/^(-|\+)?(?:(\d*)[. ])?(\d+):(\d+)(?::(\d+)(\.\d*)?)?$/,Da=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function N(e,t){var n=e,a=null,r,o,i;if(da(e))n={ms:e._milliseconds,d:e._days,M:e._months};else if(u(e)||!isNaN(+e)){n={};if(t)n[t]=+e;else n.milliseconds=+e}else if(a=Ta.exec(e)){r=a[1]==="-"?-1:1;n={y:0,d:y(a[S])*r,h:y(a[E])*r,m:y(a[x])*r,s:y(a[C])*r,ms:y(ca(a[tt]*1e3))*r}}else if(a=Da.exec(e)){r=a[1]==="-"?-1:1;n={y:Oa(a[2],r),M:Oa(a[3],r),w:Oa(a[4],r),d:Oa(a[5],r),h:Oa(a[6],r),m:Oa(a[7],r),s:Oa(a[8],r)}}else if(n==null)n={};else if(typeof n==="object"&&("from"in n||"to"in n)){i=Pa(O(n.from),O(n.to));n={};n.ms=i.milliseconds;n.M=i.months}o=new ua(n);if(da(e)&&s(e,"_locale"))o._locale=e._locale;if(da(e)&&s(e,"_isValid"))o._isValid=e._isValid;return o}function Oa(e,t){var n=e&&parseFloat(e.replace(",","."));return(isNaN(n)?0:n)*t}function Na(e,t){var n={};n.months=t.month()-e.month()+(t.year()-e.year())*12;if(e.clone().add(n.months,"M").isAfter(t))--n.months;n.milliseconds=+t-+e.clone().add(n.months,"M");return n}function Pa(e,t){var n;if(!(e.isValid()&&t.isValid()))return{milliseconds:0,months:0};t=ga(t,e);if(e.isBefore(t))n=Na(e,t);else{n=Na(t,e);n.milliseconds=-n.milliseconds;n.months=-n.months}return n}function ja(r,o){return function(e,t){var n,a;if(t!==null&&!isNaN(+t)){ee(o,"moment()."+o+"(period, number) is deprecated. Please use moment()."+o+"(number, period). "+"See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.");a=e;e=t;t=a}n=N(e,t);Ya(this,n,r);return this}}function Ya(e,t,n,a){var r=t._milliseconds,o=ca(t._days),i=ca(t._months);if(!e.isValid())return;a=a==null?true:a;if(i)mt(e,De(e,"Month")+i*n);if(o)Oe(e,"Date",De(e,"Date")+o*n);if(r)e._d.setTime(e._d.valueOf()+r*n);if(a)c.updateOffset(e,o||i)}N.fn=ua.prototype,N.invalid=sa;var Ia=ja(1,"add"),Ra=ja(-1,"subtract");function Aa(e){return typeof e==="string"||e instanceof String}function Ha(e){return p(e)||z(e)||Aa(e)||u(e)||za(e)||Fa(e)||e===null||e===undefined}function Fa(e){var t=H(e)&&!F(e),n=false,a=["years","year","y","months","month","M","days","day","d","dates","date","D","hours","hour","h","minutes","minute","m","seconds","second","s","milliseconds","millisecond","ms"],r,o,i=a.length;for(r=0;rn.valueOf();else return n.valueOf()9999)return fe(n,t?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ");if(h(Date.prototype.toISOString))if(t)return this.toDate().toISOString();else return new Date(this.valueOf()+this.utcOffset()*60*1e3).toISOString().replace("Z",fe(n,"Z"));return fe(n,t?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")}function nr(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var e="moment",t="",n,a,r,o;if(!this.isLocal()){e=this.utcOffset()===0?"moment.utc":"moment.parseZone";t="Z"}n="["+e+'("]';a=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY";r="-MM-DD[T]HH:mm:ss.SSS";o=t+'[")]';return this.format(n+a+r+o)}function ar(e){if(!e)e=this.isUtc()?c.defaultFormatUtc:c.defaultFormat;var t=fe(this,e);return this.localeData().postformat(t)}function rr(e,t){if(this.isValid()&&(p(e)&&e.isValid()||O(e).isValid()))return N({to:this,from:e}).locale(this.locale()).humanize(!t);else return this.localeData().invalidDate()}function or(e){return this.from(O(),e)}function ir(e,t){if(this.isValid()&&(p(e)&&e.isValid()||O(e).isValid()))return N({from:this,to:e}).locale(this.locale()).humanize(!t);else return this.localeData().invalidDate()}function lr(e){return this.to(O(),e)}function sr(e){var t;if(e===undefined)return this._locale._abbr;else{t=Mn(e);if(t!=null)this._locale=t;return this}}c.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",c.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var ur=e("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(e){if(e===undefined)return this.localeData();else return this.locale(e)});function dr(){return this._locale}var cr=1e3,fr=60*cr,pr=60*fr,hr=(365*400+97)*24*pr;function mr(e,t){return(e%t+t)%t}function gr(e,t,n){if(e<100&&e>=0)return new Date(e+400,t,n)-hr;else return new Date(e,t,n).valueOf()}function yr(e,t,n){if(e<100&&e>=0)return Date.UTC(e+400,t,n)-hr;else return Date.UTC(e,t,n)}function vr(e){var t,n;e=m(e);if(e===undefined||e==="millisecond"||!this.isValid())return this;n=this._isUTC?yr:gr;switch(e){case"year":t=n(this.year(),0,1);break;case"quarter":t=n(this.year(),this.month()-this.month()%3,1);break;case"month":t=n(this.year(),this.month(),1);break;case"week":t=n(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":t=n(this.year(),this.month(),this.date());break;case"hour":t=this._d.valueOf();t-=mr(t+(this._isUTC?0:this.utcOffset()*fr),pr);break;case"minute":t=this._d.valueOf();t-=mr(t,fr);break;case"second":t=this._d.valueOf();t-=mr(t,cr);break}this._d.setTime(t);c.updateOffset(this,true);return this}function _r(e){var t,n;e=m(e);if(e===undefined||e==="millisecond"||!this.isValid())return this;n=this._isUTC?yr:gr;switch(e){case"year":t=n(this.year()+1,0,1)-1;break;case"quarter":t=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":t=n(this.year(),this.month()+1,1)-1;break;case"week":t=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":t=n(this.year(),this.month(),this.date()+1)-1;break;case"hour":t=this._d.valueOf();t+=pr-mr(t+(this._isUTC?0:this.utcOffset()*fr),pr)-1;break;case"minute":t=this._d.valueOf();t+=fr-mr(t,fr)-1;break;case"second":t=this._d.valueOf();t+=cr-mr(t,cr)-1;break}this._d.setTime(t);c.updateOffset(this,true);return this}function br(){return this._d.valueOf()-(this._offset||0)*6e4}function wr(){return Math.floor(this.valueOf()/1e3)}function Mr(){return new Date(this.valueOf())}function kr(){var e=this;return[e.year(),e.month(),e.date(),e.hour(),e.minute(),e.second(),e.millisecond()]}function Sr(){var e=this;return{years:e.year(),months:e.month(),date:e.date(),hours:e.hours(),minutes:e.minutes(),seconds:e.seconds(),milliseconds:e.milliseconds()}}function Er(){return this.isValid()?this.toISOString():null}function xr(){return U(this)}function Cr(){return V({},f(this))}function Lr(){return f(this).overflow}function Tr(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}}function Dr(e,t){var n,a,r,o=this._eras||Mn("en")._eras;for(n=0,a=o.length;n=0)return o[a]}}function Nr(e,t){var n=e.since<=e.until?+1:-1;if(t===undefined)return c(e.since).year();else return c(e.since).year()+(t-e.offset)*n}function Pr(){var e,t,n,a=this.localeData().eras();for(e=0,t=a.length;eo)t=o;return Zr.call(this,e,t,n,a,r)}}function Zr(e,t,n,a,r){var o=Ct(e,t,n,a,r),i=Et(o.year,0,o.dayOfYear);this.year(i.getUTCFullYear());this.month(i.getUTCMonth());this.date(i.getUTCDate());return this}function eo(e){return e==null?Math.ceil((this.month()+1)/3):this.month((e-1)*3+this.month()%3)}a("N",0,0,"eraAbbr"),a("NN",0,0,"eraAbbr"),a("NNN",0,0,"eraAbbr"),a("NNNN",0,0,"eraName"),a("NNNNN",0,0,"eraNarrow"),a("y",["y",1],"yo","eraYear"),a("y",["yy",2],0,"eraYear"),a("y",["yyy",3],0,"eraYear"),a("y",["yyyy",4],0,"eraYear"),_("N",Fr),_("NN",Fr),_("NNN",Fr),_("NNNN",zr),_("NNNNN",Wr),w(["N","NN","NNN","NNNN","NNNNN"],function(e,t,n,a){var r=n._locale.erasParse(e,a,n._strict);if(r)f(n).era=r;else f(n).invalidEra=e}),_("y",Ve),_("yy",Ve),_("yyy",Ve),_("yyyy",Ve),_("yo",Vr),w(["y","yy","yyy","yyyy"],M),w(["yo"],function(e,t,n,a){var r;if(n._locale._eraYearOrdinalRegex)r=e.match(n._locale._eraYearOrdinalRegex);if(n._locale.eraYearOrdinalParse)t[M]=n._locale.eraYearOrdinalParse(e,r);else t[M]=parseInt(e,10)}),a(0,["gg",2],0,function(){return this.weekYear()%100}),a(0,["GG",2],0,function(){return this.isoWeekYear()%100}),Ur("gggg","weekYear"),Ur("ggggg","weekYear"),Ur("GGGG","isoWeekYear"),Ur("GGGGG","isoWeekYear"),t("weekYear","gg"),t("isoWeekYear","GG"),n("weekYear",1),n("isoWeekYear",1),_("G",Be),_("g",Be),_("GG",v,r),_("gg",v,r),_("GGGG",ze,Ie),_("gggg",ze,Ie),_("GGGGG",We,Re),_("ggggg",We,Re),Ze(["gggg","ggggg","GGGG","GGGGG"],function(e,t,n,a){t[a.substr(0,2)]=y(e)}),Ze(["gg","GG"],function(e,t,n,a){t[a]=c.parseTwoDigitYear(e)}),a("Q",0,"Qo","quarter"),t("quarter","Q"),n("quarter",7),_("Q",je),w("Q",function(e,t){t[k]=(y(e)-1)*3}),a("D",["DD",2],"Do","date"),t("date","D"),n("date",9),_("D",v),_("DD",v,r),_("Do",function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient}),w(["D","DD"],S),w("Do",function(e,t){t[S]=y(e.match(v)[0])});var to=Te("Date",true);function no(e){var t=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return e==null?t:this.add(e-t,"d")}a("DDD",["DDDD",3],"DDDo","dayOfYear"),t("dayOfYear","DDD"),n("dayOfYear",4),_("DDD",Fe),_("DDDD",Ye),w(["DDD","DDDD"],function(e,t,n){n._dayOfYear=y(e)}),a("m",["mm",2],0,"minute"),t("minute","m"),n("minute",14),_("m",v),_("mm",v,r),w(["m","mm"],x);var ao=Te("Minutes",false),ro=(a("s",["ss",2],0,"second"),t("second","s"),n("second",15),_("s",v),_("ss",v,r),w(["s","ss"],C),Te("Seconds",false)),oo,io;for(a("S",0,0,function(){return~~(this.millisecond()/100)}),a(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),a(0,["SSS",3],0,"millisecond"),a(0,["SSSS",4],0,function(){return this.millisecond()*10}),a(0,["SSSSS",5],0,function(){return this.millisecond()*100}),a(0,["SSSSSS",6],0,function(){return this.millisecond()*1e3}),a(0,["SSSSSSS",7],0,function(){return this.millisecond()*1e4}),a(0,["SSSSSSSS",8],0,function(){return this.millisecond()*1e5}),a(0,["SSSSSSSSS",9],0,function(){return this.millisecond()*1e6}),t("millisecond","ms"),n("millisecond",16),_("S",Fe,je),_("SS",Fe,r),_("SSS",Fe,Ye),oo="SSSS";oo.length<=9;oo+="S")_(oo,Ve);function lo(e,t){t[tt]=y(("0."+e)*1e3)}for(oo="S";oo.length<=9;oo+="S")w(oo,lo);function so(){return this._isUTC?"UTC":""}function uo(){return this._isUTC?"Coordinated Universal Time":""}io=Te("Milliseconds",false),a("z",0,0,"zoneAbbr"),a("zz",0,0,"zoneName");var P=J.prototype;if(P.add=Ia,P.calendar=Ba,P.clone=Ua,P.diff=Qa,P.endOf=_r,P.format=ar,P.from=rr,P.fromNow=or,P.to=ir,P.toNow=lr,P.get=Ne,P.invalidAt=Lr,P.isAfter=Ka,P.isBefore=Ga,P.isBetween=qa,P.isSame=$a,P.isSameOrAfter=Ja,P.isSameOrBefore=Xa,P.isValid=xr,P.lang=ur,P.locale=sr,P.localeData=dr,P.max=ea,P.min=Zn,P.parsingFlags=Cr,P.set=Pe,P.startOf=vr,P.subtract=Ra,P.toArray=kr,P.toObject=Sr,P.toDate=Mr,P.toISOString=tr,P.inspect=nr,typeof Symbol!=="undefined"&&Symbol.for!=null)P[Symbol.for("nodejs.util.inspect.custom")]=function(){return"Moment<"+this.format()+">"};function co(e){return O(e*1e3)}function fo(){return O.apply(null,arguments).parseZone()}function po(e){return e}P.toJSON=Er,P.toString=er,P.unix=wr,P.valueOf=br,P.creationData=Tr,P.eraName=Pr,P.eraNarrow=jr,P.eraAbbr=Yr,P.eraYear=Ir,P.year=Mt,P.isLeapYear=kt,P.weekYear=Kr,P.isoWeekYear=Gr,P.quarter=P.quarters=eo,P.month=gt,P.daysInMonth=yt,P.week=P.weeks=Pt,P.isoWeek=P.isoWeeks=jt,P.weeksInYear=Jr,P.weeksInWeekYear=Xr,P.isoWeeksInYear=qr,P.isoWeeksInISOWeekYear=$r,P.date=to,P.day=P.days=$t,P.weekday=Jt,P.isoWeekday=Xt,P.dayOfYear=no,P.hour=P.hours=un,P.minute=P.minutes=ao,P.second=P.seconds=ro,P.millisecond=P.milliseconds=io,P.utcOffset=va,P.utc=ba,P.local=wa,P.parseZone=Ma,P.hasAlignedHourOffset=ka,P.isDST=Sa,P.isLocal=xa,P.isUtcOffset=Ca,P.isUtc=La,P.isUTC=La,P.zoneAbbr=so,P.zoneName=uo,P.dates=e("dates accessor is deprecated. Use date instead.",to),P.months=e("months accessor is deprecated. Use month instead",gt),P.years=e("years accessor is deprecated. Use year instead",Mt),P.zone=e("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",_a),P.isDSTShifted=e("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",Ea);var j=ae.prototype;function ho(e,t,n,a){var r=Mn(),o=d().set(a,t);return r[n](o,e)}function mo(e,t,n){if(u(e)){t=e;e=undefined}e=e||"";if(t!=null)return ho(e,t,n,"month");var a,r=[];for(a=0;a<12;a++)r[a]=ho(e,a,n,"month");return r}function go(e,t,n,a){if(typeof e==="boolean"){if(u(t)){n=t;t=undefined}t=t||""}else{t=e;n=t;e=false;if(u(t)){n=t;t=undefined}t=t||""}var r=Mn(),o=e?r._week.dow:0,i,l=[];if(n!=null)return ho(t,(n+o)%7,a,"day");for(i=0;i<7;i++)l[i]=ho(t,(i+o)%7,a,"day");return l}function yo(e,t){return mo(e,t,"months")}function vo(e,t){return mo(e,t,"monthsShort")}function _o(e,t,n){return go(e,t,n,"weekdays")}function bo(e,t,n){return go(e,t,n,"weekdaysShort")}function wo(e,t,n){return go(e,t,n,"weekdaysMin")}j.calendar=oe,j.longDateFormat=me,j.invalidDate=ye,j.ordinal=be,j.preparse=po,j.postformat=po,j.relativeTime=Me,j.pastFuture=ke,j.set=te,j.eras=Dr,j.erasParse=Or,j.erasConvertYear=Nr,j.erasAbbrRegex=Ar,j.erasNameRegex=Rr,j.erasNarrowRegex=Hr,j.months=ct,j.monthsShort=ft,j.monthsParse=ht,j.monthsRegex=_t,j.monthsShortRegex=vt,j.week=Tt,j.firstDayOfYear=Nt,j.firstDayOfWeek=Ot,j.weekdays=Bt,j.weekdaysMin=Kt,j.weekdaysShort=Ut,j.weekdaysParse=qt,j.weekdaysRegex=Qt,j.weekdaysShortRegex=Zt,j.weekdaysMinRegex=en,j.isPM=ln,j.meridiem=dn,_n("en",{eras:[{since:"0001-01-01",until:+Infinity,offset:1,name:"Anno Domini",narrow:"AD",abbr:"AD"},{since:"0000-12-31",until:-Infinity,offset:1,name:"Before Christ",narrow:"BC",abbr:"BC"}],dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10,n=y(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n}}),c.lang=e("moment.lang is deprecated. Use moment.locale instead.",_n),c.langData=e("moment.langData is deprecated. Use moment.localeData instead.",Mn);var Mo=Math.abs;function ko(){var e=this._data;this._milliseconds=Mo(this._milliseconds);this._days=Mo(this._days);this._months=Mo(this._months);e.milliseconds=Mo(e.milliseconds);e.seconds=Mo(e.seconds);e.minutes=Mo(e.minutes);e.hours=Mo(e.hours);e.months=Mo(e.months);e.years=Mo(e.years);return this}function So(e,t,n,a){var r=N(t,n);e._milliseconds+=a*r._milliseconds;e._days+=a*r._days;e._months+=a*r._months;return e._bubble()}function Eo(e,t){return So(this,e,t,1)}function xo(e,t){return So(this,e,t,-1)}function Co(e){if(e<0)return Math.floor(e);else return Math.ceil(e)}function Lo(){var e=this._milliseconds,t=this._days,n=this._months,a=this._data,r,o,i,l,s;if(!(e>=0&&t>=0&&n>=0||e<=0&&t<=0&&n<=0)){e+=Co(Do(n)+t)*864e5;t=0;n=0}a.milliseconds=e%1e3;r=g(e/1e3);a.seconds=r%60;o=g(r/60);a.minutes=o%60;i=g(o/60);a.hours=i%24;t+=g(i/24);s=g(To(t));n+=s;t-=Co(Do(s));l=g(n/12);n%=12;a.days=t;a.months=n;a.years=l;return this}function To(e){return e*4800/146097}function Do(e){return e*146097/4800}function Oo(e){if(!this.isValid())return NaN;var t,n,a=this._milliseconds;e=m(e);if(e==="month"||e==="quarter"||e==="year"){t=this._days+a/864e5;n=this._months+To(t);switch(e){case"month":return n;case"quarter":return n/3;case"year":return n/12}}else{t=this._days+Math.round(Do(this._months));switch(e){case"week":return t/7+a/6048e5;case"day":return t+a/864e5;case"hour":return t*24+a/36e5;case"minute":return t*1440+a/6e4;case"second":return t*86400+a/1e3;case"millisecond":return Math.floor(t*864e5)+a;default:throw new Error("Unknown unit "+e)}}}function No(){if(!this.isValid())return NaN;return this._milliseconds+this._days*864e5+this._months%12*2592e6+y(this._months/12)*31536e6}function Po(e){return function(){return this.as(e)}}var jo=Po("ms"),Yo=Po("s"),Io=Po("m"),Ro=Po("h"),Ao=Po("d"),Ho=Po("w"),Fo=Po("M"),zo=Po("Q"),Wo=Po("y");function Vo(){return N(this)}function Bo(e){e=m(e);return this.isValid()?this[e+"s"]():NaN}function Uo(e){return function(){return this.isValid()?this._data[e]:NaN}}var Ko=Uo("milliseconds"),Go=Uo("seconds"),qo=Uo("minutes"),$o=Uo("hours"),Jo=Uo("days"),Xo=Uo("months"),Qo=Uo("years");function Zo(){return g(this.days()/7)}var ei=Math.round,ti={ss:44,s:45,m:45,h:22,d:26,w:null,M:11};function ni(e,t,n,a,r){return r.relativeTime(t||1,!!n,e,a)}function ai(e,t,n,a){var r=N(e).abs(),o=ei(r.as("s")),i=ei(r.as("m")),l=ei(r.as("h")),s=ei(r.as("d")),u=ei(r.as("M")),d=ei(r.as("w")),c=ei(r.as("y")),f=o<=n.ss&&["s",o]||o0;f[4]=a;return ni.apply(null,f)}function ri(e){if(e===undefined)return ei;if(typeof e==="function"){ei=e;return true}return false}function oi(e,t){if(ti[e]===undefined)return false;if(t===undefined)return ti[e];ti[e]=t;if(e==="s")ti.ss=t-1;return true}function ii(e,t){if(!this.isValid())return this.localeData().invalidDate();var n=false,a=ti,r,o;if(typeof e==="object"){t=e;e=false}if(typeof e==="boolean")n=e;if(typeof t==="object"){a=Object.assign({},ti,t);if(t.s!=null&&t.ss==null)a.ss=t.s-1}r=this.localeData();o=ai(this,!n,a,r);if(n)o=r.pastFuture(+this,o);return r.postformat(o)}var li=Math.abs;function si(e){return(e>0)-(e<0)||+e}function ui(){if(!this.isValid())return this.localeData().invalidDate();var e=li(this._milliseconds)/1e3,t=li(this._days),n=li(this._months),a,r,o,i,l=this.asSeconds(),s,u,d,c;if(!l)return"P0D";a=g(e/60);r=g(a/60);e%=60;a%=60;o=g(n/12);n%=12;i=e?e.toFixed(3).replace(/\.?0+$/,""):"";s=l<0?"-":"";u=si(this._months)!==si(l)?"-":"";d=si(this._days)!==si(l)?"-":"";c=si(this._milliseconds)!==si(l)?"-":"";return s+"P"+(o?u+o+"Y":"")+(n?u+n+"M":"")+(t?d+t+"D":"")+(r||a||e?"T":"")+(r?c+r+"H":"")+(a?c+a+"M":"")+(e?c+i+"S":"")}var Y=ua.prototype;return Y.isValid=la,Y.abs=ko,Y.add=Eo,Y.subtract=xo,Y.as=Oo,Y.asMilliseconds=jo,Y.asSeconds=Yo,Y.asMinutes=Io,Y.asHours=Ro,Y.asDays=Ao,Y.asWeeks=Ho,Y.asMonths=Fo,Y.asQuarters=zo,Y.asYears=Wo,Y.valueOf=No,Y._bubble=Lo,Y.clone=Vo,Y.get=Bo,Y.milliseconds=Ko,Y.seconds=Go,Y.minutes=qo,Y.hours=$o,Y.days=Jo,Y.weeks=Zo,Y.months=Xo,Y.years=Qo,Y.humanize=ii,Y.toISOString=ui,Y.toString=ui,Y.toJSON=ui,Y.locale=sr,Y.localeData=dr,Y.toIsoString=e("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",ui),Y.lang=ur,a("X",0,0,"unix"),a("x",0,0,"valueOf"),_("x",Be),_("X",Ge),w("X",function(e,t,n){n._d=new Date(parseFloat(e)*1e3)}),w("x",function(e,t,n){n._d=new Date(y(e))}), //! moment.js -c.version="2.29.4",A(O),c.fn=P,c.min=na,c.max=aa,c.now=ra,c.utc=d,c.unix=co,c.months=yo,c.isDate=z,c.locale=_n,c.invalid=K,c.duration=N,c.isMoment=p,c.weekdays=_o,c.parseZone=fo,c.localeData=Mn,c.isDuration=da,c.monthsShort=vo,c.weekdaysMin=wo,c.defineLocale=bn,c.updateLocale=wn,c.locales=kn,c.weekdaysShort=bo,c.normalizeUnits=m,c.relativeTimeRounding=ri,c.relativeTimeThreshold=oi,c.calendarFormat=Va,c.prototype=P,c.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},c}()}.call(this,fi(516)(e))},function(e,t,n){"use strict";t.__esModule=!0;var a=d(n(2)),r=d(n(12)),o=d(n(7)),i=d(n(548)),l=d(n(571)),s=d(n(575)),u=d(n(578)),n=d(n(357));function d(e){return e&&e.__esModule?e:{default:e}}i.default.Item=o.default.config(l.default,{transform:function(e,t){var n;return"validateStatus"in e&&(t("validateStatus","validateState","Form.Item"),n=(t=e).validateStatus,t=(0,r.default)(t,["validateStatus"]),e=(0,a.default)({validateState:n},t)),e}}),i.default.Submit=s.default,i.default.Reset=u.default,i.default.Error=n.default,t.default=o.default.config(i.default,{transform:function(e,t){var n;return"direction"in e&&(t("direction","inline","Form"),n=(t=e).direction,t=(0,r.default)(t,["direction"]),"hoz"===n&&(e=(0,a.default)({inline:!0},t))),e}}),e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0,t.pickAttrs=t.datejs=t.htmlId=t.KEYCODE=t.guid=t.focus=t.support=t.str=t.obj=t.log=t.func=t.events=t.env=t.dom=void 0;var a=y(n(201)),r=y(n(204)),o=y(n(499)),i=y(n(500)),l=y(n(203)),s=y(n(95)),u=y(n(202)),d=y(n(508)),c=y(n(509)),f=y(n(510)),p=g(n(511)),h=g(n(206)),m=g(n(155)),n=g(n(512));function g(e){return e&&e.__esModule?e:{default:e}}function y(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}t.dom=a,t.env=r,t.events=o,t.func=i,t.log=l,t.obj=s,t.str=u,t.support=d,t.focus=c,t.guid=p.default,t.KEYCODE=h.default,t.htmlId=f,t.datejs=m.default,t.pickAttrs=n.default},function(e,t,n){"use strict";t.__esModule=!0;var a=u(n(2)),r=u(n(12)),o=u(n(7)),i=u(n(360)),l=u(n(581)),s=u(n(582)),n=u(n(362));function u(e){return e&&e.__esModule?e:{default:e}}i.default.Password=o.default.config(l.default,{exportNames:["getInputNode","focus"],transform:function(e,t){var n;return"hasLimitHint"in e&&(t("hasLimitHint","showLimitHint","Input"),n=(t=e).hasLimitHint,t=(0,r.default)(t,["hasLimitHint"]),e=(0,a.default)({showLimitHint:n},t)),e}}),i.default.TextArea=o.default.config(s.default,{exportNames:["getInputNode","focus"],transform:function(e,t){var n;return"hasLimitHint"in e&&(t("hasLimitHint","showLimitHint","Input"),n=(t=e).hasLimitHint,t=(0,r.default)(t,["hasLimitHint"]),e=(0,a.default)({showLimitHint:n},t)),e}}),i.default.Group=n.default,t.default=o.default.config(i.default,{exportNames:["getInputNode","focus"],transform:function(e,t){var n;return"hasLimitHint"in e&&(t("hasLimitHint","showLimitHint","Input"),n=(t=e).hasLimitHint,t=(0,r.default)(t,["hasLimitHint"]),e=(0,a.default)({showLimitHint:n},t)),e}}),e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0,t.default=function(e,t){var n,a={};for(n in e)0<=t.indexOf(n)||Object.prototype.hasOwnProperty.call(e,n)&&(a[n]=e[n]);return a}},function(e,t,n){var a; +c.version="2.29.4",A(O),c.fn=P,c.min=na,c.max=aa,c.now=ra,c.utc=d,c.unix=co,c.months=yo,c.isDate=z,c.locale=_n,c.invalid=K,c.duration=N,c.isMoment=p,c.weekdays=_o,c.parseZone=fo,c.localeData=Mn,c.isDuration=da,c.monthsShort=vo,c.weekdaysMin=wo,c.defineLocale=bn,c.updateLocale=wn,c.locales=kn,c.weekdaysShort=bo,c.normalizeUnits=m,c.relativeTimeRounding=ri,c.relativeTimeThreshold=oi,c.calendarFormat=Va,c.prototype=P,c.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},c}()}.call(this,fi(516)(e))},function(e,t,n){"use strict";t.__esModule=!0;var a=u(n(2)),r=u(n(12)),o=u(n(8)),i=u(n(360)),l=u(n(581)),s=u(n(582)),n=u(n(362));function u(e){return e&&e.__esModule?e:{default:e}}i.default.Password=o.default.config(l.default,{exportNames:["getInputNode","focus"],transform:function(e,t){var n;return"hasLimitHint"in e&&(t("hasLimitHint","showLimitHint","Input"),n=(t=e).hasLimitHint,t=(0,r.default)(t,["hasLimitHint"]),e=(0,a.default)({showLimitHint:n},t)),e}}),i.default.TextArea=o.default.config(s.default,{exportNames:["getInputNode","focus"],transform:function(e,t){var n;return"hasLimitHint"in e&&(t("hasLimitHint","showLimitHint","Input"),n=(t=e).hasLimitHint,t=(0,r.default)(t,["hasLimitHint"]),e=(0,a.default)({showLimitHint:n},t)),e}}),i.default.Group=n.default,t.default=o.default.config(i.default,{exportNames:["getInputNode","focus"],transform:function(e,t){var n;return"hasLimitHint"in e&&(t("hasLimitHint","showLimitHint","Input"),n=(t=e).hasLimitHint,t=(0,r.default)(t,["hasLimitHint"]),e=(0,a.default)({showLimitHint:n},t)),e}}),e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0,t.pickAttrs=t.datejs=t.htmlId=t.KEYCODE=t.guid=t.focus=t.support=t.str=t.obj=t.log=t.func=t.events=t.env=t.dom=void 0;var a=y(n(201)),r=y(n(204)),o=y(n(499)),i=y(n(500)),l=y(n(203)),s=y(n(96)),u=y(n(202)),d=y(n(508)),c=y(n(509)),f=y(n(510)),p=g(n(511)),h=g(n(206)),m=g(n(155)),n=g(n(512));function g(e){return e&&e.__esModule?e:{default:e}}function y(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}t.dom=a,t.env=r,t.events=o,t.func=i,t.log=l,t.obj=s,t.str=u,t.support=d,t.focus=c,t.guid=p.default,t.KEYCODE=h.default,t.htmlId=f,t.datejs=m.default,t.pickAttrs=n.default},function(e,t,n){"use strict";t.__esModule=!0,t.default=function(e,t){var n,a={};for(n in e)0<=t.indexOf(n)||Object.prototype.hasOwnProperty.call(e,n)&&(a[n]=e[n]);return a}},function(e,t,n){var a; /*! Copyright (c) 2018 Jed Watson. Licensed under the MIT License (MIT), see http://jedwatson.github.io/classnames */ -!function(){"use strict";var i={}.hasOwnProperty;function l(){for(var e=[],t=0;t")),"shouldUpdatePosition"in r&&(delete t.shouldUpdatePosition,d.log.warning("Warning: [ shouldUpdatePosition ] is deprecated at [ ]")),"minMargin"in r&&o("minMargin","top/bottom",""),"isFullScreen"in r&&(r.overFlowScroll=!r.isFullScreen,delete t.isFullScreen,o("isFullScreen","overFlowScroll","")),t;return["target","offset","beforeOpen","onOpen","afterOpen","beforePosition","onPosition","cache","safeNode","wrapperClassName","container"].forEach(function(e){var t,n,a;e in r&&(o(e,"overlayProps."+e,"Dialog"),t=(n=r).overlayProps,n=(0,l.default)(n,["overlayProps"]),a=(0,i.default)(((a={})[e]=r[e],a),t||{}),delete n[e],r=(0,i.default)({overlayProps:a},n))}),r}n.displayName="Dialog",n.Inner=p.default,n.show=function(e){return!1!==u.default.getContextProps(e,"Dialog").warning&&(e=v(e,d.log.deprecated)),(0,h.show)(e)},n.alert=function(e){return!1!==u.default.getContextProps(e,"Dialog").warning&&(e=v(e,d.log.deprecated)),(0,h.alert)(e)},n.confirm=function(e){return!1!==u.default.getContextProps(e,"Dialog").warning&&(e=v(e,d.log.deprecated)),(0,h.confirm)(e)},n.success=function(e){return(0,h.success)(e)},n.error=function(e){return(0,h.error)(e)},n.notice=function(e){return(0,h.notice)(e)},n.warning=function(e){return(0,h.warning)(e)},n.help=function(e){return(0,h.help)(e)},n.withContext=h.withContext,t.default=u.default.config(n,{transform:v}),e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var i=v(n(2)),l=v(n(12)),a=v(n(7)),r=v(n(638)),o=v(n(643)),s=v(n(646)),u=v(n(647)),d=v(n(648)),c=v(n(649)),f=v(n(651)),p=v(n(652)),h=v(n(653)),m=v(n(656)),g=v(n(393)),y=v(n(394));function v(e){return e&&e.__esModule?e:{default:e}}var _=n(10).env.ieVersion,n=[s.default,f.default,u.default,d.default,c.default,o.default,h.default,m.default],b=n.reduce(function(e,t){return e=t(e)},r.default),n=(f.default._typeMark="lock",d.default._typeMark="expanded",s.default._typeMark="fixed",n.reduce(function(e,t){var n=!_;return e="lock"===t._typeMark?(n?(0,p.default):(0,f.default))(e):"expanded"===t._typeMark?n?(0,d.default)(e,!0):(0,d.default)(e):"fixed"===t._typeMark?n?(0,s.default)(e,!0):(0,s.default)(e):t(e)},r.default));b.Base=r.default,b.fixed=s.default,b.lock=f.default,b.selection=u.default,b.expanded=d.default,b.tree=o.default,b.virtual=c.default,b.list=h.default,b.sticky=m.default,b.GroupHeader=g.default,b.GroupFooter=y.default,b.StickyLock=a.default.config(n,{componentName:"Table"}),t.default=a.default.config(b,{componentName:"Table",transform:function(e,t){var n,a,r,o;return"expandedRowKeys"in e&&(t("expandedRowKeys","openRowKeys","Table"),o=(r=e).expandedRowKeys,r=(0,l.default)(r,["expandedRowKeys"]),e=(0,i.default)({openRowKeys:o},r)),"onExpandedChange"in e&&(t("onExpandedChange","onRowOpen","Table"),r=(o=e).onExpandedChange,o=(0,l.default)(o,["onExpandedChange"]),e=(0,i.default)({onRowOpen:r},o)),"isLoading"in e&&(t("isLoading","loading","Table"),o=(r=e).isLoading,r=(0,l.default)(r,["isLoading"]),e=(0,i.default)({loading:o},r)),"indentSize"in e&&(t("indentSize","indent","Table"),r=(o=e).indentSize,o=(0,l.default)(o,["indentSize"]),e=(0,i.default)({indent:r},o)),"optimization"in e&&(t("optimization","pure","Table"),o=(r=e).optimization,r=(0,l.default)(r,["optimization"]),e=(0,i.default)({pure:o},r)),"getRowClassName"in e&&(t("getRowClassName","getRowProps","Table"),n=(o=e).getRowClassName,a=o.getRowProps,r=(0,l.default)(o,["getRowClassName","getRowProps"]),e=n?(0,i.default)({getRowProps:function(){return(0,i.default)({className:n.apply(void 0,arguments)},a?a.apply(void 0,arguments):{})}},r):(0,i.default)({getRowProps:a},r)),"getRowProps"in e&&(t("getRowProps","rowProps","Table in 1.15.0"),r=(o=e).getRowProps,o=(0,l.default)(o,["getRowProps"]),e=(0,i.default)({rowProps:r},o)),"getCellProps"in e&&(t("getCellProps","cellProps","Table in 1.15.0"),o=(r=e).getCellProps,t=(0,l.default)(r,["getCellProps"]),e=(0,i.default)({cellProps:o},t)),e}}),e.exports=t.default},function(e,t,n){"use strict";n.d(t,"a",function(){return o});var a=n(88);function r(t,e){var n,a=Object.keys(t);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(t),e&&(n=n.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),a.push.apply(a,n)),a}function o(t){for(var e=1;e 16.8.0")},p.prototype.validate=function(e,t){this.validateCallback(e,t)},p.prototype.reset=function(e){var t=1","Select");t=s(e,t);return e.onInputUpdate&&(t.onSearch=e.onInputUpdate,t.showSearch=!0),t}}),t.default=a.default.config(r.default,{transform:s,exportNames:["focusInput","handleSearchClear"]}),e.exports=t.default},function(e,t,n){"use strict";n.d(t,"f",function(){return a}),n.d(t,"g",function(){return r}),n.d(t,"i",function(){return o}),n.d(t,"c",function(){return i}),n.d(t,"d",function(){return l}),n.d(t,"j",function(){return s}),n.d(t,"l",function(){return u}),n.d(t,"k",function(){return d}),n.d(t,"h",function(){return c}),n.d(t,"b",function(){return f}),n.d(t,"a",function(){return p}),n.d(t,"e",function(){return h});var a="docsite_language",r="LANGUAGE_SWITCH",o="__REDUX_DEVTOOLS_EXTENSION__",i="GET_STATE",l="GET_SUBSCRIBERS",s="REMOVE_SUBSCRIBERS",u="USER_LIST",d="ROLE_LIST",c="PERMISSIONS_LIST",f="GET_NAMESPACES",p="GET_CONFIGURATION",h=[10,20,30,50,100]},function(e,t,n){"use strict";function s(){var e=this.constructor.getDerivedStateFromProps(this.props,this.state);null!=e&&this.setState(e)}function u(t){this.setState(function(e){return null!=(e=this.constructor.getDerivedStateFromProps(t,e))?e:null}.bind(this))}function d(e,t){try{var n=this.props,a=this.state;this.props=e,this.state=t,this.__reactInternalSnapshotFlag=!0,this.__reactInternalSnapshot=this.getSnapshotBeforeUpdate(n,a)}finally{this.props=n,this.state=a}}function a(e){var t=e.prototype;if(!t||!t.isReactComponent)throw new Error("Can only polyfill class components");if("function"==typeof e.getDerivedStateFromProps||"function"==typeof t.getSnapshotBeforeUpdate){var n,a,r=null,o=null,i=null;if("function"==typeof t.componentWillMount?r="componentWillMount":"function"==typeof t.UNSAFE_componentWillMount&&(r="UNSAFE_componentWillMount"),"function"==typeof t.componentWillReceiveProps?o="componentWillReceiveProps":"function"==typeof t.UNSAFE_componentWillReceiveProps&&(o="UNSAFE_componentWillReceiveProps"),"function"==typeof t.componentWillUpdate?i="componentWillUpdate":"function"==typeof t.UNSAFE_componentWillUpdate&&(i="UNSAFE_componentWillUpdate"),null!==r||null!==o||null!==i)throw n=e.displayName||e.name,a="function"==typeof e.getDerivedStateFromProps?"getDerivedStateFromProps()":"getSnapshotBeforeUpdate()",Error("Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n"+n+" uses "+a+" but also contains the following legacy lifecycles:"+(null!==r?"\n "+r:"")+(null!==o?"\n "+o:"")+(null!==i?"\n "+i:"")+"\n\nThe above lifecycles should be removed. Learn more about this warning here:\nhttps://fb.me/react-async-component-lifecycle-hooks");if("function"==typeof e.getDerivedStateFromProps&&(t.componentWillMount=s,t.componentWillReceiveProps=u),"function"==typeof t.getSnapshotBeforeUpdate){if("function"!=typeof t.componentDidUpdate)throw new Error("Cannot polyfill getSnapshotBeforeUpdate() for components that do not define componentDidUpdate() on the prototype");t.componentWillUpdate=d;var l=t.componentDidUpdate;t.componentDidUpdate=function(e,t,n){n=this.__reactInternalSnapshotFlag?this.__reactInternalSnapshot:n;l.call(this,e,t,n)}}}return e}n.r(t),n.d(t,"polyfill",function(){return a}),d.__suppressDeprecationWarning=u.__suppressDeprecationWarning=s.__suppressDeprecationWarning=!0},function(e,t,n){"use strict";n.d(t,"a",function(){return r});var a=n(133);function r(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(e)){var n=[],a=!0,r=!1,o=void 0;try{for(var i,l=e[Symbol.iterator]();!(a=(i=l.next()).done)&&(n.push(i.value),!t||n.length!==t);a=!0);}catch(e){r=!0,o=e}finally{try{a||null==l.return||l.return()}finally{if(r)throw o}}return n}}(e,t)||Object(a.a)(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}},function(e,t,n){"use strict";n(41),n(540)},function(e,t,n){"use strict";t.__esModule=!0;var v=s(n(2)),a=s(n(4)),r=s(n(5)),o=s(n(6)),_=s(n(0)),i=s(n(3)),b=s(n(13)),w=s(n(61)),l=s(n(7)),M=n(10);function s(e){return e&&e.__esModule?e:{default:e}}u=_.default.Component,(0,o.default)(k,u),k.prototype.render=function(){var e=this.props,t=e.tip,n=e.visible,a=e.children,r=e.className,o=e.style,i=e.indicator,l=e.color,s=e.prefix,u=e.fullScreen,d=e.disableScroll,c=e.onVisibleChange,f=e.tipAlign,p=e.size,h=e.inline,m=e.rtl,e=e.safeNode,g=null,y=s+"loading-dot",p=(g=i||(i=l,p=(0,b.default)(((l={})[s+"loading-fusion-reactor"]=!0,l[s+"loading-medium-fusion-reactor"]="medium"===p,l)),_.default.createElement("div",{className:p,dir:m?"rtl":void 0},_.default.createElement("span",{className:y,style:{backgroundColor:i}}),_.default.createElement("span",{className:y,style:{backgroundColor:i}}),_.default.createElement("span",{className:y,style:{backgroundColor:i}}),_.default.createElement("span",{className:y,style:{backgroundColor:i}}))),(0,b.default)(((l={})[s+"loading"]=!0,l[s+"open"]=n,l[s+"loading-inline"]=h,l[r]=r,l))),y=(0,b.default)(((m={})[s+"loading-tip"]=!0,m[s+"loading-tip-fullscreen"]=u,m[s+"loading-right-tip"]="right"===f,m)),i=M.obj.pickOthers(k.propTypes,this.props),l=(0,b.default)(((h={})[s+"loading-component"]=n,h[s+"loading-wrap"]=!0,h));return u?[a,_.default.createElement(w.default,(0,v.default)({key:"overlay",hasMask:!0,align:"cc cc",safeNode:e,disableScroll:d},i,{className:r,style:o,visible:n,onRequestClose:c}),_.default.createElement("div",{className:y},_.default.createElement("div",{className:s+"loading-indicator"},g),_.default.createElement("div",{className:s+"loading-tip-content"},t),_.default.createElement("div",{className:s+"loading-tip-placeholder"},t)))]:_.default.createElement("div",(0,v.default)({className:p,style:o},i),n?_.default.createElement("div",{className:y},_.default.createElement("div",{className:s+"loading-indicator"},g),_.default.createElement("div",{className:s+"loading-tip-content"},t),_.default.createElement("div",{className:s+"loading-tip-placeholder"},t)):null,_.default.createElement("div",{className:l},n?_.default.createElement("div",{className:s+"loading-masker"}):null,a))},o=n=k,n.propTypes=(0,v.default)({},l.default.propTypes,{prefix:i.default.string,tip:i.default.any,tipAlign:i.default.oneOf(["right","bottom"]),visible:i.default.bool,onVisibleChange:i.default.func,className:i.default.string,style:i.default.object,size:i.default.oneOf(["large","medium"]),indicator:i.default.any,color:i.default.string,fullScreen:i.default.bool,disableScroll:i.default.bool,safeNode:i.default.any,children:i.default.any,inline:i.default.bool,rtl:i.default.bool}),n.defaultProps={prefix:"next-",visible:!0,onVisibleChange:M.func.noop,animate:null,tipAlign:"bottom",size:"large",inline:!0,disableScroll:!1};var u,i=o;function k(){return(0,a.default)(this,k),(0,r.default)(this,u.apply(this,arguments))}i.displayName="Loading",t.default=l.default.config(i),e.exports=t.default},function(e,t,n){"use strict";n(74),n(52),n(32),n(41),n(541)},function(M,e,t){"use strict";t.d(e,"a",function(){return a}),t.d(e,"b",function(){return B});var x=t(0),C=t.n(x),d=C.a.createContext(null);function s(){return n}var n=function(e){e()};var r={notify:function(){},get:function(){return[]}};function L(t,n){var o,i=r;function l(){e.onStateChange&&e.onStateChange()}function a(){var e,a,r;o||(o=n?n.addNestedSub(l):t.subscribe(l),e=s(),r=a=null,i={clear:function(){r=a=null},notify:function(){e(function(){for(var e=a;e;)e.callback(),e=e.next})},get:function(){for(var e=[],t=a;t;)e.push(t),t=t.next;return e},subscribe:function(e){var t=!0,n=r={callback:e,next:null,prev:r};return n.prev?n.prev.next=n:a=n,function(){t&&null!==a&&(t=!1,n.next?n.next.prev=n.prev:r=n.prev,n.prev?n.prev.next=n.next:a=n.next)}}})}var e={addNestedSub:function(e){return a(),i.subscribe(e)},notifyNestedSubs:function(){i.notify()},handleChangeWrapper:l,isSubscribed:function(){return Boolean(o)},trySubscribe:a,tryUnsubscribe:function(){o&&(o(),o=void 0,i.clear(),i=r)},getListeners:function(){return i}};return e}var o="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?x.useLayoutEffect:x.useEffect;var a=function(e){var t=e.store,n=e.context,e=e.children,a=Object(x.useMemo)(function(){var e=L(t);return{store:t,subscription:e}},[t]),r=Object(x.useMemo)(function(){return t.getState()},[t]),n=(o(function(){var e=a.subscription;return e.onStateChange=e.notifyNestedSubs,e.trySubscribe(),r!==t.getState()&&e.notifyNestedSubs(),function(){e.tryUnsubscribe(),e.onStateChange=null}},[a,r]),n||d);return C.a.createElement(n.Provider,{value:a},e)},T=t(40),D=t(53),e=t(99),c=t.n(e),O=t(412),f=["getDisplayName","methodName","renderCountProp","shouldHandleStateChanges","storeKey","withRef","forwardRef","context"],N=["reactReduxForwardedRef"],P=[],j=[null,null];function Y(e,t){e=e[1];return[t.payload,e+1]}function I(e,t,n){o(function(){return e.apply(void 0,t)},n)}function R(e,t,n,a,r,o,i){e.current=a,t.current=r,n.current=!1,o.current&&(o.current=null,i())}function A(e,a,t,r,o,i,l,s,u,d){var c,f;if(e)return c=!1,f=null,e=function(){if(!c){var e,t,n=a.getState();try{e=r(n,o.current)}catch(e){f=t=e}t||(f=null),e===i.current?l.current||u():(i.current=e,s.current=e,l.current=!0,d({type:"STORE_UPDATED",payload:{error:t}}))}},t.onStateChange=e,t.trySubscribe(),e(),function(){if(c=!0,t.tryUnsubscribe(),t.onStateChange=null,f)throw f}}var H=function(){return[null,0]};function i(k,e){var e=e=void 0===e?{}:e,t=e.getDisplayName,r=void 0===t?function(e){return"ConnectAdvanced("+e+")"}:t,t=e.methodName,o=void 0===t?"connectAdvanced":t,t=e.renderCountProp,i=void 0===t?void 0:t,t=e.shouldHandleStateChanges,S=void 0===t||t,t=e.storeKey,l=void 0===t?"store":t,t=(e.withRef,e.forwardRef),s=void 0!==t&&t,t=e.context,t=void 0===t?d:t,u=Object(D.a)(e,f),E=t;return function(b){var e=b.displayName||b.name||"Component",t=r(e),w=Object(T.a)({},u,{getDisplayName:r,methodName:o,renderCountProp:i,shouldHandleStateChanges:S,storeKey:l,displayName:t,wrappedComponentName:e,WrappedComponent:b}),e=u.pure;var M=e?x.useMemo:function(e){return e()};function n(n){var e=Object(x.useMemo)(function(){var e=n.reactReduxForwardedRef,t=Object(D.a)(n,N);return[n.context,e,t]},[n]),t=e[0],a=e[1],r=e[2],o=Object(x.useMemo)(function(){return t&&t.Consumer&&Object(O.isContextConsumer)(C.a.createElement(t.Consumer,null))?t:E},[t,E]),i=Object(x.useContext)(o),l=Boolean(n.store)&&Boolean(n.store.getState)&&Boolean(n.store.dispatch),s=(Boolean(i)&&Boolean(i.store),(l?n:i).store),u=Object(x.useMemo)(function(){return k(s.dispatch,w)},[s]),e=Object(x.useMemo)(function(){if(!S)return j;var e=L(s,l?null:i.subscription),t=e.notifyNestedSubs.bind(e);return[e,t]},[s,l,i]),d=e[0],e=e[1],c=Object(x.useMemo)(function(){return l?i:Object(T.a)({},i,{subscription:d})},[l,i,d]),f=Object(x.useReducer)(Y,P,H),p=f[0][0],f=f[1];if(p&&p.error)throw p.error;var h=Object(x.useRef)(),m=Object(x.useRef)(r),g=Object(x.useRef)(),y=Object(x.useRef)(!1),v=M(function(){return g.current&&r===m.current?g.current:u(s.getState(),r)},[s,p,r]),_=(I(R,[m,h,y,r,v,g,e]),I(A,[S,s,d,u,m,h,y,g,e,f],[s,d,u]),Object(x.useMemo)(function(){return C.a.createElement(b,Object(T.a)({},v,{ref:a}))},[a,b,v]));return Object(x.useMemo)(function(){return S?C.a.createElement(o.Provider,{value:c},_):_},[o,_,c])}var a=e?C.a.memo(n):n;return a.WrappedComponent=b,a.displayName=n.displayName=t,s?((e=C.a.forwardRef(function(e,t){return C.a.createElement(a,Object(T.a)({},e,{reactReduxForwardedRef:t}))})).displayName=t,e.WrappedComponent=b,c()(e,b)):c()(a,b)}}function l(e,t){return e===t?0!==e||0!==t||1/e==1/t:e!=e&&t!=t}function m(e,t){if(!l(e,t)){if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),a=Object.keys(t);if(n.length!==a.length)return!1;for(var r=0;re?t.splice(e,t.length-e,n):t.push(n),i({action:"PUSH",location:n,index:e,entries:t}))})},replace:function(e,t){var n=O(e,t,l(),u.location);o.confirmTransitionTo(n,"REPLACE",a,function(e){e&&i({action:"REPLACE",location:u.entries[u.index]=n})})},go:s,goBack:function(){s(-1)},goForward:function(){s(1)},canGo:function(e){return 0<=(e=u.index+e)&&ex',"Tag"),"readonly"!==n&&"interactive"!==n||r.log.warning("Warning: [ shape="+n+" ] is deprecated at [ Tag ]"),"secondary"===a&&r.log.warning("Warning: [ type=secondary ] is deprecated at [ Tag ]"),["count","marked","value","onChange"].forEach(function(e){e in t&&r.log.warning("Warning: [ "+e+" ] is deprecated at [ Tag ]")}),("selected"in t||"defaultSelected"in t)&&r.log.warning("Warning: [ selected|defaultSelected ] is deprecated at [ Tag ], use [ checked|defaultChecked ] at [ Tag.Selectable ] instead of it"),"closed"in t&&r.log.warning("Warning: [ closed ] is deprecated at [ Tag ], use [ onClose ] at [ Tag.Closeable ] instead of it"),"onSelect"in t&&e("onSelect","","Tag"),"afterClose"in t&&r.log.warning("Warning: [ afterClose ] is deprecated at [ Tag ], use [ afterClose ] at [ Tag.Closeable ] instead of it"),t}});o.Group=a.default.config(i.default),o.Selectable=a.default.config(l.default),o.Closable=a.default.config(n.default),o.Closeable=o.Closable,t.default=o,e.exports=t.default},function(e,t,n){"use strict";n(69),n(447)},function(e,t){e=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=e)},function(e,t){e=e.exports={version:"2.6.12"};"number"==typeof __e&&(__e=e)},function(e,t,n){e.exports=!n(107)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,t,n){"use strict";t.__esModule=!0;var a=o(n(344)),r=o(n(526)),n=o(n(527));function o(e){return e&&e.__esModule?e:{default:e}}a.default.Expand=r.default,a.default.OverlayAnimate=n.default,t.default=a.default,e.exports=t.default},function(e,t,n){"use strict";n(41),n(69),n(124),n(108),n(538)},function(e,t,n){"use strict";n.d(t,"a",function(){return r});function v(e,t){return"function"==typeof e?e(t):e}function _(e,t){return"string"==typeof e?Object(d.c)(e,null,null,t):e}function u(e){return e}var b=n(38),a=n(57),t=n(0),w=n.n(t),d=n(54),M=n(40),k=n(53),S=n(56),r=(w.a.Component,function(r){function e(){for(var e,t=arguments.length,n=new Array(t),a=0;athis.menuNode.clientHeight&&(this.menuNode.clientHeight+this.menuNode.scrollTop<(e=this.itemNode.offsetTop+this.itemNode.offsetHeight)?this.menuNode.scrollTop=e-this.menuNode.clientHeight:this.itemNode.offsetTop"===(d=e.charAt(o+1))){r+="(",o++,0[0-9][0-9][0-9][0-9])-(?[0-9][0-9]?)-(?[0-9][0-9]?)(?:(?:[Tt]|[ \t]+)(?[0-9][0-9]?):(?[0-9][0-9]):(?[0-9][0-9])(?:.(?[0-9]*))?(?:[ \t]*(?Z|(?[-+])(?[0-9][0-9]?)(?::(?[0-9][0-9]))?))?)?$","i"),r.LOCAL_TIMEZONE_OFFSET=60*(new Date).getTimezoneOffset()*1e3,r.trim=function(e,t){var n,a;return null==(n=this.REGEX_LEFT_TRIM_BY_CHAR[t=null==t?"\\s":t])&&(this.REGEX_LEFT_TRIM_BY_CHAR[t]=n=new RegExp("^"+t+t+"*")),n.lastIndex=0,null==(a=this.REGEX_RIGHT_TRIM_BY_CHAR[t])&&(this.REGEX_RIGHT_TRIM_BY_CHAR[t]=a=new RegExp(t+""+t+"*$")),a.lastIndex=0,e.replace(n,"").replace(a,"")},r.ltrim=function(e,t){var n;return null==(n=this.REGEX_LEFT_TRIM_BY_CHAR[t=null==t?"\\s":t])&&(this.REGEX_LEFT_TRIM_BY_CHAR[t]=n=new RegExp("^"+t+t+"*")),n.lastIndex=0,e.replace(n,"")},r.rtrim=function(e,t){var n;return null==(n=this.REGEX_RIGHT_TRIM_BY_CHAR[t=null==t?"\\s":t])&&(this.REGEX_RIGHT_TRIM_BY_CHAR[t]=n=new RegExp(t+""+t+"*$")),n.lastIndex=0,e.replace(n,"")},r.isEmpty=function(e){return!e||""===e||"0"===e||e instanceof Array&&0===e.length||this.isEmptyObject(e)},r.isEmptyObject=function(t){var n;return t instanceof Object&&0===function(){var e=[];for(n in t)a.call(t,n)&&e.push(n);return e}().length},r.subStrCount=function(e,t,n,a){var r,o,i,l,s=0;for(e=""+e,t=""+t,null!=n&&(e=e.slice(n)),n=(e=null!=a?e.slice(0,a):e).length,l=t.length,r=o=0,i=n;0<=i?o>6)+t(128|63&e):e<65536?t(224|e>>12)+t(128|e>>6&63)+t(128|63&e):t(240|e>>18)+t(128|e>>12&63)+t(128|e>>6&63)+t(128|63&e)},r.parseBoolean=function(e,t){var n;return null==t&&(t=!0),"string"==typeof e?(n=e.toLowerCase(),!(!t&&"no"===n)&&("0"!==n&&("false"!==n&&""!==n))):!!e},r.isNumeric=function(e){return this.REGEX_SPACES.lastIndex=0,"number"==typeof e||"string"==typeof e&&!isNaN(e)&&""!==e.replace(this.REGEX_SPACES,"")},r.stringToDate=function(e){var t,n,a,r,o,i,l,s,u;if(null==e||!e.length)return null;if(!(e=this.PATTERN_DATE.exec(e)))return null;if(u=parseInt(e.year,10),i=parseInt(e.month,10)-1,n=parseInt(e.day,10),null==e.hour)return t=new Date(Date.UTC(u,i,n));if(r=parseInt(e.hour,10),o=parseInt(e.minute,10),l=parseInt(e.second,10),null!=e.fraction){for(a=e.fraction.slice(0,3);a.length<3;)a+="0";a=parseInt(a,10)}else a=0;return null!=e.tz&&(s=6e4*(60*parseInt(e.tz_hour,10)+(null!=e.tz_minute?parseInt(e.tz_minute,10):0)),"-"===e.tz_sign&&(s*=-1)),t=new Date(Date.UTC(u,i,n,r,o,l,a)),s&&t.setTime(t.getTime()-s),t},r.strRepeat=function(e,t){for(var n="",a=0;ae.length)&&(t=e.length);for(var n=0,a=new Array(t);ndocument.F=Object<\/script>"),e.close(),u=e.F;t--;)delete u[s][i[t]];return u()};e.exports=Object.create||function(e,t){var n;return null!==e?(a[s]=r(e),n=new a,a[s]=null,n[l]=e):n=u(),void 0===t?n:o(n,t)}},function(e,t,n){var a=n(84).f,r=n(85),o=n(94)("toStringTag");e.exports=function(e,t,n){e&&!r(e=n?e:e.prototype,o)&&a(e,o,{configurable:!0,value:t})}},function(e,t,n){t.f=n(94)},function(e,t,n){var a=n(75),r=n(76),o=n(120),i=n(153),l=n(84).f;e.exports=function(e){var t=r.Symbol||(r.Symbol=!o&&a.Symbol||{});"_"==e.charAt(0)||e in t||l(t,e,{value:i.f(e)})}},function(e,t,n){"use strict";t.__esModule=!0;var a=d(n(205)),r=d(n(501)),o=d(n(502)),i=d(n(503)),l=d(n(504)),s=d(n(505)),u=d(n(506));function d(e){return e&&e.__esModule?e:{default:e}}n(507),a.default.extend(s.default),a.default.extend(l.default),a.default.extend(r.default),a.default.extend(o.default),a.default.extend(i.default),a.default.extend(u.default),a.default.locale("zh-cn");n=a.default;n.isSelf=a.default.isDayjs,a.default.localeData(),t.default=n,e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var v=c(n(2)),o=c(n(4)),i=c(n(5)),a=c(n(6)),r=n(0),_=c(r),l=c(n(3)),s=n(30),b=c(n(13)),u=c(n(44)),w=c(n(24)),M=c(n(78)),d=c(n(7)),k=n(10);function c(e){return e&&e.__esModule?e:{default:e}}function f(){}p=r.Component,(0,a.default)(S,p),S.getDerivedStateFromProps=function(e){return"visible"in e?{visible:e.visible}:{}},S.prototype.render=function(){var e,t=this.props,n=t.prefix,a=(t.pure,t.className),r=t.style,o=t.type,i=t.shape,l=t.size,s=t.title,u=t.children,d=(t.defaultVisible,t.visible,t.iconType),c=t.closeable,f=(t.onClose,t.afterClose),p=t.animation,h=t.rtl,t=t.locale,m=(0,v.default)({},k.obj.pickOthers(Object.keys(S.propTypes),this.props)),g=this.state.visible,y=n+"message",o=(0,b.default)(((e={})[y]=!0,e[n+"message-"+o]=o,e[""+n+i]=i,e[""+n+l]=l,e[n+"title-content"]=!!s,e[n+"only-content"]=!s&&!!u,e[a]=a,e)),i=g?_.default.createElement("div",(0,v.default)({role:"alert",style:r},m,{className:o,dir:h?"rtl":void 0}),c?_.default.createElement("a",{role:"button","aria-label":t.closeAriaLabel,className:y+"-close",onClick:this.onClose},_.default.createElement(w.default,{type:"close"})):null,!1!==d?_.default.createElement(w.default,{className:y+"-symbol "+(!d&&y+"-symbol-icon"),type:d}):null,s?_.default.createElement("div",{className:y+"-title"},s):null,u?_.default.createElement("div",{className:y+"-content"},u):null):null;return p?_.default.createElement(M.default.Expand,{animationAppear:!1,afterLeave:f},i):i},r=n=S,n.propTypes={prefix:l.default.string,pure:l.default.bool,className:l.default.string,style:l.default.object,type:l.default.oneOf(["success","warning","error","notice","help","loading"]),shape:l.default.oneOf(["inline","addon","toast"]),size:l.default.oneOf(["medium","large"]),title:l.default.node,children:l.default.node,defaultVisible:l.default.bool,visible:l.default.bool,iconType:l.default.oneOfType([l.default.string,l.default.bool]),closeable:l.default.bool,onClose:l.default.func,afterClose:l.default.func,animation:l.default.bool,locale:l.default.object,rtl:l.default.bool},n.defaultProps={prefix:"next-",pure:!1,type:"success",shape:"inline",size:"medium",defaultVisible:!0,closeable:!1,onClose:f,afterClose:f,animation:!0,locale:u.default.Message};var p,a=r;function S(){var e,t;(0,o.default)(this,S);for(var n=arguments.length,a=Array(n),r=0;r=n.length?(s=!!(c=h(o,u)))&&"get"in c&&!("originalValue"in c.get)?c.get:o[u]:(s=_(o,u),o[u]),s&&!i&&(y[d]=o)}}return o}},function(e,t,n){"use strict";n=n(604);e.exports=Function.prototype.bind||n},function(e,t,n){"use strict";var a=String.prototype.replace,r=/%20/g,o="RFC1738",i="RFC3986";e.exports={default:i,formatters:{RFC1738:function(e){return a.call(e,r,"+")},RFC3986:function(e){return String(e)}},RFC1738:o,RFC3986:i}},function(e,t,n){"use strict";t.__esModule=!0,t.default=void 0;var d=s(n(2)),a=s(n(4)),r=s(n(5)),o=s(n(6)),c=n(0),f=s(c),i=s(n(3)),p=s(n(13)),h=s(n(24)),l=n(10),m=s(n(97));function s(e){return e&&e.__esModule?e:{default:e}}var u,g=l.func.bindCtx,y=l.obj.pickOthers,i=(u=c.Component,(0,o.default)(v,u),v.prototype.getSelected=function(){var e=this.props,t=e._key,n=e.root,e=e.selected,a=n.props.selectMode,n=n.state.selectedKeys;return e||!!a&&-1e.length&&e.every(function(e,t){return e===n[t]})},t.isAvailablePos=function(e,t,n){var n=n[t],a=n.type,n=n.disabled;return r(e,t)&&("item"===a&&!n||"submenu"===a)});t.getFirstAvaliablelChildKey=function(t,n){var e=Object.keys(n).find(function(e){return a(t+"-0",e,n)});return e?n[e].key:null},t.getChildSelected=function(e){var t=e.selectMode,n=e.selectedKeys,a=e._k2n,e=e._key;if(!a)return!1;var r=(a[e]&&a[e].pos)+"-";return!!t&&n.some(function(e){return a[e]&&0===a[e].pos.indexOf(r)})}},function(e,t,n){"use strict";n(41),n(32),n(626)},function(e,t,n){var o=n(639),i=Object.prototype.hasOwnProperty;function l(e){return Array.isArray(e)?"array":typeof e}function s(e,t){var n,a=0,r=0;for(n in e)if(i.call(e,n)){if("style"===n){if(!o(e[n],t[n]))return!1}else if("children"!==n&&e[n]!==t[n])return!1;a++}for(n in t)i.call(t,n)&&r++;return a===r&&function e(t,n){var a=l(t);if(a!==l(n))return!1;switch(a){case"array":if(t.length!==n.length)return!1;for(var r=0;r=u,u=(0,O.default)(((u={})[n+"upload-inner"]=!0,u[n+"hidden"]=x,u)),C=this.props.children;if("card"===r&&(r=(0,O.default)(((r={})[n+"upload-card"]=!0,r[n+"disabled"]=s,r)),C=D.default.createElement("div",{className:r},D.default.createElement(P.default,{size:"large",type:"add",className:n+"upload-add-icon"}),D.default.createElement("div",{tabIndex:"0",role:"button",className:n+"upload-text"},C))),b)return"function"==typeof w?(b=(0,O.default)(((r={})[n+"form-preview"]=!0,r[o]=!!o,r)),D.default.createElement("div",{style:i,className:b},w(this.state.value,this.props))):t?D.default.createElement(Y.default,{isPreview:!0,listType:t,style:i,className:o,value:this.state.value}):null;n=s?N.func.prevent:p,r=N.obj.pickAttrsWith(this.props,"data-");return D.default.createElement("div",(0,L.default)({className:f,style:i},r),D.default.createElement(j.default,(0,L.default)({},e,{name:M,beforeUpload:c,dragable:a,disabled:s||x,className:u,onSelect:this.onSelect,onDrop:this.onDrop,onProgress:this.onProgress,onSuccess:this.onSuccess,onError:this.onError,ref:this.saveUploaderRef}),C),t||g?D.default.createElement(Y.default,{useDataURL:l,fileNameRender:k,actionRender:S,uploader:this,listType:t,value:this.state.value,closable:d,onRemove:n,progressProps:v,onCancel:h,onPreview:m,extraRender:y,rtl:_,previewOnFileName:E}):null)},i=u=h,u.displayName="Upload",u.propTypes=(0,L.default)({},d.default.propTypes,Y.default.propTypes,{prefix:l.default.string.isRequired,action:l.default.string,value:l.default.array,defaultValue:l.default.array,shape:l.default.oneOf(["card"]),listType:l.default.oneOf(["text","image","card"]),list:l.default.any,name:l.default.string,data:l.default.oneOfType([l.default.object,l.default.func]),formatter:l.default.func,limit:l.default.number,timeout:l.default.number,dragable:l.default.bool,closable:l.default.bool,useDataURL:l.default.bool,disabled:l.default.bool,onSelect:l.default.func,onProgress:l.default.func,onChange:l.default.func,onSuccess:l.default.func,afterSelect:l.default.func,onRemove:l.default.func,onError:l.default.func,beforeUpload:l.default.func,onDrop:l.default.func,className:l.default.string,style:l.default.object,children:l.default.node,autoUpload:l.default.bool,request:l.default.func,progressProps:l.default.object,rtl:l.default.bool,isPreview:l.default.bool,renderPreview:l.default.func,fileKeyName:l.default.string,fileNameRender:l.default.func,actionRender:l.default.func,previewOnFileName:l.default.bool}),u.defaultProps=(0,L.default)({},d.default.defaultProps,{prefix:"next-",limit:1/0,autoUpload:!0,closable:!0,onSelect:n,onProgress:n,onChange:n,onSuccess:n,onRemove:n,onError:n,onDrop:n,beforeUpload:n,afterSelect:n,previewOnFileName:!1}),a=function(){var u=this;this.onSelect=function(e){var t,n,a=u.props,r=a.autoUpload,o=a.afterSelect,i=a.onSelect,a=a.limit,l=u.state.value.length+e.length,s=a-u.state.value.length;s<=0||(t=e=e.map(function(e){e=(0,c.fileToObject)(e);return e.state="selected",e}),n=[],ai||l+a.width>o):t<0||e<0||t+a.height>u.height||e+a.width>u.width}function T(e,t,n,a){var r=a.overlayInfo,a=a.containerInfo,n=n.split("");return 1===n.length&&n.push(""),t<0&&(n=[n[0].replace("t","b"),n[1].replace("b","t")]),e<0&&(n=[n[0].replace("l","r"),n[1].replace("r","l")]),t+r.height>a.height&&(n=[n[0].replace("b","t"),n[1].replace("t","b")]),(n=e+r.width>a.width?[n[0].replace("r","l"),n[1].replace("l","r")]:n).join("")}function D(e,t,n){var a=n.overlayInfo,n=n.containerInfo;return(t=t<0?0:t)+a.height>n.height&&(t=n.height-a.height),{left:e=(e=e<0?0:e)+a.width>n.width?n.width-a.width:e,top:t}}function ve(e){var t=e.target,n=e.overlay,a=e.container,r=e.scrollNode,o=e.placement,i=e.placementOffset,i=void 0===i?0:i,l=e.points,l=void 0===l?["tl","bl"]:l,s=e.offset,s=void 0===s?[0,0]:s,u=e.position,u=void 0===u?"absolute":u,d=e.beforePosition,c=e.autoAdjust,c=void 0===c||c,f=e.autoHideScrollOverflow,f=void 0===f||f,e=e.rtl,p="offsetWidth"in(p=n)&&"offsetHeight"in p?{width:p.offsetWidth,height:p.offsetHeight}:{width:(p=p.getBoundingClientRect()).width,height:p.height},h=p.width,p=p.height;if("fixed"===u)return m={config:{placement:void 0,points:void 0},style:{position:u,left:s[0],top:s[1]}},d?d(m,{overlay:{node:n,width:h,height:p}}):m;var m=t.getBoundingClientRect(),g=m.width,y=m.height,v=m.left,_=m.top,m=x(a),b=m.left,m=m.top,w=a.scrollWidth,M=a.scrollHeight,k=a.scrollTop,S=a.scrollLeft,b={targetInfo:{width:g,height:y,left:v,top:_},containerInfo:{left:b,top:m,width:w,height:M,scrollTop:k,scrollLeft:S},overlayInfo:{width:h,height:p},points:l,placementOffset:i,offset:s,container:a,rtl:e},m=C(o,b),w=m.left,M=m.top,k=m.points,S=function(e){for(var t=e;t;){var n=fe(t,"overflow");if(null!=n&&n.match(/auto|scroll|hidden/))return t;t=t.parentNode}return document.documentElement}(a),E=(c&&o&&L(w,M,S,b)&&(o!==(l=T(w,M,o,b))&&(M=L(s=(i=C(l,b)).left,e=i.top,S,b)&&l!==(m=T(s,e,l,b))?(w=(c=D((a=C(o=m,b)).left,a.top,b)).left,c.top):(o=l,w=s,e)),w=(i=D(w,M,b)).left,M=i.top),{config:{placement:o,points:k},style:{position:u,left:Math.round(w),top:Math.round(M)}});return f&&o&&null!=r&&r.length&&r.forEach(function(e){var e=e.getBoundingClientRect(),t=e.top,n=e.left,a=e.width,e=e.height;E.style.display=_+y=e.length?{done:!0}:{done:!1,value:e[n++]}};throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function o(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,a=new Array(t);nt.clientHeight&&0")),"shouldUpdatePosition"in r&&(delete t.shouldUpdatePosition,d.log.warning("Warning: [ shouldUpdatePosition ] is deprecated at [ ]")),"minMargin"in r&&o("minMargin","top/bottom",""),"isFullScreen"in r&&(r.overFlowScroll=!r.isFullScreen,delete t.isFullScreen,o("isFullScreen","overFlowScroll","")),t;return["target","offset","beforeOpen","onOpen","afterOpen","beforePosition","onPosition","cache","safeNode","wrapperClassName","container"].forEach(function(e){var t,n,a;e in r&&(o(e,"overlayProps."+e,"Dialog"),t=(n=r).overlayProps,n=(0,l.default)(n,["overlayProps"]),a=(0,i.default)(((a={})[e]=r[e],a),t||{}),delete n[e],r=(0,i.default)({overlayProps:a},n))}),r}n.displayName="Dialog",n.Inner=p.default,n.show=function(e){return!1!==u.default.getContextProps(e,"Dialog").warning&&(e=v(e,d.log.deprecated)),(0,h.show)(e)},n.alert=function(e){return!1!==u.default.getContextProps(e,"Dialog").warning&&(e=v(e,d.log.deprecated)),(0,h.alert)(e)},n.confirm=function(e){return!1!==u.default.getContextProps(e,"Dialog").warning&&(e=v(e,d.log.deprecated)),(0,h.confirm)(e)},n.success=function(e){return(0,h.success)(e)},n.error=function(e){return(0,h.error)(e)},n.notice=function(e){return(0,h.notice)(e)},n.warning=function(e){return(0,h.warning)(e)},n.help=function(e){return(0,h.help)(e)},n.withContext=h.withContext,t.default=u.default.config(n,{transform:v}),e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var i=v(n(2)),l=v(n(12)),a=v(n(8)),r=v(n(638)),o=v(n(643)),s=v(n(646)),u=v(n(647)),d=v(n(648)),c=v(n(649)),f=v(n(651)),p=v(n(652)),h=v(n(653)),m=v(n(656)),g=v(n(393)),y=v(n(394));function v(e){return e&&e.__esModule?e:{default:e}}var _=n(11).env.ieVersion,n=[s.default,f.default,u.default,d.default,c.default,o.default,h.default,m.default],b=n.reduce(function(e,t){return e=t(e)},r.default),n=(f.default._typeMark="lock",d.default._typeMark="expanded",s.default._typeMark="fixed",n.reduce(function(e,t){var n=!_;return e="lock"===t._typeMark?(n?(0,p.default):(0,f.default))(e):"expanded"===t._typeMark?n?(0,d.default)(e,!0):(0,d.default)(e):"fixed"===t._typeMark?n?(0,s.default)(e,!0):(0,s.default)(e):t(e)},r.default));b.Base=r.default,b.fixed=s.default,b.lock=f.default,b.selection=u.default,b.expanded=d.default,b.tree=o.default,b.virtual=c.default,b.list=h.default,b.sticky=m.default,b.GroupHeader=g.default,b.GroupFooter=y.default,b.StickyLock=a.default.config(n,{componentName:"Table"}),t.default=a.default.config(b,{componentName:"Table",transform:function(e,t){var n,a,r,o;return"expandedRowKeys"in e&&(t("expandedRowKeys","openRowKeys","Table"),o=(r=e).expandedRowKeys,r=(0,l.default)(r,["expandedRowKeys"]),e=(0,i.default)({openRowKeys:o},r)),"onExpandedChange"in e&&(t("onExpandedChange","onRowOpen","Table"),r=(o=e).onExpandedChange,o=(0,l.default)(o,["onExpandedChange"]),e=(0,i.default)({onRowOpen:r},o)),"isLoading"in e&&(t("isLoading","loading","Table"),o=(r=e).isLoading,r=(0,l.default)(r,["isLoading"]),e=(0,i.default)({loading:o},r)),"indentSize"in e&&(t("indentSize","indent","Table"),r=(o=e).indentSize,o=(0,l.default)(o,["indentSize"]),e=(0,i.default)({indent:r},o)),"optimization"in e&&(t("optimization","pure","Table"),o=(r=e).optimization,r=(0,l.default)(r,["optimization"]),e=(0,i.default)({pure:o},r)),"getRowClassName"in e&&(t("getRowClassName","getRowProps","Table"),n=(o=e).getRowClassName,a=o.getRowProps,r=(0,l.default)(o,["getRowClassName","getRowProps"]),e=n?(0,i.default)({getRowProps:function(){return(0,i.default)({className:n.apply(void 0,arguments)},a?a.apply(void 0,arguments):{})}},r):(0,i.default)({getRowProps:a},r)),"getRowProps"in e&&(t("getRowProps","rowProps","Table in 1.15.0"),r=(o=e).getRowProps,o=(0,l.default)(o,["getRowProps"]),e=(0,i.default)({rowProps:r},o)),"getCellProps"in e&&(t("getCellProps","cellProps","Table in 1.15.0"),o=(r=e).getCellProps,t=(0,l.default)(r,["getCellProps"]),e=(0,i.default)({cellProps:o},t)),e}}),e.exports=t.default},function(e,t,n){"use strict";n.d(t,"a",function(){return o});var a=n(89);function r(t,e){var n,a=Object.keys(t);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(t),e&&(n=n.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),a.push.apply(a,n)),a}function o(t){for(var e=1;e 16.8.0")},p.prototype.validate=function(e,t){this.validateCallback(e,t)},p.prototype.reset=function(e){var t=1","Select");t=s(e,t);return e.onInputUpdate&&(t.onSearch=e.onInputUpdate,t.showSearch=!0),t}}),t.default=a.default.config(r.default,{transform:s,exportNames:["focusInput","handleSearchClear"]}),e.exports=t.default},function(e,t,n){"use strict";n.d(t,"f",function(){return a}),n.d(t,"g",function(){return r}),n.d(t,"i",function(){return o}),n.d(t,"c",function(){return i}),n.d(t,"d",function(){return l}),n.d(t,"j",function(){return s}),n.d(t,"l",function(){return u}),n.d(t,"k",function(){return d}),n.d(t,"h",function(){return c}),n.d(t,"b",function(){return f}),n.d(t,"a",function(){return p}),n.d(t,"e",function(){return h});var a="docsite_language",r="LANGUAGE_SWITCH",o="__REDUX_DEVTOOLS_EXTENSION__",i="GET_STATE",l="GET_SUBSCRIBERS",s="REMOVE_SUBSCRIBERS",u="USER_LIST",d="ROLE_LIST",c="PERMISSIONS_LIST",f="GET_NAMESPACES",p="GET_CONFIGURATION",h=[10,20,30,50,100]},function(e,t,n){"use strict";function s(){var e=this.constructor.getDerivedStateFromProps(this.props,this.state);null!=e&&this.setState(e)}function u(t){this.setState(function(e){return null!=(e=this.constructor.getDerivedStateFromProps(t,e))?e:null}.bind(this))}function d(e,t){try{var n=this.props,a=this.state;this.props=e,this.state=t,this.__reactInternalSnapshotFlag=!0,this.__reactInternalSnapshot=this.getSnapshotBeforeUpdate(n,a)}finally{this.props=n,this.state=a}}function a(e){var t=e.prototype;if(!t||!t.isReactComponent)throw new Error("Can only polyfill class components");if("function"==typeof e.getDerivedStateFromProps||"function"==typeof t.getSnapshotBeforeUpdate){var n,a,r=null,o=null,i=null;if("function"==typeof t.componentWillMount?r="componentWillMount":"function"==typeof t.UNSAFE_componentWillMount&&(r="UNSAFE_componentWillMount"),"function"==typeof t.componentWillReceiveProps?o="componentWillReceiveProps":"function"==typeof t.UNSAFE_componentWillReceiveProps&&(o="UNSAFE_componentWillReceiveProps"),"function"==typeof t.componentWillUpdate?i="componentWillUpdate":"function"==typeof t.UNSAFE_componentWillUpdate&&(i="UNSAFE_componentWillUpdate"),null!==r||null!==o||null!==i)throw n=e.displayName||e.name,a="function"==typeof e.getDerivedStateFromProps?"getDerivedStateFromProps()":"getSnapshotBeforeUpdate()",Error("Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n"+n+" uses "+a+" but also contains the following legacy lifecycles:"+(null!==r?"\n "+r:"")+(null!==o?"\n "+o:"")+(null!==i?"\n "+i:"")+"\n\nThe above lifecycles should be removed. Learn more about this warning here:\nhttps://fb.me/react-async-component-lifecycle-hooks");if("function"==typeof e.getDerivedStateFromProps&&(t.componentWillMount=s,t.componentWillReceiveProps=u),"function"==typeof t.getSnapshotBeforeUpdate){if("function"!=typeof t.componentDidUpdate)throw new Error("Cannot polyfill getSnapshotBeforeUpdate() for components that do not define componentDidUpdate() on the prototype");t.componentWillUpdate=d;var l=t.componentDidUpdate;t.componentDidUpdate=function(e,t,n){n=this.__reactInternalSnapshotFlag?this.__reactInternalSnapshot:n;l.call(this,e,t,n)}}}return e}n.r(t),n.d(t,"polyfill",function(){return a}),d.__suppressDeprecationWarning=u.__suppressDeprecationWarning=s.__suppressDeprecationWarning=!0},function(e,t,n){"use strict";n.d(t,"a",function(){return r});var a=n(133);function r(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(e)){var n=[],a=!0,r=!1,o=void 0;try{for(var i,l=e[Symbol.iterator]();!(a=(i=l.next()).done)&&(n.push(i.value),!t||n.length!==t);a=!0);}catch(e){r=!0,o=e}finally{try{a||null==l.return||l.return()}finally{if(r)throw o}}return n}}(e,t)||Object(a.a)(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}},function(e,t,n){"use strict";n(43),n(540)},function(e,t,n){"use strict";t.__esModule=!0;var v=s(n(2)),a=s(n(4)),r=s(n(5)),o=s(n(6)),_=s(n(0)),i=s(n(3)),b=s(n(13)),w=s(n(62)),l=s(n(8)),M=n(11);function s(e){return e&&e.__esModule?e:{default:e}}u=_.default.Component,(0,o.default)(k,u),k.prototype.render=function(){var e=this.props,t=e.tip,n=e.visible,a=e.children,r=e.className,o=e.style,i=e.indicator,l=e.color,s=e.prefix,u=e.fullScreen,d=e.disableScroll,c=e.onVisibleChange,f=e.tipAlign,p=e.size,h=e.inline,m=e.rtl,e=e.safeNode,g=null,y=s+"loading-dot",p=(g=i||(i=l,p=(0,b.default)(((l={})[s+"loading-fusion-reactor"]=!0,l[s+"loading-medium-fusion-reactor"]="medium"===p,l)),_.default.createElement("div",{className:p,dir:m?"rtl":void 0},_.default.createElement("span",{className:y,style:{backgroundColor:i}}),_.default.createElement("span",{className:y,style:{backgroundColor:i}}),_.default.createElement("span",{className:y,style:{backgroundColor:i}}),_.default.createElement("span",{className:y,style:{backgroundColor:i}}))),(0,b.default)(((l={})[s+"loading"]=!0,l[s+"open"]=n,l[s+"loading-inline"]=h,l[r]=r,l))),y=(0,b.default)(((m={})[s+"loading-tip"]=!0,m[s+"loading-tip-fullscreen"]=u,m[s+"loading-right-tip"]="right"===f,m)),i=M.obj.pickOthers(k.propTypes,this.props),l=(0,b.default)(((h={})[s+"loading-component"]=n,h[s+"loading-wrap"]=!0,h));return u?[a,_.default.createElement(w.default,(0,v.default)({key:"overlay",hasMask:!0,align:"cc cc",safeNode:e,disableScroll:d},i,{className:r,style:o,visible:n,onRequestClose:c}),_.default.createElement("div",{className:y},_.default.createElement("div",{className:s+"loading-indicator"},g),_.default.createElement("div",{className:s+"loading-tip-content"},t),_.default.createElement("div",{className:s+"loading-tip-placeholder"},t)))]:_.default.createElement("div",(0,v.default)({className:p,style:o},i),n?_.default.createElement("div",{className:y},_.default.createElement("div",{className:s+"loading-indicator"},g),_.default.createElement("div",{className:s+"loading-tip-content"},t),_.default.createElement("div",{className:s+"loading-tip-placeholder"},t)):null,_.default.createElement("div",{className:l},n?_.default.createElement("div",{className:s+"loading-masker"}):null,a))},o=n=k,n.propTypes=(0,v.default)({},l.default.propTypes,{prefix:i.default.string,tip:i.default.any,tipAlign:i.default.oneOf(["right","bottom"]),visible:i.default.bool,onVisibleChange:i.default.func,className:i.default.string,style:i.default.object,size:i.default.oneOf(["large","medium"]),indicator:i.default.any,color:i.default.string,fullScreen:i.default.bool,disableScroll:i.default.bool,safeNode:i.default.any,children:i.default.any,inline:i.default.bool,rtl:i.default.bool}),n.defaultProps={prefix:"next-",visible:!0,onVisibleChange:M.func.noop,animate:null,tipAlign:"bottom",size:"large",inline:!0,disableScroll:!1};var u,i=o;function k(){return(0,a.default)(this,k),(0,r.default)(this,u.apply(this,arguments))}i.displayName="Loading",t.default=l.default.config(i),e.exports=t.default},function(e,t,n){"use strict";n(75),n(52),n(32),n(43),n(541)},function(e,t,n){"use strict";n(43),n(542)},function(M,e,t){"use strict";t.d(e,"a",function(){return a}),t.d(e,"b",function(){return B});var x=t(0),C=t.n(x),d=C.a.createContext(null);function s(){return n}var n=function(e){e()};var r={notify:function(){},get:function(){return[]}};function L(t,n){var o,i=r;function l(){e.onStateChange&&e.onStateChange()}function a(){var e,a,r;o||(o=n?n.addNestedSub(l):t.subscribe(l),e=s(),r=a=null,i={clear:function(){r=a=null},notify:function(){e(function(){for(var e=a;e;)e.callback(),e=e.next})},get:function(){for(var e=[],t=a;t;)e.push(t),t=t.next;return e},subscribe:function(e){var t=!0,n=r={callback:e,next:null,prev:r};return n.prev?n.prev.next=n:a=n,function(){t&&null!==a&&(t=!1,n.next?n.next.prev=n.prev:r=n.prev,n.prev?n.prev.next=n.next:a=n.next)}}})}var e={addNestedSub:function(e){return a(),i.subscribe(e)},notifyNestedSubs:function(){i.notify()},handleChangeWrapper:l,isSubscribed:function(){return Boolean(o)},trySubscribe:a,tryUnsubscribe:function(){o&&(o(),o=void 0,i.clear(),i=r)},getListeners:function(){return i}};return e}var o="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?x.useLayoutEffect:x.useEffect;var a=function(e){var t=e.store,n=e.context,e=e.children,a=Object(x.useMemo)(function(){var e=L(t);return{store:t,subscription:e}},[t]),r=Object(x.useMemo)(function(){return t.getState()},[t]),n=(o(function(){var e=a.subscription;return e.onStateChange=e.notifyNestedSubs,e.trySubscribe(),r!==t.getState()&&e.notifyNestedSubs(),function(){e.tryUnsubscribe(),e.onStateChange=null}},[a,r]),n||d);return C.a.createElement(n.Provider,{value:a},e)},T=t(42),D=t(54),e=t(100),c=t.n(e),O=t(412),f=["getDisplayName","methodName","renderCountProp","shouldHandleStateChanges","storeKey","withRef","forwardRef","context"],N=["reactReduxForwardedRef"],P=[],j=[null,null];function Y(e,t){e=e[1];return[t.payload,e+1]}function I(e,t,n){o(function(){return e.apply(void 0,t)},n)}function R(e,t,n,a,r,o,i){e.current=a,t.current=r,n.current=!1,o.current&&(o.current=null,i())}function A(e,a,t,r,o,i,l,s,u,d){var c,f;if(e)return c=!1,f=null,e=function(){if(!c){var e,t,n=a.getState();try{e=r(n,o.current)}catch(e){f=t=e}t||(f=null),e===i.current?l.current||u():(i.current=e,s.current=e,l.current=!0,d({type:"STORE_UPDATED",payload:{error:t}}))}},t.onStateChange=e,t.trySubscribe(),e(),function(){if(c=!0,t.tryUnsubscribe(),t.onStateChange=null,f)throw f}}var H=function(){return[null,0]};function i(k,e){var e=e=void 0===e?{}:e,t=e.getDisplayName,r=void 0===t?function(e){return"ConnectAdvanced("+e+")"}:t,t=e.methodName,o=void 0===t?"connectAdvanced":t,t=e.renderCountProp,i=void 0===t?void 0:t,t=e.shouldHandleStateChanges,S=void 0===t||t,t=e.storeKey,l=void 0===t?"store":t,t=(e.withRef,e.forwardRef),s=void 0!==t&&t,t=e.context,t=void 0===t?d:t,u=Object(D.a)(e,f),E=t;return function(b){var e=b.displayName||b.name||"Component",t=r(e),w=Object(T.a)({},u,{getDisplayName:r,methodName:o,renderCountProp:i,shouldHandleStateChanges:S,storeKey:l,displayName:t,wrappedComponentName:e,WrappedComponent:b}),e=u.pure;var M=e?x.useMemo:function(e){return e()};function n(n){var e=Object(x.useMemo)(function(){var e=n.reactReduxForwardedRef,t=Object(D.a)(n,N);return[n.context,e,t]},[n]),t=e[0],a=e[1],r=e[2],o=Object(x.useMemo)(function(){return t&&t.Consumer&&Object(O.isContextConsumer)(C.a.createElement(t.Consumer,null))?t:E},[t,E]),i=Object(x.useContext)(o),l=Boolean(n.store)&&Boolean(n.store.getState)&&Boolean(n.store.dispatch),s=(Boolean(i)&&Boolean(i.store),(l?n:i).store),u=Object(x.useMemo)(function(){return k(s.dispatch,w)},[s]),e=Object(x.useMemo)(function(){if(!S)return j;var e=L(s,l?null:i.subscription),t=e.notifyNestedSubs.bind(e);return[e,t]},[s,l,i]),d=e[0],e=e[1],c=Object(x.useMemo)(function(){return l?i:Object(T.a)({},i,{subscription:d})},[l,i,d]),f=Object(x.useReducer)(Y,P,H),p=f[0][0],f=f[1];if(p&&p.error)throw p.error;var h=Object(x.useRef)(),m=Object(x.useRef)(r),g=Object(x.useRef)(),y=Object(x.useRef)(!1),v=M(function(){return g.current&&r===m.current?g.current:u(s.getState(),r)},[s,p,r]),_=(I(R,[m,h,y,r,v,g,e]),I(A,[S,s,d,u,m,h,y,g,e,f],[s,d,u]),Object(x.useMemo)(function(){return C.a.createElement(b,Object(T.a)({},v,{ref:a}))},[a,b,v]));return Object(x.useMemo)(function(){return S?C.a.createElement(o.Provider,{value:c},_):_},[o,_,c])}var a=e?C.a.memo(n):n;return a.WrappedComponent=b,a.displayName=n.displayName=t,s?((e=C.a.forwardRef(function(e,t){return C.a.createElement(a,Object(T.a)({},e,{reactReduxForwardedRef:t}))})).displayName=t,e.WrappedComponent=b,c()(e,b)):c()(a,b)}}function l(e,t){return e===t?0!==e||0!==t||1/e==1/t:e!=e&&t!=t}function m(e,t){if(!l(e,t)){if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),a=Object.keys(t);if(n.length!==a.length)return!1;for(var r=0;re?t.splice(e,t.length-e,n):t.push(n),i({action:"PUSH",location:n,index:e,entries:t}))})},replace:function(e,t){var n=O(e,t,l(),u.location);o.confirmTransitionTo(n,"REPLACE",a,function(e){e&&i({action:"REPLACE",location:u.entries[u.index]=n})})},go:s,goBack:function(){s(-1)},goForward:function(){s(1)},canGo:function(e){return 0<=(e=u.index+e)&&ex',"Tag"),"readonly"!==n&&"interactive"!==n||r.log.warning("Warning: [ shape="+n+" ] is deprecated at [ Tag ]"),"secondary"===a&&r.log.warning("Warning: [ type=secondary ] is deprecated at [ Tag ]"),["count","marked","value","onChange"].forEach(function(e){e in t&&r.log.warning("Warning: [ "+e+" ] is deprecated at [ Tag ]")}),("selected"in t||"defaultSelected"in t)&&r.log.warning("Warning: [ selected|defaultSelected ] is deprecated at [ Tag ], use [ checked|defaultChecked ] at [ Tag.Selectable ] instead of it"),"closed"in t&&r.log.warning("Warning: [ closed ] is deprecated at [ Tag ], use [ onClose ] at [ Tag.Closeable ] instead of it"),"onSelect"in t&&e("onSelect","","Tag"),"afterClose"in t&&r.log.warning("Warning: [ afterClose ] is deprecated at [ Tag ], use [ afterClose ] at [ Tag.Closeable ] instead of it"),t}});o.Group=a.default.config(i.default),o.Selectable=a.default.config(l.default),o.Closable=a.default.config(n.default),o.Closeable=o.Closable,t.default=o,e.exports=t.default},function(e,t,n){"use strict";n(70),n(447)},function(e,t){e=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=e)},function(e,t){e=e.exports={version:"2.6.12"};"number"==typeof __e&&(__e=e)},function(e,t,n){e.exports=!n(108)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,t,n){"use strict";t.__esModule=!0;var a=o(n(344)),r=o(n(526)),n=o(n(527));function o(e){return e&&e.__esModule?e:{default:e}}a.default.Expand=r.default,a.default.OverlayAnimate=n.default,t.default=a.default,e.exports=t.default},function(e,t,n){"use strict";n(43),n(70),n(125),n(109),n(538)},function(e,t,n){"use strict";n.d(t,"a",function(){return r});function v(e,t){return"function"==typeof e?e(t):e}function _(e,t){return"string"==typeof e?Object(d.c)(e,null,null,t):e}function u(e){return e}var b=n(40),a=n(58),t=n(0),w=n.n(t),d=n(55),M=n(42),k=n(54),S=n(57),r=(w.a.Component,function(r){function e(){for(var e,t=arguments.length,n=new Array(t),a=0;athis.menuNode.clientHeight&&(this.menuNode.clientHeight+this.menuNode.scrollTop<(e=this.itemNode.offsetTop+this.itemNode.offsetHeight)?this.menuNode.scrollTop=e-this.menuNode.clientHeight:this.itemNode.offsetTop"===(d=e.charAt(o+1))){r+="(",o++,0[0-9][0-9][0-9][0-9])-(?[0-9][0-9]?)-(?[0-9][0-9]?)(?:(?:[Tt]|[ \t]+)(?[0-9][0-9]?):(?[0-9][0-9]):(?[0-9][0-9])(?:.(?[0-9]*))?(?:[ \t]*(?Z|(?[-+])(?[0-9][0-9]?)(?::(?[0-9][0-9]))?))?)?$","i"),r.LOCAL_TIMEZONE_OFFSET=60*(new Date).getTimezoneOffset()*1e3,r.trim=function(e,t){var n,a;return null==(n=this.REGEX_LEFT_TRIM_BY_CHAR[t=null==t?"\\s":t])&&(this.REGEX_LEFT_TRIM_BY_CHAR[t]=n=new RegExp("^"+t+t+"*")),n.lastIndex=0,null==(a=this.REGEX_RIGHT_TRIM_BY_CHAR[t])&&(this.REGEX_RIGHT_TRIM_BY_CHAR[t]=a=new RegExp(t+""+t+"*$")),a.lastIndex=0,e.replace(n,"").replace(a,"")},r.ltrim=function(e,t){var n;return null==(n=this.REGEX_LEFT_TRIM_BY_CHAR[t=null==t?"\\s":t])&&(this.REGEX_LEFT_TRIM_BY_CHAR[t]=n=new RegExp("^"+t+t+"*")),n.lastIndex=0,e.replace(n,"")},r.rtrim=function(e,t){var n;return null==(n=this.REGEX_RIGHT_TRIM_BY_CHAR[t=null==t?"\\s":t])&&(this.REGEX_RIGHT_TRIM_BY_CHAR[t]=n=new RegExp(t+""+t+"*$")),n.lastIndex=0,e.replace(n,"")},r.isEmpty=function(e){return!e||""===e||"0"===e||e instanceof Array&&0===e.length||this.isEmptyObject(e)},r.isEmptyObject=function(t){var n;return t instanceof Object&&0===function(){var e=[];for(n in t)a.call(t,n)&&e.push(n);return e}().length},r.subStrCount=function(e,t,n,a){var r,o,i,l,s=0;for(e=""+e,t=""+t,null!=n&&(e=e.slice(n)),n=(e=null!=a?e.slice(0,a):e).length,l=t.length,r=o=0,i=n;0<=i?o>6)+t(128|63&e):e<65536?t(224|e>>12)+t(128|e>>6&63)+t(128|63&e):t(240|e>>18)+t(128|e>>12&63)+t(128|e>>6&63)+t(128|63&e)},r.parseBoolean=function(e,t){var n;return null==t&&(t=!0),"string"==typeof e?(n=e.toLowerCase(),!(!t&&"no"===n)&&("0"!==n&&("false"!==n&&""!==n))):!!e},r.isNumeric=function(e){return this.REGEX_SPACES.lastIndex=0,"number"==typeof e||"string"==typeof e&&!isNaN(e)&&""!==e.replace(this.REGEX_SPACES,"")},r.stringToDate=function(e){var t,n,a,r,o,i,l,s,u;if(null==e||!e.length)return null;if(!(e=this.PATTERN_DATE.exec(e)))return null;if(u=parseInt(e.year,10),i=parseInt(e.month,10)-1,n=parseInt(e.day,10),null==e.hour)return t=new Date(Date.UTC(u,i,n));if(r=parseInt(e.hour,10),o=parseInt(e.minute,10),l=parseInt(e.second,10),null!=e.fraction){for(a=e.fraction.slice(0,3);a.length<3;)a+="0";a=parseInt(a,10)}else a=0;return null!=e.tz&&(s=6e4*(60*parseInt(e.tz_hour,10)+(null!=e.tz_minute?parseInt(e.tz_minute,10):0)),"-"===e.tz_sign&&(s*=-1)),t=new Date(Date.UTC(u,i,n,r,o,l,a)),s&&t.setTime(t.getTime()-s),t},r.strRepeat=function(e,t){for(var n="",a=0;ae.length)&&(t=e.length);for(var n=0,a=new Array(t);ndocument.F=Object<\/script>"),e.close(),u=e.F;t--;)delete u[s][i[t]];return u()};e.exports=Object.create||function(e,t){var n;return null!==e?(a[s]=r(e),n=new a,a[s]=null,n[l]=e):n=u(),void 0===t?n:o(n,t)}},function(e,t,n){var a=n(84).f,r=n(85),o=n(95)("toStringTag");e.exports=function(e,t,n){e&&!r(e=n?e:e.prototype,o)&&a(e,o,{configurable:!0,value:t})}},function(e,t,n){t.f=n(95)},function(e,t,n){var a=n(76),r=n(77),o=n(121),i=n(153),l=n(84).f;e.exports=function(e){var t=r.Symbol||(r.Symbol=!o&&a.Symbol||{});"_"==e.charAt(0)||e in t||l(t,e,{value:i.f(e)})}},function(e,t,n){"use strict";t.__esModule=!0;var a=d(n(205)),r=d(n(501)),o=d(n(502)),i=d(n(503)),l=d(n(504)),s=d(n(505)),u=d(n(506));function d(e){return e&&e.__esModule?e:{default:e}}n(507),a.default.extend(s.default),a.default.extend(l.default),a.default.extend(r.default),a.default.extend(o.default),a.default.extend(i.default),a.default.extend(u.default),a.default.locale("zh-cn");n=a.default;n.isSelf=a.default.isDayjs,a.default.localeData(),t.default=n,e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var v=c(n(2)),o=c(n(4)),i=c(n(5)),a=c(n(6)),r=n(0),_=c(r),l=c(n(3)),s=n(30),b=c(n(13)),u=c(n(44)),w=c(n(24)),M=c(n(79)),d=c(n(8)),k=n(11);function c(e){return e&&e.__esModule?e:{default:e}}function f(){}p=r.Component,(0,a.default)(S,p),S.getDerivedStateFromProps=function(e){return"visible"in e?{visible:e.visible}:{}},S.prototype.render=function(){var e,t=this.props,n=t.prefix,a=(t.pure,t.className),r=t.style,o=t.type,i=t.shape,l=t.size,s=t.title,u=t.children,d=(t.defaultVisible,t.visible,t.iconType),c=t.closeable,f=(t.onClose,t.afterClose),p=t.animation,h=t.rtl,t=t.locale,m=(0,v.default)({},k.obj.pickOthers(Object.keys(S.propTypes),this.props)),g=this.state.visible,y=n+"message",o=(0,b.default)(((e={})[y]=!0,e[n+"message-"+o]=o,e[""+n+i]=i,e[""+n+l]=l,e[n+"title-content"]=!!s,e[n+"only-content"]=!s&&!!u,e[a]=a,e)),i=g?_.default.createElement("div",(0,v.default)({role:"alert",style:r},m,{className:o,dir:h?"rtl":void 0}),c?_.default.createElement("a",{role:"button","aria-label":t.closeAriaLabel,className:y+"-close",onClick:this.onClose},_.default.createElement(w.default,{type:"close"})):null,!1!==d?_.default.createElement(w.default,{className:y+"-symbol "+(!d&&y+"-symbol-icon"),type:d}):null,s?_.default.createElement("div",{className:y+"-title"},s):null,u?_.default.createElement("div",{className:y+"-content"},u):null):null;return p?_.default.createElement(M.default.Expand,{animationAppear:!1,afterLeave:f},i):i},r=n=S,n.propTypes={prefix:l.default.string,pure:l.default.bool,className:l.default.string,style:l.default.object,type:l.default.oneOf(["success","warning","error","notice","help","loading"]),shape:l.default.oneOf(["inline","addon","toast"]),size:l.default.oneOf(["medium","large"]),title:l.default.node,children:l.default.node,defaultVisible:l.default.bool,visible:l.default.bool,iconType:l.default.oneOfType([l.default.string,l.default.bool]),closeable:l.default.bool,onClose:l.default.func,afterClose:l.default.func,animation:l.default.bool,locale:l.default.object,rtl:l.default.bool},n.defaultProps={prefix:"next-",pure:!1,type:"success",shape:"inline",size:"medium",defaultVisible:!0,closeable:!1,onClose:f,afterClose:f,animation:!0,locale:u.default.Message};var p,a=r;function S(){var e,t;(0,o.default)(this,S);for(var n=arguments.length,a=Array(n),r=0;r=n.length?(s=!!(c=h(o,u)))&&"get"in c&&!("originalValue"in c.get)?c.get:o[u]:(s=_(o,u),o[u]),s&&!i&&(y[d]=o)}}return o}},function(e,t,n){"use strict";n=n(604);e.exports=Function.prototype.bind||n},function(e,t,n){"use strict";var a=String.prototype.replace,r=/%20/g,o="RFC1738",i="RFC3986";e.exports={default:i,formatters:{RFC1738:function(e){return a.call(e,r,"+")},RFC3986:function(e){return String(e)}},RFC1738:o,RFC3986:i}},function(e,t,n){"use strict";t.__esModule=!0,t.default=void 0;var d=s(n(2)),a=s(n(4)),r=s(n(5)),o=s(n(6)),c=n(0),f=s(c),i=s(n(3)),p=s(n(13)),h=s(n(24)),l=n(11),m=s(n(98));function s(e){return e&&e.__esModule?e:{default:e}}var u,g=l.func.bindCtx,y=l.obj.pickOthers,i=(u=c.Component,(0,o.default)(v,u),v.prototype.getSelected=function(){var e=this.props,t=e._key,n=e.root,e=e.selected,a=n.props.selectMode,n=n.state.selectedKeys;return e||!!a&&-1e.length&&e.every(function(e,t){return e===n[t]})},t.isAvailablePos=function(e,t,n){var n=n[t],a=n.type,n=n.disabled;return r(e,t)&&("item"===a&&!n||"submenu"===a)});t.getFirstAvaliablelChildKey=function(t,n){var e=Object.keys(n).find(function(e){return a(t+"-0",e,n)});return e?n[e].key:null},t.getChildSelected=function(e){var t=e.selectMode,n=e.selectedKeys,a=e._k2n,e=e._key;if(!a)return!1;var r=(a[e]&&a[e].pos)+"-";return!!t&&n.some(function(e){return a[e]&&0===a[e].pos.indexOf(r)})}},function(e,t,n){"use strict";n(43),n(32),n(626)},function(e,t,n){var o=n(639),i=Object.prototype.hasOwnProperty;function l(e){return Array.isArray(e)?"array":typeof e}function s(e,t){var n,a=0,r=0;for(n in e)if(i.call(e,n)){if("style"===n){if(!o(e[n],t[n]))return!1}else if("children"!==n&&e[n]!==t[n])return!1;a++}for(n in t)i.call(t,n)&&r++;return a===r&&function e(t,n){var a=l(t);if(a!==l(n))return!1;switch(a){case"array":if(t.length!==n.length)return!1;for(var r=0;r=u,u=(0,O.default)(((u={})[n+"upload-inner"]=!0,u[n+"hidden"]=x,u)),C=this.props.children;if("card"===r&&(r=(0,O.default)(((r={})[n+"upload-card"]=!0,r[n+"disabled"]=s,r)),C=D.default.createElement("div",{className:r},D.default.createElement(P.default,{size:"large",type:"add",className:n+"upload-add-icon"}),D.default.createElement("div",{tabIndex:"0",role:"button",className:n+"upload-text"},C))),b)return"function"==typeof w?(b=(0,O.default)(((r={})[n+"form-preview"]=!0,r[o]=!!o,r)),D.default.createElement("div",{style:i,className:b},w(this.state.value,this.props))):t?D.default.createElement(Y.default,{isPreview:!0,listType:t,style:i,className:o,value:this.state.value}):null;n=s?N.func.prevent:p,r=N.obj.pickAttrsWith(this.props,"data-");return D.default.createElement("div",(0,L.default)({className:f,style:i},r),D.default.createElement(j.default,(0,L.default)({},e,{name:M,beforeUpload:c,dragable:a,disabled:s||x,className:u,onSelect:this.onSelect,onDrop:this.onDrop,onProgress:this.onProgress,onSuccess:this.onSuccess,onError:this.onError,ref:this.saveUploaderRef}),C),t||g?D.default.createElement(Y.default,{useDataURL:l,fileNameRender:k,actionRender:S,uploader:this,listType:t,value:this.state.value,closable:d,onRemove:n,progressProps:v,onCancel:h,onPreview:m,extraRender:y,rtl:_,previewOnFileName:E}):null)},i=u=h,u.displayName="Upload",u.propTypes=(0,L.default)({},d.default.propTypes,Y.default.propTypes,{prefix:l.default.string.isRequired,action:l.default.string,value:l.default.array,defaultValue:l.default.array,shape:l.default.oneOf(["card"]),listType:l.default.oneOf(["text","image","card"]),list:l.default.any,name:l.default.string,data:l.default.oneOfType([l.default.object,l.default.func]),formatter:l.default.func,limit:l.default.number,timeout:l.default.number,dragable:l.default.bool,closable:l.default.bool,useDataURL:l.default.bool,disabled:l.default.bool,onSelect:l.default.func,onProgress:l.default.func,onChange:l.default.func,onSuccess:l.default.func,afterSelect:l.default.func,onRemove:l.default.func,onError:l.default.func,beforeUpload:l.default.func,onDrop:l.default.func,className:l.default.string,style:l.default.object,children:l.default.node,autoUpload:l.default.bool,request:l.default.func,progressProps:l.default.object,rtl:l.default.bool,isPreview:l.default.bool,renderPreview:l.default.func,fileKeyName:l.default.string,fileNameRender:l.default.func,actionRender:l.default.func,previewOnFileName:l.default.bool}),u.defaultProps=(0,L.default)({},d.default.defaultProps,{prefix:"next-",limit:1/0,autoUpload:!0,closable:!0,onSelect:n,onProgress:n,onChange:n,onSuccess:n,onRemove:n,onError:n,onDrop:n,beforeUpload:n,afterSelect:n,previewOnFileName:!1}),a=function(){var u=this;this.onSelect=function(e){var t,n,a=u.props,r=a.autoUpload,o=a.afterSelect,i=a.onSelect,a=a.limit,l=u.state.value.length+e.length,s=a-u.state.value.length;s<=0||(t=e=e.map(function(e){e=(0,c.fileToObject)(e);return e.state="selected",e}),n=[],ai||l+a.width>o):t<0||e<0||t+a.height>u.height||e+a.width>u.width}function T(e,t,n,a){var r=a.overlayInfo,a=a.containerInfo,n=n.split("");return 1===n.length&&n.push(""),t<0&&(n=[n[0].replace("t","b"),n[1].replace("b","t")]),e<0&&(n=[n[0].replace("l","r"),n[1].replace("r","l")]),t+r.height>a.height&&(n=[n[0].replace("b","t"),n[1].replace("t","b")]),(n=e+r.width>a.width?[n[0].replace("r","l"),n[1].replace("l","r")]:n).join("")}function D(e,t,n){var a=n.overlayInfo,n=n.containerInfo;return(t=t<0?0:t)+a.height>n.height&&(t=n.height-a.height),{left:e=(e=e<0?0:e)+a.width>n.width?n.width-a.width:e,top:t}}function ve(e){var t=e.target,n=e.overlay,a=e.container,r=e.scrollNode,o=e.placement,i=e.placementOffset,i=void 0===i?0:i,l=e.points,l=void 0===l?["tl","bl"]:l,s=e.offset,s=void 0===s?[0,0]:s,u=e.position,u=void 0===u?"absolute":u,d=e.beforePosition,c=e.autoAdjust,c=void 0===c||c,f=e.autoHideScrollOverflow,f=void 0===f||f,e=e.rtl,p="offsetWidth"in(p=n)&&"offsetHeight"in p?{width:p.offsetWidth,height:p.offsetHeight}:{width:(p=p.getBoundingClientRect()).width,height:p.height},h=p.width,p=p.height;if("fixed"===u)return m={config:{placement:void 0,points:void 0},style:{position:u,left:s[0],top:s[1]}},d?d(m,{overlay:{node:n,width:h,height:p}}):m;var m=t.getBoundingClientRect(),g=m.width,y=m.height,v=m.left,_=m.top,m=x(a),b=m.left,m=m.top,w=a.scrollWidth,M=a.scrollHeight,k=a.scrollTop,S=a.scrollLeft,b={targetInfo:{width:g,height:y,left:v,top:_},containerInfo:{left:b,top:m,width:w,height:M,scrollTop:k,scrollLeft:S},overlayInfo:{width:h,height:p},points:l,placementOffset:i,offset:s,container:a,rtl:e},m=C(o,b),w=m.left,M=m.top,k=m.points,S=function(e){for(var t=e;t;){var n=fe(t,"overflow");if(null!=n&&n.match(/auto|scroll|hidden/))return t;t=t.parentNode}return document.documentElement}(a),E=(c&&o&&L(w,M,S,b)&&(o!==(l=T(w,M,o,b))&&(M=L(s=(i=C(l,b)).left,e=i.top,S,b)&&l!==(m=T(s,e,l,b))?(w=(c=D((a=C(o=m,b)).left,a.top,b)).left,c.top):(o=l,w=s,e)),w=(i=D(w,M,b)).left,M=i.top),{config:{placement:o,points:k},style:{position:u,left:Math.round(w),top:Math.round(M)}});return f&&o&&null!=r&&r.length&&r.forEach(function(e){var e=e.getBoundingClientRect(),t=e.top,n=e.left,a=e.width,e=e.height;E.style.display=_+y=e.length?{done:!0}:{done:!1,value:e[n++]}};throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function o(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,a=new Array(t);nt.clientHeight&&0r;)!i(a,n=t[r++])||~s(o,n)||o.push(n);return o}},function(e,t,n){var a=n(195);e.exports=Object("z").propertyIsEnumerable(0)?Object:function(e){return"String"==a(e)?e.split(""):Object(e)}},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t,n){"use strict";function y(){return this}var v=n(120),_=n(90),b=n(197),w=n(91),M=n(150),k=n(472),S=n(152),E=n(475),x=n(94)("iterator"),C=!([].keys&&"next"in[].keys()),L="values";e.exports=function(e,t,n,a,r,o,i){k(n,t,a);function l(e){if(!C&&e in f)return f[e];switch(e){case"keys":case L:return function(){return new n(this,e)}}return function(){return new n(this,e)}}var s,u,a=t+" Iterator",d=r==L,c=!1,f=e.prototype,p=f[x]||f["@@iterator"]||r&&f[r],h=p||l(r),m=r?d?l("entries"):h:void 0,g="Array"==t&&f.entries||p;if(g&&(g=E(g.call(new e)))!==Object.prototype&&g.next&&(S(g,a,!0),v||"function"==typeof g[x]||w(g,x,y)),d&&p&&p.name!==L&&(c=!0,h=function(){return p.call(this)}),v&&!i||!C&&!c&&f[x]||w(f,x,h),M[t]=h,M[a]=y,r)if(s={values:d?h:l(L),keys:o?h:l("keys"),entries:m},i)for(u in s)u in f||b(f,u,s[u]);else _(_.P+_.F*(C||c),t,s);return s}},function(e,t,n){e.exports=n(91)},function(e,t,n){var a=n(193),r=n(147).concat("length","prototype");t.f=Object.getOwnPropertyNames||function(e){return a(e,r)}},function(e,t,n){var a=n(122),r=n(118),o=n(93),i=n(142),l=n(85),s=n(191),u=Object.getOwnPropertyDescriptor;t.f=n(77)?u:function(e,t){if(e=o(e),t=i(t,!0),s)try{return u(e,t)}catch(e){}if(l(e,t))return r(!a.f.call(e,t),e[t])}},function(e,t,n){"use strict";t.__esModule=!0;var v=a(n(2));t.default=function(e,t,n){var a=e.prefix,r=e.locale,o=(e.defaultPropsConfig,e.pure),i=e.rtl,l=e.device,s=e.popupContainer,e=e.errorBoundary,u=t.nextPrefix,d=t.nextLocale,c=t.nextDefaultPropsConfig,f=t.nextPure,p=t.nextWarning,h=t.nextRtl,m=t.nextDevice,g=t.nextPopupContainer,t=t.nextErrorBoundary,a=a||u,u=void 0,y=n;switch(n){case"DatePicker2":y="DatePicker";break;case"Calendar2":y="Calendar";break;case"TimePicker2":y="TimePicker"}d&&(u=d[y])&&(u.momentLocale=d.momentLocale);n=void 0;r?n=b.obj.deepMerge({},_.default[y],u,r):u&&(n=b.obj.deepMerge({},_.default[y],u));d="boolean"==typeof o?o:f,r="boolean"==typeof i?i:h,u=(0,v.default)({},w(t),w(e));"open"in u||(u.open=!1);return{prefix:a,locale:n,pure:d,rtl:r,warning:p,defaultPropsConfig:c||{},device:l||m||void 0,popupContainer:s||g,errorBoundary:u}};var _=a(n(44)),b=n(10);function a(e){return e&&e.__esModule?e:{default:e}}var w=function(e){return null==e?{}:"boolean"==typeof e?{open:e}:(0,v.default)({open:!0},e)};e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0,t.matches=t.hasDOM=void 0;var a=n(37),r=(a=a)&&a.__esModule?a:{default:a},o=(t.hasClass=s,t.addClass=u,t.removeClass=d,t.toggleClass=function(e,t){if(!l||!e)return!1;{var n;return e.classList?e.classList.toggle(t):(((n=s(e,t))?d:u)(e,t,!0),!n)}},t.getNodeHozWhitespace=function(e){var t=m(e,"paddingLeft"),n=m(e,"paddingRight"),a=m(e,"marginLeft"),e=m(e,"marginRight");return t+n+a+e},t.getStyle=m,t.setStyle=g,t.scrollbar=v,t.hasScroll=function(e){if("hidden"===m(e,"overflow"))return!1;var t=e.parentNode;return t&&t.scrollHeight>t.clientHeight&&0=t?e:""+Array(t+1-a.length).join(n)+e},t={s:o,z:function(e){var t=-e.utcOffset(),n=Math.abs(t),a=Math.floor(n/60),r=n%60;return(t<=0?"+":"-")+o(a,2,"0")+":"+o(r,2,"0")},m:function e(t,n){if(t.date()1)return e(i[0])}else{var l=t.name;S[l]=t,r=l}return!a&&r&&(k=r),r||!a&&k},E=function(e,t){if(a(e))return e.clone();var n="object"==typeof t?t:{};return n.date=e,n.args=arguments,new l(n)},x=t,l=(x.l=r,x.i=a,x.w=function(e,t){return E(e,{locale:t.$L,utc:t.$u,x:t.$x,$offset:t.$offset})},function(){function e(e){this.$L=r(e.locale,null,!0),this.parse(e)}var t=e.prototype;return t.parse=function(e){this.$d=function(e){var t=e.date,n=e.utc;if(null===t)return new Date(NaN);if(x.u(t))return new Date;if(t instanceof Date)return new Date(t);if("string"==typeof t&&!/Z$/i.test(t)){var a=t.match(i);if(a){var r=a[2]-1||0,o=(a[7]||"0").substring(0,3);return n?new Date(Date.UTC(a[1],r,a[3]||1,a[4]||0,a[5]||0,a[6]||0,o)):new Date(a[1],r,a[3]||1,a[4]||0,a[5]||0,a[6]||0,o)}}return new Date(t)}(e),this.$x=e.x||{},this.init()},t.init=function(){var e=this.$d;this.$y=e.getFullYear(),this.$M=e.getMonth(),this.$D=e.getDate(),this.$W=e.getDay(),this.$H=e.getHours(),this.$m=e.getMinutes(),this.$s=e.getSeconds(),this.$ms=e.getMilliseconds()},t.$utils=function(){return x},t.isValid=function(){return!(this.$d.toString()===w)},t.isSame=function(e,t){var n=E(e);return this.startOf(t)<=n&&n<=this.endOf(t)},t.isAfter=function(e,t){return E(e)r;)!i(a,n=t[r++])||~s(o,n)||o.push(n);return o}},function(e,t,n){var a=n(195);e.exports=Object("z").propertyIsEnumerable(0)?Object:function(e){return"String"==a(e)?e.split(""):Object(e)}},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t,n){"use strict";function y(){return this}var v=n(121),_=n(91),b=n(197),w=n(92),M=n(150),k=n(472),S=n(152),E=n(475),x=n(95)("iterator"),C=!([].keys&&"next"in[].keys()),L="values";e.exports=function(e,t,n,a,r,o,i){k(n,t,a);function l(e){if(!C&&e in f)return f[e];switch(e){case"keys":case L:return function(){return new n(this,e)}}return function(){return new n(this,e)}}var s,u,a=t+" Iterator",d=r==L,c=!1,f=e.prototype,p=f[x]||f["@@iterator"]||r&&f[r],h=p||l(r),m=r?d?l("entries"):h:void 0,g="Array"==t&&f.entries||p;if(g&&(g=E(g.call(new e)))!==Object.prototype&&g.next&&(S(g,a,!0),v||"function"==typeof g[x]||w(g,x,y)),d&&p&&p.name!==L&&(c=!0,h=function(){return p.call(this)}),v&&!i||!C&&!c&&f[x]||w(f,x,h),M[t]=h,M[a]=y,r)if(s={values:d?h:l(L),keys:o?h:l("keys"),entries:m},i)for(u in s)u in f||b(f,u,s[u]);else _(_.P+_.F*(C||c),t,s);return s}},function(e,t,n){e.exports=n(92)},function(e,t,n){var a=n(193),r=n(147).concat("length","prototype");t.f=Object.getOwnPropertyNames||function(e){return a(e,r)}},function(e,t,n){var a=n(123),r=n(119),o=n(94),i=n(142),l=n(85),s=n(191),u=Object.getOwnPropertyDescriptor;t.f=n(78)?u:function(e,t){if(e=o(e),t=i(t,!0),s)try{return u(e,t)}catch(e){}if(l(e,t))return r(!a.f.call(e,t),e[t])}},function(e,t,n){"use strict";t.__esModule=!0;var v=a(n(2));t.default=function(e,t,n){var a=e.prefix,r=e.locale,o=(e.defaultPropsConfig,e.pure),i=e.rtl,l=e.device,s=e.popupContainer,e=e.errorBoundary,u=t.nextPrefix,d=t.nextLocale,c=t.nextDefaultPropsConfig,f=t.nextPure,p=t.nextWarning,h=t.nextRtl,m=t.nextDevice,g=t.nextPopupContainer,t=t.nextErrorBoundary,a=a||u,u=void 0,y=n;switch(n){case"DatePicker2":y="DatePicker";break;case"Calendar2":y="Calendar";break;case"TimePicker2":y="TimePicker"}d&&(u=d[y])&&(u.momentLocale=d.momentLocale);n=void 0;r?n=b.obj.deepMerge({},_.default[y],u,r):u&&(n=b.obj.deepMerge({},_.default[y],u));d="boolean"==typeof o?o:f,r="boolean"==typeof i?i:h,u=(0,v.default)({},w(t),w(e));"open"in u||(u.open=!1);return{prefix:a,locale:n,pure:d,rtl:r,warning:p,defaultPropsConfig:c||{},device:l||m||void 0,popupContainer:s||g,errorBoundary:u}};var _=a(n(44)),b=n(11);function a(e){return e&&e.__esModule?e:{default:e}}var w=function(e){return null==e?{}:"boolean"==typeof e?{open:e}:(0,v.default)({open:!0},e)};e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0,t.matches=t.hasDOM=void 0;var a=n(38),r=(a=a)&&a.__esModule?a:{default:a},o=(t.hasClass=s,t.addClass=u,t.removeClass=d,t.toggleClass=function(e,t){if(!l||!e)return!1;{var n;return e.classList?e.classList.toggle(t):(((n=s(e,t))?d:u)(e,t,!0),!n)}},t.getNodeHozWhitespace=function(e){var t=m(e,"paddingLeft"),n=m(e,"paddingRight"),a=m(e,"marginLeft"),e=m(e,"marginRight");return t+n+a+e},t.getStyle=m,t.setStyle=g,t.scrollbar=v,t.hasScroll=function(e){if("hidden"===m(e,"overflow"))return!1;var t=e.parentNode;return t&&t.scrollHeight>t.clientHeight&&0=t?e:""+Array(t+1-a.length).join(n)+e},t={s:o,z:function(e){var t=-e.utcOffset(),n=Math.abs(t),a=Math.floor(n/60),r=n%60;return(t<=0?"+":"-")+o(a,2,"0")+":"+o(r,2,"0")},m:function e(t,n){if(t.date()1)return e(i[0])}else{var l=t.name;S[l]=t,r=l}return!a&&r&&(k=r),r||!a&&k},E=function(e,t){if(a(e))return e.clone();var n="object"==typeof t?t:{};return n.date=e,n.args=arguments,new l(n)},x=t,l=(x.l=r,x.i=a,x.w=function(e,t){return E(e,{locale:t.$L,utc:t.$u,x:t.$x,$offset:t.$offset})},function(){function e(e){this.$L=r(e.locale,null,!0),this.parse(e)}var t=e.prototype;return t.parse=function(e){this.$d=function(e){var t=e.date,n=e.utc;if(null===t)return new Date(NaN);if(x.u(t))return new Date;if(t instanceof Date)return new Date(t);if("string"==typeof t&&!/Z$/i.test(t)){var a=t.match(i);if(a){var r=a[2]-1||0,o=(a[7]||"0").substring(0,3);return n?new Date(Date.UTC(a[1],r,a[3]||1,a[4]||0,a[5]||0,a[6]||0,o)):new Date(a[1],r,a[3]||1,a[4]||0,a[5]||0,a[6]||0,o)}}return new Date(t)}(e),this.$x=e.x||{},this.init()},t.init=function(){var e=this.$d;this.$y=e.getFullYear(),this.$M=e.getMonth(),this.$D=e.getDate(),this.$W=e.getDay(),this.$H=e.getHours(),this.$m=e.getMinutes(),this.$s=e.getSeconds(),this.$ms=e.getMilliseconds()},t.$utils=function(){return x},t.isValid=function(){return!(this.$d.toString()===w)},t.isSame=function(e,t){var n=E(e);return this.startOf(t)<=n&&n<=this.endOf(t)},t.isAfter=function(e,t){return E(e)=20?"ste":"de")},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("af",{months:"Januarie_Februarie_Maart_April_Mei_Junie_Julie_Augustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mrt_Apr_Mei_Jun_Jul_Aug_Sep_Okt_Nov_Des".split("_"),weekdays:"Sondag_Maandag_Dinsdag_Woensdag_Donderdag_Vrydag_Saterdag".split("_"),weekdaysShort:"Son_Maa_Din_Woe_Don_Vry_Sat".split("_"),weekdaysMin:"So_Ma_Di_Wo_Do_Vr_Sa".split("_"),meridiemParse:/vm|nm/i,isPM:function(e){return/^nm$/i.test(e)},meridiem:function(e,t,n){if(e<12)return n?"vm":"VM";else return n?"nm":"NM"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Vandag om] LT",nextDay:"[MĆ“re om] LT",nextWeek:"dddd [om] LT",lastDay:"[Gister om] LT",lastWeek:"[Laas] dddd [om] LT",sameElse:"L"},relativeTime:{future:"oor %s",past:"%s gelede",s:"'n paar sekondes",ss:"%d sekondes",m:"'n minuut",mm:"%d minute",h:"'n uur",hh:"%d ure",d:"'n dag",dd:"%d dae",M:"'n maand",MM:"%d maande",y:"'n jaar",yy:"%d jaar"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(e===1||e===8||e>=20?"ste":"de")},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"Ł”",2:"Ł¢",3:"Ł£",4:"Ł¤",5:"Ł„",6:"Ł¦",7:"Ł§",8:"ŁØ",9:"Ł©",0:"Ł "},n={"Ł”":"1","Ł¢":"2","Ł£":"3","Ł¤":"4","Ł„":"5","Ł¦":"6","Ł§":"7","ŁØ":"8","Ł©":"9","Ł ":"0"},l=function(e){return e===0?0:e===1?1:e===2?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},s={s:["Ų£Ł‚Ł„ Ł…Ł† Ų«Ų§Ł†ŁŠŲ©","Ų«Ų§Ł†ŁŠŲ© ŁˆŲ§Ų­ŲÆŲ©",["Ų«Ų§Ł†ŁŠŲŖŲ§Ł†","Ų«Ų§Ł†ŁŠŲŖŁŠŁ†"],"%d Ų«ŁˆŲ§Ł†","%d Ų«Ų§Ł†ŁŠŲ©","%d Ų«Ų§Ł†ŁŠŲ©"],m:["Ų£Ł‚Ł„ Ł…Ł† ŲÆŁ‚ŁŠŁ‚Ų©","ŲÆŁ‚ŁŠŁ‚Ų© ŁˆŲ§Ų­ŲÆŲ©",["ŲÆŁ‚ŁŠŁ‚ŲŖŲ§Ł†","ŲÆŁ‚ŁŠŁ‚ŲŖŁŠŁ†"],"%d ŲÆŁ‚Ų§Ų¦Ł‚","%d ŲÆŁ‚ŁŠŁ‚Ų©","%d ŲÆŁ‚ŁŠŁ‚Ų©"],h:["Ų£Ł‚Ł„ Ł…Ł† Ų³Ų§Ų¹Ų©","Ų³Ų§Ų¹Ų© ŁˆŲ§Ų­ŲÆŲ©",["Ų³Ų§Ų¹ŲŖŲ§Ł†","Ų³Ų§Ų¹ŲŖŁŠŁ†"],"%d Ų³Ų§Ų¹Ų§ŲŖ","%d Ų³Ų§Ų¹Ų©","%d Ų³Ų§Ų¹Ų©"],d:["Ų£Ł‚Ł„ Ł…Ł† ŁŠŁˆŁ…","ŁŠŁˆŁ… ŁˆŲ§Ų­ŲÆ",["ŁŠŁˆŁ…Ų§Ł†","ŁŠŁˆŁ…ŁŠŁ†"],"%d Ų£ŁŠŲ§Ł…","%d ŁŠŁˆŁ…Ł‹Ų§","%d ŁŠŁˆŁ…"],M:["Ų£Ł‚Ł„ Ł…Ł† Ų“Ł‡Ų±","Ų“Ł‡Ų± ŁˆŲ§Ų­ŲÆ",["Ų“Ł‡Ų±Ų§Ł†","Ų“Ł‡Ų±ŁŠŁ†"],"%d Ų£Ų“Ł‡Ų±","%d Ų“Ł‡Ų±Ų§","%d Ų“Ł‡Ų±"],y:["Ų£Ł‚Ł„ Ł…Ł† Ų¹Ų§Ł…","Ų¹Ų§Ł… ŁˆŲ§Ų­ŲÆ",["Ų¹Ų§Ł…Ų§Ł†","Ų¹Ų§Ł…ŁŠŁ†"],"%d Ų£Ų¹ŁˆŲ§Ł…","%d Ų¹Ų§Ł…Ł‹Ų§","%d Ų¹Ų§Ł…"]},a=function(i){return function(e,t,n,a){var r=l(e),o=s[i][l(e)];if(r===2)o=o[t?0:1];return o.replace(/%d/i,e)}},r=["ŁŠŁ†Ų§ŁŠŲ±","ŁŲØŲ±Ų§ŁŠŲ±","Ł…Ų§Ų±Ų³","Ų£ŲØŲ±ŁŠŁ„","Ł…Ų§ŁŠŁˆ","ŁŠŁˆŁ†ŁŠŁˆ","ŁŠŁˆŁ„ŁŠŁˆ","Ų£ŲŗŲ³Ų·Ų³","Ų³ŲØŲŖŁ…ŲØŲ±","Ų£ŁƒŲŖŁˆŲØŲ±","Ł†ŁˆŁŁ…ŲØŲ±","ŲÆŁŠŲ³Ł…ŲØŲ±"],o;e.defineLocale("ar",{months:r,monthsShort:r,weekdays:"Ų§Ł„Ų£Ų­ŲÆ_Ų§Ł„Ų„Ų«Ł†ŁŠŁ†_Ų§Ł„Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ł„Ų£Ų±ŲØŲ¹Ų§Ų”_Ų§Ł„Ų®Ł…ŁŠŲ³_Ų§Ł„Ų¬Ł…Ų¹Ų©_Ų§Ł„Ų³ŲØŲŖ".split("_"),weekdaysShort:"Ų£Ų­ŲÆ_Ų„Ų«Ł†ŁŠŁ†_Ų«Ł„Ų§Ų«Ų§Ų”_Ų£Ų±ŲØŲ¹Ų§Ų”_Ų®Ł…ŁŠŲ³_Ų¬Ł…Ų¹Ų©_Ų³ŲØŲŖ".split("_"),weekdaysMin:"Ų­_Ł†_Ų«_Ų±_Ų®_Ų¬_Ų³".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/ā€M/ā€YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/Ųµ|Ł…/,isPM:function(e){return"Ł…"===e},meridiem:function(e,t,n){if(e<12)return"Ųµ";else return"Ł…"},calendar:{sameDay:"[Ų§Ł„ŁŠŁˆŁ… Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextDay:"[ŲŗŲÆŁ‹Ų§ Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextWeek:"dddd [Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastDay:"[Ų£Ł…Ų³ Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastWeek:"dddd [Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",sameElse:"L"},relativeTime:{future:"ŲØŲ¹ŲÆ %s",past:"Ł…Ł†Ų° %s",s:a("s"),ss:a("s"),m:a("m"),mm:a("m"),h:a("h"),hh:a("h"),d:a("d"),dd:a("d"),M:a("M"),MM:a("M"),y:a("y"),yy:a("y")},preparse:function(e){return e.replace(/[Ł”Ł¢Ł£Ł¤Ł„Ł¦Ł§ŁØŁ©Ł ]/g,function(e){return n[e]}).replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"ŲŒ")},week:{dow:6,doy:12}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"Ł”",2:"Ł¢",3:"Ł£",4:"Ł¤",5:"Ł„",6:"Ł¦",7:"Ł§",8:"ŁØ",9:"Ł©",0:"Ł "},n={"Ł”":"1","Ł¢":"2","Ł£":"3","Ł¤":"4","Ł„":"5","Ł¦":"6","Ł§":"7","ŁØ":"8","Ł©":"9","Ł ":"0"},l=function(e){return e===0?0:e===1?1:e===2?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},s={s:["Ų£Ł‚Ł„ Ł…Ł† Ų«Ų§Ł†ŁŠŲ©","Ų«Ų§Ł†ŁŠŲ© ŁˆŲ§Ų­ŲÆŲ©",["Ų«Ų§Ł†ŁŠŲŖŲ§Ł†","Ų«Ų§Ł†ŁŠŲŖŁŠŁ†"],"%d Ų«ŁˆŲ§Ł†","%d Ų«Ų§Ł†ŁŠŲ©","%d Ų«Ų§Ł†ŁŠŲ©"],m:["Ų£Ł‚Ł„ Ł…Ł† ŲÆŁ‚ŁŠŁ‚Ų©","ŲÆŁ‚ŁŠŁ‚Ų© ŁˆŲ§Ų­ŲÆŲ©",["ŲÆŁ‚ŁŠŁ‚ŲŖŲ§Ł†","ŲÆŁ‚ŁŠŁ‚ŲŖŁŠŁ†"],"%d ŲÆŁ‚Ų§Ų¦Ł‚","%d ŲÆŁ‚ŁŠŁ‚Ų©","%d ŲÆŁ‚ŁŠŁ‚Ų©"],h:["Ų£Ł‚Ł„ Ł…Ł† Ų³Ų§Ų¹Ų©","Ų³Ų§Ų¹Ų© ŁˆŲ§Ų­ŲÆŲ©",["Ų³Ų§Ų¹ŲŖŲ§Ł†","Ų³Ų§Ų¹ŲŖŁŠŁ†"],"%d Ų³Ų§Ų¹Ų§ŲŖ","%d Ų³Ų§Ų¹Ų©","%d Ų³Ų§Ų¹Ų©"],d:["Ų£Ł‚Ł„ Ł…Ł† ŁŠŁˆŁ…","ŁŠŁˆŁ… ŁˆŲ§Ų­ŲÆ",["ŁŠŁˆŁ…Ų§Ł†","ŁŠŁˆŁ…ŁŠŁ†"],"%d Ų£ŁŠŲ§Ł…","%d ŁŠŁˆŁ…Ł‹Ų§","%d ŁŠŁˆŁ…"],M:["Ų£Ł‚Ł„ Ł…Ł† Ų“Ł‡Ų±","Ų“Ł‡Ų± ŁˆŲ§Ų­ŲÆ",["Ų“Ł‡Ų±Ų§Ł†","Ų“Ł‡Ų±ŁŠŁ†"],"%d Ų£Ų“Ł‡Ų±","%d Ų“Ł‡Ų±Ų§","%d Ų“Ł‡Ų±"],y:["Ų£Ł‚Ł„ Ł…Ł† Ų¹Ų§Ł…","Ų¹Ų§Ł… ŁˆŲ§Ų­ŲÆ",["Ų¹Ų§Ł…Ų§Ł†","Ų¹Ų§Ł…ŁŠŁ†"],"%d Ų£Ų¹ŁˆŲ§Ł…","%d Ų¹Ų§Ł…Ł‹Ų§","%d Ų¹Ų§Ł…"]},a=function(i){return function(e,t,n,a){var r=l(e),o=s[i][l(e)];if(r===2)o=o[t?0:1];return o.replace(/%d/i,e)}},r=["ŁŠŁ†Ų§ŁŠŲ±","ŁŲØŲ±Ų§ŁŠŲ±","Ł…Ų§Ų±Ų³","Ų£ŲØŲ±ŁŠŁ„","Ł…Ų§ŁŠŁˆ","ŁŠŁˆŁ†ŁŠŁˆ","ŁŠŁˆŁ„ŁŠŁˆ","Ų£ŲŗŲ³Ų·Ų³","Ų³ŲØŲŖŁ…ŲØŲ±","Ų£ŁƒŲŖŁˆŲØŲ±","Ł†ŁˆŁŁ…ŲØŲ±","ŲÆŁŠŲ³Ł…ŲØŲ±"],o;e.defineLocale("ar",{months:r,monthsShort:r,weekdays:"Ų§Ł„Ų£Ų­ŲÆ_Ų§Ł„Ų„Ų«Ł†ŁŠŁ†_Ų§Ł„Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ł„Ų£Ų±ŲØŲ¹Ų§Ų”_Ų§Ł„Ų®Ł…ŁŠŲ³_Ų§Ł„Ų¬Ł…Ų¹Ų©_Ų§Ł„Ų³ŲØŲŖ".split("_"),weekdaysShort:"Ų£Ų­ŲÆ_Ų„Ų«Ł†ŁŠŁ†_Ų«Ł„Ų§Ų«Ų§Ų”_Ų£Ų±ŲØŲ¹Ų§Ų”_Ų®Ł…ŁŠŲ³_Ų¬Ł…Ų¹Ų©_Ų³ŲØŲŖ".split("_"),weekdaysMin:"Ų­_Ł†_Ų«_Ų±_Ų®_Ų¬_Ų³".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/ā€M/ā€YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/Ųµ|Ł…/,isPM:function(e){return"Ł…"===e},meridiem:function(e,t,n){if(e<12)return"Ųµ";else return"Ł…"},calendar:{sameDay:"[Ų§Ł„ŁŠŁˆŁ… Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextDay:"[ŲŗŲÆŁ‹Ų§ Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextWeek:"dddd [Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastDay:"[Ų£Ł…Ų³ Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastWeek:"dddd [Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",sameElse:"L"},relativeTime:{future:"ŲØŲ¹ŲÆ %s",past:"Ł…Ł†Ų° %s",s:a("s"),ss:a("s"),m:a("m"),mm:a("m"),h:a("h"),hh:a("h"),d:a("d"),dd:a("d"),M:a("M"),MM:a("M"),y:a("y"),yy:a("y")},preparse:function(e){return e.replace(/[Ł”Ł¢Ł£Ł¤Ł„Ł¦Ł§ŁØŁ©Ł ]/g,function(e){return n[e]}).replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"ŲŒ")},week:{dow:6,doy:12}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var l=function(e){return e===0?0:e===1?1:e===2?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},s={s:["Ų£Ł‚Ł„ Ł…Ł† Ų«Ų§Ł†ŁŠŲ©","Ų«Ų§Ł†ŁŠŲ© ŁˆŲ§Ų­ŲÆŲ©",["Ų«Ų§Ł†ŁŠŲŖŲ§Ł†","Ų«Ų§Ł†ŁŠŲŖŁŠŁ†"],"%d Ų«ŁˆŲ§Ł†","%d Ų«Ų§Ł†ŁŠŲ©","%d Ų«Ų§Ł†ŁŠŲ©"],m:["Ų£Ł‚Ł„ Ł…Ł† ŲÆŁ‚ŁŠŁ‚Ų©","ŲÆŁ‚ŁŠŁ‚Ų© ŁˆŲ§Ų­ŲÆŲ©",["ŲÆŁ‚ŁŠŁ‚ŲŖŲ§Ł†","ŲÆŁ‚ŁŠŁ‚ŲŖŁŠŁ†"],"%d ŲÆŁ‚Ų§Ų¦Ł‚","%d ŲÆŁ‚ŁŠŁ‚Ų©","%d ŲÆŁ‚ŁŠŁ‚Ų©"],h:["Ų£Ł‚Ł„ Ł…Ł† Ų³Ų§Ų¹Ų©","Ų³Ų§Ų¹Ų© ŁˆŲ§Ų­ŲÆŲ©",["Ų³Ų§Ų¹ŲŖŲ§Ł†","Ų³Ų§Ų¹ŲŖŁŠŁ†"],"%d Ų³Ų§Ų¹Ų§ŲŖ","%d Ų³Ų§Ų¹Ų©","%d Ų³Ų§Ų¹Ų©"],d:["Ų£Ł‚Ł„ Ł…Ł† ŁŠŁˆŁ…","ŁŠŁˆŁ… ŁˆŲ§Ų­ŲÆ",["ŁŠŁˆŁ…Ų§Ł†","ŁŠŁˆŁ…ŁŠŁ†"],"%d Ų£ŁŠŲ§Ł…","%d ŁŠŁˆŁ…Ł‹Ų§","%d ŁŠŁˆŁ…"],M:["Ų£Ł‚Ł„ Ł…Ł† Ų“Ł‡Ų±","Ų“Ł‡Ų± ŁˆŲ§Ų­ŲÆ",["Ų“Ł‡Ų±Ų§Ł†","Ų“Ł‡Ų±ŁŠŁ†"],"%d Ų£Ų“Ł‡Ų±","%d Ų“Ł‡Ų±Ų§","%d Ų“Ł‡Ų±"],y:["Ų£Ł‚Ł„ Ł…Ł† Ų¹Ų§Ł…","Ų¹Ų§Ł… ŁˆŲ§Ų­ŲÆ",["Ų¹Ų§Ł…Ų§Ł†","Ų¹Ų§Ł…ŁŠŁ†"],"%d Ų£Ų¹ŁˆŲ§Ł…","%d Ų¹Ų§Ł…Ł‹Ų§","%d Ų¹Ų§Ł…"]},t=function(i){return function(e,t,n,a){var r=l(e),o=s[i][l(e)];if(r===2)o=o[t?0:1];return o.replace(/%d/i,e)}},n=["Ų¬Ų§Ł†ŁŁŠ","ŁŁŠŁŲ±ŁŠ","Ł…Ų§Ų±Ų³","Ų£ŁŲ±ŁŠŁ„","Ł…Ų§ŁŠ","Ų¬ŁˆŲ§Ł†","Ų¬ŁˆŁŠŁ„ŁŠŲ©","Ų£ŁˆŲŖ","Ų³ŲØŲŖŁ…ŲØŲ±","Ų£ŁƒŲŖŁˆŲØŲ±","Ł†ŁˆŁŁ…ŲØŲ±","ŲÆŁŠŲ³Ł…ŲØŲ±"],a;e.defineLocale("ar-dz",{months:n,monthsShort:n,weekdays:"Ų§Ł„Ų£Ų­ŲÆ_Ų§Ł„Ų„Ų«Ł†ŁŠŁ†_Ų§Ł„Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ł„Ų£Ų±ŲØŲ¹Ų§Ų”_Ų§Ł„Ų®Ł…ŁŠŲ³_Ų§Ł„Ų¬Ł…Ų¹Ų©_Ų§Ł„Ų³ŲØŲŖ".split("_"),weekdaysShort:"Ų£Ų­ŲÆ_Ų„Ų«Ł†ŁŠŁ†_Ų«Ł„Ų§Ų«Ų§Ų”_Ų£Ų±ŲØŲ¹Ų§Ų”_Ų®Ł…ŁŠŲ³_Ų¬Ł…Ų¹Ų©_Ų³ŲØŲŖ".split("_"),weekdaysMin:"Ų­_Ł†_Ų«_Ų±_Ų®_Ų¬_Ų³".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/ā€M/ā€YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/Ųµ|Ł…/,isPM:function(e){return"Ł…"===e},meridiem:function(e,t,n){if(e<12)return"Ųµ";else return"Ł…"},calendar:{sameDay:"[Ų§Ł„ŁŠŁˆŁ… Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextDay:"[ŲŗŲÆŁ‹Ų§ Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextWeek:"dddd [Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastDay:"[Ų£Ł…Ų³ Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastWeek:"dddd [Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",sameElse:"L"},relativeTime:{future:"ŲØŲ¹ŲÆ %s",past:"Ł…Ł†Ų° %s",s:t("s"),ss:t("s"),m:t("m"),mm:t("m"),h:t("h"),hh:t("h"),d:t("d"),dd:t("d"),M:t("M"),MM:t("M"),y:t("y"),yy:t("y")},postformat:function(e){return e.replace(/,/g,"ŲŒ")},week:{dow:0,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var l=function(e){return e===0?0:e===1?1:e===2?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},s={s:["Ų£Ł‚Ł„ Ł…Ł† Ų«Ų§Ł†ŁŠŲ©","Ų«Ų§Ł†ŁŠŲ© ŁˆŲ§Ų­ŲÆŲ©",["Ų«Ų§Ł†ŁŠŲŖŲ§Ł†","Ų«Ų§Ł†ŁŠŲŖŁŠŁ†"],"%d Ų«ŁˆŲ§Ł†","%d Ų«Ų§Ł†ŁŠŲ©","%d Ų«Ų§Ł†ŁŠŲ©"],m:["Ų£Ł‚Ł„ Ł…Ł† ŲÆŁ‚ŁŠŁ‚Ų©","ŲÆŁ‚ŁŠŁ‚Ų© ŁˆŲ§Ų­ŲÆŲ©",["ŲÆŁ‚ŁŠŁ‚ŲŖŲ§Ł†","ŲÆŁ‚ŁŠŁ‚ŲŖŁŠŁ†"],"%d ŲÆŁ‚Ų§Ų¦Ł‚","%d ŲÆŁ‚ŁŠŁ‚Ų©","%d ŲÆŁ‚ŁŠŁ‚Ų©"],h:["Ų£Ł‚Ł„ Ł…Ł† Ų³Ų§Ų¹Ų©","Ų³Ų§Ų¹Ų© ŁˆŲ§Ų­ŲÆŲ©",["Ų³Ų§Ų¹ŲŖŲ§Ł†","Ų³Ų§Ų¹ŲŖŁŠŁ†"],"%d Ų³Ų§Ų¹Ų§ŲŖ","%d Ų³Ų§Ų¹Ų©","%d Ų³Ų§Ų¹Ų©"],d:["Ų£Ł‚Ł„ Ł…Ł† ŁŠŁˆŁ…","ŁŠŁˆŁ… ŁˆŲ§Ų­ŲÆ",["ŁŠŁˆŁ…Ų§Ł†","ŁŠŁˆŁ…ŁŠŁ†"],"%d Ų£ŁŠŲ§Ł…","%d ŁŠŁˆŁ…Ł‹Ų§","%d ŁŠŁˆŁ…"],M:["Ų£Ł‚Ł„ Ł…Ł† Ų“Ł‡Ų±","Ų“Ł‡Ų± ŁˆŲ§Ų­ŲÆ",["Ų“Ł‡Ų±Ų§Ł†","Ų“Ł‡Ų±ŁŠŁ†"],"%d Ų£Ų“Ł‡Ų±","%d Ų“Ł‡Ų±Ų§","%d Ų“Ł‡Ų±"],y:["Ų£Ł‚Ł„ Ł…Ł† Ų¹Ų§Ł…","Ų¹Ų§Ł… ŁˆŲ§Ų­ŲÆ",["Ų¹Ų§Ł…Ų§Ł†","Ų¹Ų§Ł…ŁŠŁ†"],"%d Ų£Ų¹ŁˆŲ§Ł…","%d Ų¹Ų§Ł…Ł‹Ų§","%d Ų¹Ų§Ł…"]},t=function(i){return function(e,t,n,a){var r=l(e),o=s[i][l(e)];if(r===2)o=o[t?0:1];return o.replace(/%d/i,e)}},n=["Ų¬Ų§Ł†ŁŁŠ","ŁŁŠŁŲ±ŁŠ","Ł…Ų§Ų±Ų³","Ų£ŁŲ±ŁŠŁ„","Ł…Ų§ŁŠ","Ų¬ŁˆŲ§Ł†","Ų¬ŁˆŁŠŁ„ŁŠŲ©","Ų£ŁˆŲŖ","Ų³ŲØŲŖŁ…ŲØŲ±","Ų£ŁƒŲŖŁˆŲØŲ±","Ł†ŁˆŁŁ…ŲØŲ±","ŲÆŁŠŲ³Ł…ŲØŲ±"],a;e.defineLocale("ar-dz",{months:n,monthsShort:n,weekdays:"Ų§Ł„Ų£Ų­ŲÆ_Ų§Ł„Ų„Ų«Ł†ŁŠŁ†_Ų§Ł„Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ł„Ų£Ų±ŲØŲ¹Ų§Ų”_Ų§Ł„Ų®Ł…ŁŠŲ³_Ų§Ł„Ų¬Ł…Ų¹Ų©_Ų§Ł„Ų³ŲØŲŖ".split("_"),weekdaysShort:"Ų£Ų­ŲÆ_Ų„Ų«Ł†ŁŠŁ†_Ų«Ł„Ų§Ų«Ų§Ų”_Ų£Ų±ŲØŲ¹Ų§Ų”_Ų®Ł…ŁŠŲ³_Ų¬Ł…Ų¹Ų©_Ų³ŲØŲŖ".split("_"),weekdaysMin:"Ų­_Ł†_Ų«_Ų±_Ų®_Ų¬_Ų³".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/ā€M/ā€YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/Ųµ|Ł…/,isPM:function(e){return"Ł…"===e},meridiem:function(e,t,n){if(e<12)return"Ųµ";else return"Ł…"},calendar:{sameDay:"[Ų§Ł„ŁŠŁˆŁ… Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextDay:"[ŲŗŲÆŁ‹Ų§ Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextWeek:"dddd [Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastDay:"[Ų£Ł…Ų³ Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastWeek:"dddd [Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",sameElse:"L"},relativeTime:{future:"ŲØŲ¹ŲÆ %s",past:"Ł…Ł†Ų° %s",s:t("s"),ss:t("s"),m:t("m"),mm:t("m"),h:t("h"),hh:t("h"),d:t("d"),dd:t("d"),M:t("M"),MM:t("M"),y:t("y"),yy:t("y")},postformat:function(e){return e.replace(/,/g,"ŲŒ")},week:{dow:0,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("ar-kw",{months:"ŁŠŁ†Ų§ŁŠŲ±_ŁŲØŲ±Ų§ŁŠŲ±_Ł…Ų§Ų±Ų³_Ų£ŲØŲ±ŁŠŁ„_Ł…Ų§ŁŠ_ŁŠŁˆŁ†ŁŠŁˆ_ŁŠŁˆŁ„ŁŠŁˆŲ²_ŲŗŲ“ŲŖ_Ų“ŲŖŁ†ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁ†ŲØŲ±_ŲÆŲ¬Ł†ŲØŲ±".split("_"),monthsShort:"ŁŠŁ†Ų§ŁŠŲ±_ŁŲØŲ±Ų§ŁŠŲ±_Ł…Ų§Ų±Ų³_Ų£ŲØŲ±ŁŠŁ„_Ł…Ų§ŁŠ_ŁŠŁˆŁ†ŁŠŁˆ_ŁŠŁˆŁ„ŁŠŁˆŲ²_ŲŗŲ“ŲŖ_Ų“ŲŖŁ†ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁ†ŲØŲ±_ŲÆŲ¬Ł†ŲØŲ±".split("_"),weekdays:"Ų§Ł„Ų£Ų­ŲÆ_Ų§Ł„Ų„ŲŖŁ†ŁŠŁ†_Ų§Ł„Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ł„Ų£Ų±ŲØŲ¹Ų§Ų”_Ų§Ł„Ų®Ł…ŁŠŲ³_Ų§Ł„Ų¬Ł…Ų¹Ų©_Ų§Ł„Ų³ŲØŲŖ".split("_"),weekdaysShort:"Ų§Ų­ŲÆ_Ų§ŲŖŁ†ŁŠŁ†_Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ų±ŲØŲ¹Ų§Ų”_Ų®Ł…ŁŠŲ³_Ų¬Ł…Ų¹Ų©_Ų³ŲØŲŖ".split("_"),weekdaysMin:"Ų­_Ł†_Ų«_Ų±_Ų®_Ų¬_Ų³".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Ų§Ł„ŁŠŁˆŁ… Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextDay:"[ŲŗŲÆŲ§ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastDay:"[Ų£Ł…Ų³ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",sameElse:"L"},relativeTime:{future:"ŁŁŠ %s",past:"Ł…Ł†Ų° %s",s:"Ų«ŁˆŲ§Ł†",ss:"%d Ų«Ų§Ł†ŁŠŲ©",m:"ŲÆŁ‚ŁŠŁ‚Ų©",mm:"%d ŲÆŁ‚Ų§Ų¦Ł‚",h:"Ų³Ų§Ų¹Ų©",hh:"%d Ų³Ų§Ų¹Ų§ŲŖ",d:"ŁŠŁˆŁ…",dd:"%d Ų£ŁŠŲ§Ł…",M:"Ų“Ł‡Ų±",MM:"%d Ų£Ų“Ł‡Ų±",y:"Ų³Ł†Ų©",yy:"%d Ų³Ł†ŁˆŲ§ŲŖ"},week:{dow:0,doy:12}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("ar-kw",{months:"ŁŠŁ†Ų§ŁŠŲ±_ŁŲØŲ±Ų§ŁŠŲ±_Ł…Ų§Ų±Ų³_Ų£ŲØŲ±ŁŠŁ„_Ł…Ų§ŁŠ_ŁŠŁˆŁ†ŁŠŁˆ_ŁŠŁˆŁ„ŁŠŁˆŲ²_ŲŗŲ“ŲŖ_Ų“ŲŖŁ†ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁ†ŲØŲ±_ŲÆŲ¬Ł†ŲØŲ±".split("_"),monthsShort:"ŁŠŁ†Ų§ŁŠŲ±_ŁŲØŲ±Ų§ŁŠŲ±_Ł…Ų§Ų±Ų³_Ų£ŲØŲ±ŁŠŁ„_Ł…Ų§ŁŠ_ŁŠŁˆŁ†ŁŠŁˆ_ŁŠŁˆŁ„ŁŠŁˆŲ²_ŲŗŲ“ŲŖ_Ų“ŲŖŁ†ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁ†ŲØŲ±_ŲÆŲ¬Ł†ŲØŲ±".split("_"),weekdays:"Ų§Ł„Ų£Ų­ŲÆ_Ų§Ł„Ų„ŲŖŁ†ŁŠŁ†_Ų§Ł„Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ł„Ų£Ų±ŲØŲ¹Ų§Ų”_Ų§Ł„Ų®Ł…ŁŠŲ³_Ų§Ł„Ų¬Ł…Ų¹Ų©_Ų§Ł„Ų³ŲØŲŖ".split("_"),weekdaysShort:"Ų§Ų­ŲÆ_Ų§ŲŖŁ†ŁŠŁ†_Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ų±ŲØŲ¹Ų§Ų”_Ų®Ł…ŁŠŲ³_Ų¬Ł…Ų¹Ų©_Ų³ŲØŲŖ".split("_"),weekdaysMin:"Ų­_Ł†_Ų«_Ų±_Ų®_Ų¬_Ų³".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Ų§Ł„ŁŠŁˆŁ… Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextDay:"[ŲŗŲÆŲ§ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastDay:"[Ų£Ł…Ų³ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",sameElse:"L"},relativeTime:{future:"ŁŁŠ %s",past:"Ł…Ł†Ų° %s",s:"Ų«ŁˆŲ§Ł†",ss:"%d Ų«Ų§Ł†ŁŠŲ©",m:"ŲÆŁ‚ŁŠŁ‚Ų©",mm:"%d ŲÆŁ‚Ų§Ų¦Ł‚",h:"Ų³Ų§Ų¹Ų©",hh:"%d Ų³Ų§Ų¹Ų§ŲŖ",d:"ŁŠŁˆŁ…",dd:"%d Ų£ŁŠŲ§Ł…",M:"Ų“Ł‡Ų±",MM:"%d Ų£Ų“Ł‡Ų±",y:"Ų³Ł†Ų©",yy:"%d Ų³Ł†ŁˆŲ§ŲŖ"},week:{dow:0,doy:12}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9",0:"0"},l=function(e){return e===0?0:e===1?1:e===2?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},s={s:["Ų£Ł‚Ł„ Ł…Ł† Ų«Ų§Ł†ŁŠŲ©","Ų«Ų§Ł†ŁŠŲ© ŁˆŲ§Ų­ŲÆŲ©",["Ų«Ų§Ł†ŁŠŲŖŲ§Ł†","Ų«Ų§Ł†ŁŠŲŖŁŠŁ†"],"%d Ų«ŁˆŲ§Ł†","%d Ų«Ų§Ł†ŁŠŲ©","%d Ų«Ų§Ł†ŁŠŲ©"],m:["Ų£Ł‚Ł„ Ł…Ł† ŲÆŁ‚ŁŠŁ‚Ų©","ŲÆŁ‚ŁŠŁ‚Ų© ŁˆŲ§Ų­ŲÆŲ©",["ŲÆŁ‚ŁŠŁ‚ŲŖŲ§Ł†","ŲÆŁ‚ŁŠŁ‚ŲŖŁŠŁ†"],"%d ŲÆŁ‚Ų§Ų¦Ł‚","%d ŲÆŁ‚ŁŠŁ‚Ų©","%d ŲÆŁ‚ŁŠŁ‚Ų©"],h:["Ų£Ł‚Ł„ Ł…Ł† Ų³Ų§Ų¹Ų©","Ų³Ų§Ų¹Ų© ŁˆŲ§Ų­ŲÆŲ©",["Ų³Ų§Ų¹ŲŖŲ§Ł†","Ų³Ų§Ų¹ŲŖŁŠŁ†"],"%d Ų³Ų§Ų¹Ų§ŲŖ","%d Ų³Ų§Ų¹Ų©","%d Ų³Ų§Ų¹Ų©"],d:["Ų£Ł‚Ł„ Ł…Ł† ŁŠŁˆŁ…","ŁŠŁˆŁ… ŁˆŲ§Ų­ŲÆ",["ŁŠŁˆŁ…Ų§Ł†","ŁŠŁˆŁ…ŁŠŁ†"],"%d Ų£ŁŠŲ§Ł…","%d ŁŠŁˆŁ…Ł‹Ų§","%d ŁŠŁˆŁ…"],M:["Ų£Ł‚Ł„ Ł…Ł† Ų“Ł‡Ų±","Ų“Ł‡Ų± ŁˆŲ§Ų­ŲÆ",["Ų“Ł‡Ų±Ų§Ł†","Ų“Ł‡Ų±ŁŠŁ†"],"%d Ų£Ų“Ł‡Ų±","%d Ų“Ł‡Ų±Ų§","%d Ų“Ł‡Ų±"],y:["Ų£Ł‚Ł„ Ł…Ł† Ų¹Ų§Ł…","Ų¹Ų§Ł… ŁˆŲ§Ų­ŲÆ",["Ų¹Ų§Ł…Ų§Ł†","Ų¹Ų§Ł…ŁŠŁ†"],"%d Ų£Ų¹ŁˆŲ§Ł…","%d Ų¹Ų§Ł…Ł‹Ų§","%d Ų¹Ų§Ł…"]},n=function(i){return function(e,t,n,a){var r=l(e),o=s[i][l(e)];if(r===2)o=o[t?0:1];return o.replace(/%d/i,e)}},a=["ŁŠŁ†Ų§ŁŠŲ±","ŁŲØŲ±Ų§ŁŠŲ±","Ł…Ų§Ų±Ų³","Ų£ŲØŲ±ŁŠŁ„","Ł…Ų§ŁŠŁˆ","ŁŠŁˆŁ†ŁŠŁˆ","ŁŠŁˆŁ„ŁŠŁˆ","Ų£ŲŗŲ³Ų·Ų³","Ų³ŲØŲŖŁ…ŲØŲ±","Ų£ŁƒŲŖŁˆŲØŲ±","Ł†ŁˆŁŁ…ŲØŲ±","ŲÆŁŠŲ³Ł…ŲØŲ±"],r;e.defineLocale("ar-ly",{months:a,monthsShort:a,weekdays:"Ų§Ł„Ų£Ų­ŲÆ_Ų§Ł„Ų„Ų«Ł†ŁŠŁ†_Ų§Ł„Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ł„Ų£Ų±ŲØŲ¹Ų§Ų”_Ų§Ł„Ų®Ł…ŁŠŲ³_Ų§Ł„Ų¬Ł…Ų¹Ų©_Ų§Ł„Ų³ŲØŲŖ".split("_"),weekdaysShort:"Ų£Ų­ŲÆ_Ų„Ų«Ł†ŁŠŁ†_Ų«Ł„Ų§Ų«Ų§Ų”_Ų£Ų±ŲØŲ¹Ų§Ų”_Ų®Ł…ŁŠŲ³_Ų¬Ł…Ų¹Ų©_Ų³ŲØŲŖ".split("_"),weekdaysMin:"Ų­_Ł†_Ų«_Ų±_Ų®_Ų¬_Ų³".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/ā€M/ā€YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/Ųµ|Ł…/,isPM:function(e){return"Ł…"===e},meridiem:function(e,t,n){if(e<12)return"Ųµ";else return"Ł…"},calendar:{sameDay:"[Ų§Ł„ŁŠŁˆŁ… Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextDay:"[ŲŗŲÆŁ‹Ų§ Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextWeek:"dddd [Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastDay:"[Ų£Ł…Ų³ Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastWeek:"dddd [Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",sameElse:"L"},relativeTime:{future:"ŲØŲ¹ŲÆ %s",past:"Ł…Ł†Ų° %s",s:n("s"),ss:n("s"),m:n("m"),mm:n("m"),h:n("h"),hh:n("h"),d:n("d"),dd:n("d"),M:n("M"),MM:n("M"),y:n("y"),yy:n("y")},preparse:function(e){return e.replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"ŲŒ")},week:{dow:6,doy:12}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9",0:"0"},l=function(e){return e===0?0:e===1?1:e===2?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},s={s:["Ų£Ł‚Ł„ Ł…Ł† Ų«Ų§Ł†ŁŠŲ©","Ų«Ų§Ł†ŁŠŲ© ŁˆŲ§Ų­ŲÆŲ©",["Ų«Ų§Ł†ŁŠŲŖŲ§Ł†","Ų«Ų§Ł†ŁŠŲŖŁŠŁ†"],"%d Ų«ŁˆŲ§Ł†","%d Ų«Ų§Ł†ŁŠŲ©","%d Ų«Ų§Ł†ŁŠŲ©"],m:["Ų£Ł‚Ł„ Ł…Ł† ŲÆŁ‚ŁŠŁ‚Ų©","ŲÆŁ‚ŁŠŁ‚Ų© ŁˆŲ§Ų­ŲÆŲ©",["ŲÆŁ‚ŁŠŁ‚ŲŖŲ§Ł†","ŲÆŁ‚ŁŠŁ‚ŲŖŁŠŁ†"],"%d ŲÆŁ‚Ų§Ų¦Ł‚","%d ŲÆŁ‚ŁŠŁ‚Ų©","%d ŲÆŁ‚ŁŠŁ‚Ų©"],h:["Ų£Ł‚Ł„ Ł…Ł† Ų³Ų§Ų¹Ų©","Ų³Ų§Ų¹Ų© ŁˆŲ§Ų­ŲÆŲ©",["Ų³Ų§Ų¹ŲŖŲ§Ł†","Ų³Ų§Ų¹ŲŖŁŠŁ†"],"%d Ų³Ų§Ų¹Ų§ŲŖ","%d Ų³Ų§Ų¹Ų©","%d Ų³Ų§Ų¹Ų©"],d:["Ų£Ł‚Ł„ Ł…Ł† ŁŠŁˆŁ…","ŁŠŁˆŁ… ŁˆŲ§Ų­ŲÆ",["ŁŠŁˆŁ…Ų§Ł†","ŁŠŁˆŁ…ŁŠŁ†"],"%d Ų£ŁŠŲ§Ł…","%d ŁŠŁˆŁ…Ł‹Ų§","%d ŁŠŁˆŁ…"],M:["Ų£Ł‚Ł„ Ł…Ł† Ų“Ł‡Ų±","Ų“Ł‡Ų± ŁˆŲ§Ų­ŲÆ",["Ų“Ł‡Ų±Ų§Ł†","Ų“Ł‡Ų±ŁŠŁ†"],"%d Ų£Ų“Ł‡Ų±","%d Ų“Ł‡Ų±Ų§","%d Ų“Ł‡Ų±"],y:["Ų£Ł‚Ł„ Ł…Ł† Ų¹Ų§Ł…","Ų¹Ų§Ł… ŁˆŲ§Ų­ŲÆ",["Ų¹Ų§Ł…Ų§Ł†","Ų¹Ų§Ł…ŁŠŁ†"],"%d Ų£Ų¹ŁˆŲ§Ł…","%d Ų¹Ų§Ł…Ł‹Ų§","%d Ų¹Ų§Ł…"]},n=function(i){return function(e,t,n,a){var r=l(e),o=s[i][l(e)];if(r===2)o=o[t?0:1];return o.replace(/%d/i,e)}},a=["ŁŠŁ†Ų§ŁŠŲ±","ŁŲØŲ±Ų§ŁŠŲ±","Ł…Ų§Ų±Ų³","Ų£ŲØŲ±ŁŠŁ„","Ł…Ų§ŁŠŁˆ","ŁŠŁˆŁ†ŁŠŁˆ","ŁŠŁˆŁ„ŁŠŁˆ","Ų£ŲŗŲ³Ų·Ų³","Ų³ŲØŲŖŁ…ŲØŲ±","Ų£ŁƒŲŖŁˆŲØŲ±","Ł†ŁˆŁŁ…ŲØŲ±","ŲÆŁŠŲ³Ł…ŲØŲ±"],r;e.defineLocale("ar-ly",{months:a,monthsShort:a,weekdays:"Ų§Ł„Ų£Ų­ŲÆ_Ų§Ł„Ų„Ų«Ł†ŁŠŁ†_Ų§Ł„Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ł„Ų£Ų±ŲØŲ¹Ų§Ų”_Ų§Ł„Ų®Ł…ŁŠŲ³_Ų§Ł„Ų¬Ł…Ų¹Ų©_Ų§Ł„Ų³ŲØŲŖ".split("_"),weekdaysShort:"Ų£Ų­ŲÆ_Ų„Ų«Ł†ŁŠŁ†_Ų«Ł„Ų§Ų«Ų§Ų”_Ų£Ų±ŲØŲ¹Ų§Ų”_Ų®Ł…ŁŠŲ³_Ų¬Ł…Ų¹Ų©_Ų³ŲØŲŖ".split("_"),weekdaysMin:"Ų­_Ł†_Ų«_Ų±_Ų®_Ų¬_Ų³".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/ā€M/ā€YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/Ųµ|Ł…/,isPM:function(e){return"Ł…"===e},meridiem:function(e,t,n){if(e<12)return"Ųµ";else return"Ł…"},calendar:{sameDay:"[Ų§Ł„ŁŠŁˆŁ… Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextDay:"[ŲŗŲÆŁ‹Ų§ Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextWeek:"dddd [Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastDay:"[Ų£Ł…Ų³ Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastWeek:"dddd [Ų¹Ł†ŲÆ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",sameElse:"L"},relativeTime:{future:"ŲØŲ¹ŲÆ %s",past:"Ł…Ł†Ų° %s",s:n("s"),ss:n("s"),m:n("m"),mm:n("m"),h:n("h"),hh:n("h"),d:n("d"),dd:n("d"),M:n("M"),MM:n("M"),y:n("y"),yy:n("y")},preparse:function(e){return e.replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"ŲŒ")},week:{dow:6,doy:12}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("ar-ma",{months:"ŁŠŁ†Ų§ŁŠŲ±_ŁŲØŲ±Ų§ŁŠŲ±_Ł…Ų§Ų±Ų³_Ų£ŲØŲ±ŁŠŁ„_Ł…Ų§ŁŠ_ŁŠŁˆŁ†ŁŠŁˆ_ŁŠŁˆŁ„ŁŠŁˆŲ²_ŲŗŲ“ŲŖ_Ų“ŲŖŁ†ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁ†ŲØŲ±_ŲÆŲ¬Ł†ŲØŲ±".split("_"),monthsShort:"ŁŠŁ†Ų§ŁŠŲ±_ŁŲØŲ±Ų§ŁŠŲ±_Ł…Ų§Ų±Ų³_Ų£ŲØŲ±ŁŠŁ„_Ł…Ų§ŁŠ_ŁŠŁˆŁ†ŁŠŁˆ_ŁŠŁˆŁ„ŁŠŁˆŲ²_ŲŗŲ“ŲŖ_Ų“ŲŖŁ†ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁ†ŲØŲ±_ŲÆŲ¬Ł†ŲØŲ±".split("_"),weekdays:"Ų§Ł„Ų£Ų­ŲÆ_Ų§Ł„Ų„Ų«Ł†ŁŠŁ†_Ų§Ł„Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ł„Ų£Ų±ŲØŲ¹Ų§Ų”_Ų§Ł„Ų®Ł…ŁŠŲ³_Ų§Ł„Ų¬Ł…Ų¹Ų©_Ų§Ł„Ų³ŲØŲŖ".split("_"),weekdaysShort:"Ų§Ų­ŲÆ_Ų§Ų«Ł†ŁŠŁ†_Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ų±ŲØŲ¹Ų§Ų”_Ų®Ł…ŁŠŲ³_Ų¬Ł…Ų¹Ų©_Ų³ŲØŲŖ".split("_"),weekdaysMin:"Ų­_Ł†_Ų«_Ų±_Ų®_Ų¬_Ų³".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Ų§Ł„ŁŠŁˆŁ… Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextDay:"[ŲŗŲÆŲ§ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastDay:"[Ų£Ł…Ų³ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",sameElse:"L"},relativeTime:{future:"ŁŁŠ %s",past:"Ł…Ł†Ų° %s",s:"Ų«ŁˆŲ§Ł†",ss:"%d Ų«Ų§Ł†ŁŠŲ©",m:"ŲÆŁ‚ŁŠŁ‚Ų©",mm:"%d ŲÆŁ‚Ų§Ų¦Ł‚",h:"Ų³Ų§Ų¹Ų©",hh:"%d Ų³Ų§Ų¹Ų§ŲŖ",d:"ŁŠŁˆŁ…",dd:"%d Ų£ŁŠŲ§Ł…",M:"Ų“Ł‡Ų±",MM:"%d Ų£Ų“Ł‡Ų±",y:"Ų³Ł†Ų©",yy:"%d Ų³Ł†ŁˆŲ§ŲŖ"},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("ar-ma",{months:"ŁŠŁ†Ų§ŁŠŲ±_ŁŲØŲ±Ų§ŁŠŲ±_Ł…Ų§Ų±Ų³_Ų£ŲØŲ±ŁŠŁ„_Ł…Ų§ŁŠ_ŁŠŁˆŁ†ŁŠŁˆ_ŁŠŁˆŁ„ŁŠŁˆŲ²_ŲŗŲ“ŲŖ_Ų“ŲŖŁ†ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁ†ŲØŲ±_ŲÆŲ¬Ł†ŲØŲ±".split("_"),monthsShort:"ŁŠŁ†Ų§ŁŠŲ±_ŁŲØŲ±Ų§ŁŠŲ±_Ł…Ų§Ų±Ų³_Ų£ŲØŲ±ŁŠŁ„_Ł…Ų§ŁŠ_ŁŠŁˆŁ†ŁŠŁˆ_ŁŠŁˆŁ„ŁŠŁˆŲ²_ŲŗŲ“ŲŖ_Ų“ŲŖŁ†ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁ†ŲØŲ±_ŲÆŲ¬Ł†ŲØŲ±".split("_"),weekdays:"Ų§Ł„Ų£Ų­ŲÆ_Ų§Ł„Ų„Ų«Ł†ŁŠŁ†_Ų§Ł„Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ł„Ų£Ų±ŲØŲ¹Ų§Ų”_Ų§Ł„Ų®Ł…ŁŠŲ³_Ų§Ł„Ų¬Ł…Ų¹Ų©_Ų§Ł„Ų³ŲØŲŖ".split("_"),weekdaysShort:"Ų§Ų­ŲÆ_Ų§Ų«Ł†ŁŠŁ†_Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ų±ŲØŲ¹Ų§Ų”_Ų®Ł…ŁŠŲ³_Ų¬Ł…Ų¹Ų©_Ų³ŲØŲŖ".split("_"),weekdaysMin:"Ų­_Ł†_Ų«_Ų±_Ų®_Ų¬_Ų³".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Ų§Ł„ŁŠŁˆŁ… Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextDay:"[ŲŗŲÆŲ§ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastDay:"[Ų£Ł…Ų³ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",sameElse:"L"},relativeTime:{future:"ŁŁŠ %s",past:"Ł…Ł†Ų° %s",s:"Ų«ŁˆŲ§Ł†",ss:"%d Ų«Ų§Ł†ŁŠŲ©",m:"ŲÆŁ‚ŁŠŁ‚Ų©",mm:"%d ŲÆŁ‚Ų§Ų¦Ł‚",h:"Ų³Ų§Ų¹Ų©",hh:"%d Ų³Ų§Ų¹Ų§ŲŖ",d:"ŁŠŁˆŁ…",dd:"%d Ų£ŁŠŲ§Ł…",M:"Ų“Ł‡Ų±",MM:"%d Ų£Ų“Ł‡Ų±",y:"Ų³Ł†Ų©",yy:"%d Ų³Ł†ŁˆŲ§ŲŖ"},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"Ł”",2:"Ł¢",3:"Ł£",4:"Ł¤",5:"Ł„",6:"Ł¦",7:"Ł§",8:"ŁØ",9:"Ł©",0:"Ł "},n={"Ł”":"1","Ł¢":"2","Ł£":"3","Ł¤":"4","Ł„":"5","Ł¦":"6","Ł§":"7","ŁØ":"8","Ł©":"9","Ł ":"0"},a;e.defineLocale("ar-sa",{months:"ŁŠŁ†Ų§ŁŠŲ±_ŁŲØŲ±Ų§ŁŠŲ±_Ł…Ų§Ų±Ų³_Ų£ŲØŲ±ŁŠŁ„_Ł…Ų§ŁŠŁˆ_ŁŠŁˆŁ†ŁŠŁˆ_ŁŠŁˆŁ„ŁŠŁˆ_Ų£ŲŗŲ³Ų·Ų³_Ų³ŲØŲŖŁ…ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁŁ…ŲØŲ±_ŲÆŁŠŲ³Ł…ŲØŲ±".split("_"),monthsShort:"ŁŠŁ†Ų§ŁŠŲ±_ŁŲØŲ±Ų§ŁŠŲ±_Ł…Ų§Ų±Ų³_Ų£ŲØŲ±ŁŠŁ„_Ł…Ų§ŁŠŁˆ_ŁŠŁˆŁ†ŁŠŁˆ_ŁŠŁˆŁ„ŁŠŁˆ_Ų£ŲŗŲ³Ų·Ų³_Ų³ŲØŲŖŁ…ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁŁ…ŲØŲ±_ŲÆŁŠŲ³Ł…ŲØŲ±".split("_"),weekdays:"Ų§Ł„Ų£Ų­ŲÆ_Ų§Ł„Ų„Ų«Ł†ŁŠŁ†_Ų§Ł„Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ł„Ų£Ų±ŲØŲ¹Ų§Ų”_Ų§Ł„Ų®Ł…ŁŠŲ³_Ų§Ł„Ų¬Ł…Ų¹Ų©_Ų§Ł„Ų³ŲØŲŖ".split("_"),weekdaysShort:"Ų£Ų­ŲÆ_Ų„Ų«Ł†ŁŠŁ†_Ų«Ł„Ų§Ų«Ų§Ų”_Ų£Ų±ŲØŲ¹Ų§Ų”_Ų®Ł…ŁŠŲ³_Ų¬Ł…Ų¹Ų©_Ų³ŲØŲŖ".split("_"),weekdaysMin:"Ų­_Ł†_Ų«_Ų±_Ų®_Ų¬_Ų³".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/Ųµ|Ł…/,isPM:function(e){return"Ł…"===e},meridiem:function(e,t,n){if(e<12)return"Ųµ";else return"Ł…"},calendar:{sameDay:"[Ų§Ł„ŁŠŁˆŁ… Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextDay:"[ŲŗŲÆŲ§ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastDay:"[Ų£Ł…Ų³ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",sameElse:"L"},relativeTime:{future:"ŁŁŠ %s",past:"Ł…Ł†Ų° %s",s:"Ų«ŁˆŲ§Ł†",ss:"%d Ų«Ų§Ł†ŁŠŲ©",m:"ŲÆŁ‚ŁŠŁ‚Ų©",mm:"%d ŲÆŁ‚Ų§Ų¦Ł‚",h:"Ų³Ų§Ų¹Ų©",hh:"%d Ų³Ų§Ų¹Ų§ŲŖ",d:"ŁŠŁˆŁ…",dd:"%d Ų£ŁŠŲ§Ł…",M:"Ų“Ł‡Ų±",MM:"%d Ų£Ų“Ł‡Ų±",y:"Ų³Ł†Ų©",yy:"%d Ų³Ł†ŁˆŲ§ŲŖ"},preparse:function(e){return e.replace(/[Ł”Ł¢Ł£Ł¤Ł„Ł¦Ł§ŁØŁ©Ł ]/g,function(e){return n[e]}).replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"ŲŒ")},week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"Ł”",2:"Ł¢",3:"Ł£",4:"Ł¤",5:"Ł„",6:"Ł¦",7:"Ł§",8:"ŁØ",9:"Ł©",0:"Ł "},n={"Ł”":"1","Ł¢":"2","Ł£":"3","Ł¤":"4","Ł„":"5","Ł¦":"6","Ł§":"7","ŁØ":"8","Ł©":"9","Ł ":"0"},a;e.defineLocale("ar-sa",{months:"ŁŠŁ†Ų§ŁŠŲ±_ŁŲØŲ±Ų§ŁŠŲ±_Ł…Ų§Ų±Ų³_Ų£ŲØŲ±ŁŠŁ„_Ł…Ų§ŁŠŁˆ_ŁŠŁˆŁ†ŁŠŁˆ_ŁŠŁˆŁ„ŁŠŁˆ_Ų£ŲŗŲ³Ų·Ų³_Ų³ŲØŲŖŁ…ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁŁ…ŲØŲ±_ŲÆŁŠŲ³Ł…ŲØŲ±".split("_"),monthsShort:"ŁŠŁ†Ų§ŁŠŲ±_ŁŲØŲ±Ų§ŁŠŲ±_Ł…Ų§Ų±Ų³_Ų£ŲØŲ±ŁŠŁ„_Ł…Ų§ŁŠŁˆ_ŁŠŁˆŁ†ŁŠŁˆ_ŁŠŁˆŁ„ŁŠŁˆ_Ų£ŲŗŲ³Ų·Ų³_Ų³ŲØŲŖŁ…ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁŁ…ŲØŲ±_ŲÆŁŠŲ³Ł…ŲØŲ±".split("_"),weekdays:"Ų§Ł„Ų£Ų­ŲÆ_Ų§Ł„Ų„Ų«Ł†ŁŠŁ†_Ų§Ł„Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ł„Ų£Ų±ŲØŲ¹Ų§Ų”_Ų§Ł„Ų®Ł…ŁŠŲ³_Ų§Ł„Ų¬Ł…Ų¹Ų©_Ų§Ł„Ų³ŲØŲŖ".split("_"),weekdaysShort:"Ų£Ų­ŲÆ_Ų„Ų«Ł†ŁŠŁ†_Ų«Ł„Ų§Ų«Ų§Ų”_Ų£Ų±ŲØŲ¹Ų§Ų”_Ų®Ł…ŁŠŲ³_Ų¬Ł…Ų¹Ų©_Ų³ŲØŲŖ".split("_"),weekdaysMin:"Ų­_Ł†_Ų«_Ų±_Ų®_Ų¬_Ų³".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/Ųµ|Ł…/,isPM:function(e){return"Ł…"===e},meridiem:function(e,t,n){if(e<12)return"Ųµ";else return"Ł…"},calendar:{sameDay:"[Ų§Ł„ŁŠŁˆŁ… Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextDay:"[ŲŗŲÆŲ§ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastDay:"[Ų£Ł…Ų³ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",sameElse:"L"},relativeTime:{future:"ŁŁŠ %s",past:"Ł…Ł†Ų° %s",s:"Ų«ŁˆŲ§Ł†",ss:"%d Ų«Ų§Ł†ŁŠŲ©",m:"ŲÆŁ‚ŁŠŁ‚Ų©",mm:"%d ŲÆŁ‚Ų§Ų¦Ł‚",h:"Ų³Ų§Ų¹Ų©",hh:"%d Ų³Ų§Ų¹Ų§ŲŖ",d:"ŁŠŁˆŁ…",dd:"%d Ų£ŁŠŲ§Ł…",M:"Ų“Ł‡Ų±",MM:"%d Ų£Ų“Ł‡Ų±",y:"Ų³Ł†Ų©",yy:"%d Ų³Ł†ŁˆŲ§ŲŖ"},preparse:function(e){return e.replace(/[Ł”Ł¢Ł£Ł¤Ł„Ł¦Ł§ŁØŁ©Ł ]/g,function(e){return n[e]}).replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"ŲŒ")},week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("ar-tn",{months:"Ų¬Ų§Ł†ŁŁŠ_ŁŁŠŁŲ±ŁŠ_Ł…Ų§Ų±Ų³_Ų£ŁŲ±ŁŠŁ„_Ł…Ų§ŁŠ_Ų¬ŁˆŲ§Ł†_Ų¬ŁˆŁŠŁ„ŁŠŲ©_Ų£ŁˆŲŖ_Ų³ŲØŲŖŁ…ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁŁ…ŲØŲ±_ŲÆŁŠŲ³Ł…ŲØŲ±".split("_"),monthsShort:"Ų¬Ų§Ł†ŁŁŠ_ŁŁŠŁŲ±ŁŠ_Ł…Ų§Ų±Ų³_Ų£ŁŲ±ŁŠŁ„_Ł…Ų§ŁŠ_Ų¬ŁˆŲ§Ł†_Ų¬ŁˆŁŠŁ„ŁŠŲ©_Ų£ŁˆŲŖ_Ų³ŲØŲŖŁ…ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁŁ…ŲØŲ±_ŲÆŁŠŲ³Ł…ŲØŲ±".split("_"),weekdays:"Ų§Ł„Ų£Ų­ŲÆ_Ų§Ł„Ų„Ų«Ł†ŁŠŁ†_Ų§Ł„Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ł„Ų£Ų±ŲØŲ¹Ų§Ų”_Ų§Ł„Ų®Ł…ŁŠŲ³_Ų§Ł„Ų¬Ł…Ų¹Ų©_Ų§Ł„Ų³ŲØŲŖ".split("_"),weekdaysShort:"Ų£Ų­ŲÆ_Ų„Ų«Ł†ŁŠŁ†_Ų«Ł„Ų§Ų«Ų§Ų”_Ų£Ų±ŲØŲ¹Ų§Ų”_Ų®Ł…ŁŠŲ³_Ų¬Ł…Ų¹Ų©_Ų³ŲØŲŖ".split("_"),weekdaysMin:"Ų­_Ł†_Ų«_Ų±_Ų®_Ų¬_Ų³".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Ų§Ł„ŁŠŁˆŁ… Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextDay:"[ŲŗŲÆŲ§ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastDay:"[Ų£Ł…Ų³ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",sameElse:"L"},relativeTime:{future:"ŁŁŠ %s",past:"Ł…Ł†Ų° %s",s:"Ų«ŁˆŲ§Ł†",ss:"%d Ų«Ų§Ł†ŁŠŲ©",m:"ŲÆŁ‚ŁŠŁ‚Ų©",mm:"%d ŲÆŁ‚Ų§Ų¦Ł‚",h:"Ų³Ų§Ų¹Ų©",hh:"%d Ų³Ų§Ų¹Ų§ŲŖ",d:"ŁŠŁˆŁ…",dd:"%d Ų£ŁŠŲ§Ł…",M:"Ų“Ł‡Ų±",MM:"%d Ų£Ų“Ł‡Ų±",y:"Ų³Ł†Ų©",yy:"%d Ų³Ł†ŁˆŲ§ŲŖ"},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("ar-tn",{months:"Ų¬Ų§Ł†ŁŁŠ_ŁŁŠŁŲ±ŁŠ_Ł…Ų§Ų±Ų³_Ų£ŁŲ±ŁŠŁ„_Ł…Ų§ŁŠ_Ų¬ŁˆŲ§Ł†_Ų¬ŁˆŁŠŁ„ŁŠŲ©_Ų£ŁˆŲŖ_Ų³ŲØŲŖŁ…ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁŁ…ŲØŲ±_ŲÆŁŠŲ³Ł…ŲØŲ±".split("_"),monthsShort:"Ų¬Ų§Ł†ŁŁŠ_ŁŁŠŁŲ±ŁŠ_Ł…Ų§Ų±Ų³_Ų£ŁŲ±ŁŠŁ„_Ł…Ų§ŁŠ_Ų¬ŁˆŲ§Ł†_Ų¬ŁˆŁŠŁ„ŁŠŲ©_Ų£ŁˆŲŖ_Ų³ŲØŲŖŁ…ŲØŲ±_Ų£ŁƒŲŖŁˆŲØŲ±_Ł†ŁˆŁŁ…ŲØŲ±_ŲÆŁŠŲ³Ł…ŲØŲ±".split("_"),weekdays:"Ų§Ł„Ų£Ų­ŲÆ_Ų§Ł„Ų„Ų«Ł†ŁŠŁ†_Ų§Ł„Ų«Ł„Ų§Ų«Ų§Ų”_Ų§Ł„Ų£Ų±ŲØŲ¹Ų§Ų”_Ų§Ł„Ų®Ł…ŁŠŲ³_Ų§Ł„Ų¬Ł…Ų¹Ų©_Ų§Ł„Ų³ŲØŲŖ".split("_"),weekdaysShort:"Ų£Ų­ŲÆ_Ų„Ų«Ł†ŁŠŁ†_Ų«Ł„Ų§Ų«Ų§Ų”_Ų£Ų±ŲØŲ¹Ų§Ų”_Ų®Ł…ŁŠŲ³_Ų¬Ł…Ų¹Ų©_Ų³ŲØŲŖ".split("_"),weekdaysMin:"Ų­_Ł†_Ų«_Ų±_Ų®_Ų¬_Ų³".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Ų§Ł„ŁŠŁˆŁ… Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextDay:"[ŲŗŲÆŲ§ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",nextWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastDay:"[Ų£Ł…Ų³ Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",lastWeek:"dddd [Ų¹Ł„Ł‰ Ų§Ł„Ų³Ų§Ų¹Ų©] LT",sameElse:"L"},relativeTime:{future:"ŁŁŠ %s",past:"Ł…Ł†Ų° %s",s:"Ų«ŁˆŲ§Ł†",ss:"%d Ų«Ų§Ł†ŁŠŲ©",m:"ŲÆŁ‚ŁŠŁ‚Ų©",mm:"%d ŲÆŁ‚Ų§Ų¦Ł‚",h:"Ų³Ų§Ų¹Ų©",hh:"%d Ų³Ų§Ų¹Ų§ŲŖ",d:"ŁŠŁˆŁ…",dd:"%d Ų£ŁŠŲ§Ł…",M:"Ų“Ł‡Ų±",MM:"%d Ų£Ų“Ł‡Ų±",y:"Ų³Ł†Ų©",yy:"%d Ų³Ł†ŁˆŲ§ŲŖ"},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var r={1:"-inci",5:"-inci",8:"-inci",70:"-inci",80:"-inci",2:"-nci",7:"-nci",20:"-nci",50:"-nci",3:"-Ć¼ncĆ¼",4:"-Ć¼ncĆ¼",100:"-Ć¼ncĆ¼",6:"-ncı",9:"-uncu",10:"-uncu",30:"-uncu",60:"-ıncı",90:"-ıncı"},t;e.defineLocale("az",{months:"yanvar_fevral_mart_aprel_may_iyun_iyul_avqust_sentyabr_oktyabr_noyabr_dekabr".split("_"),monthsShort:"yan_fev_mar_apr_may_iyn_iyl_avq_sen_okt_noy_dek".split("_"),weekdays:"Bazar_Bazar ertəsi_Ƈərşənbə axşamı_Ƈərşənbə_CĆ¼mə axşamı_CĆ¼mə_Şənbə".split("_"),weekdaysShort:"Baz_BzE_ƇAx_Ƈər_CAx_CĆ¼m_Şən".split("_"),weekdaysMin:"Bz_BE_ƇA_Ƈə_CA_CĆ¼_Şə".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bugĆ¼n saat] LT",nextDay:"[sabah saat] LT",nextWeek:"[gələn həftə] dddd [saat] LT",lastDay:"[dĆ¼nən] LT",lastWeek:"[keƧən həftə] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s əvvəl",s:"bir neƧə saniyə",ss:"%d saniyə",m:"bir dəqiqə",mm:"%d dəqiqə",h:"bir saat",hh:"%d saat",d:"bir gĆ¼n",dd:"%d gĆ¼n",M:"bir ay",MM:"%d ay",y:"bir il",yy:"%d il"},meridiemParse:/gecə|səhər|gĆ¼ndĆ¼z|axşam/,isPM:function(e){return/^(gĆ¼ndĆ¼z|axşam)$/.test(e)},meridiem:function(e,t,n){if(e<4)return"gecə";else if(e<12)return"səhər";else if(e<17)return"gĆ¼ndĆ¼z";else return"axşam"},dayOfMonthOrdinalParse:/\d{1,2}-(ıncı|inci|nci|Ć¼ncĆ¼|ncı|uncu)/,ordinal:function(e){if(e===0)return e+"-ıncı";var t=e%10,n=e%100-t,a=e>=100?100:null;return e+(r[t]||r[n]||r[a])},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var r={1:"-inci",5:"-inci",8:"-inci",70:"-inci",80:"-inci",2:"-nci",7:"-nci",20:"-nci",50:"-nci",3:"-Ć¼ncĆ¼",4:"-Ć¼ncĆ¼",100:"-Ć¼ncĆ¼",6:"-ncı",9:"-uncu",10:"-uncu",30:"-uncu",60:"-ıncı",90:"-ıncı"},t;e.defineLocale("az",{months:"yanvar_fevral_mart_aprel_may_iyun_iyul_avqust_sentyabr_oktyabr_noyabr_dekabr".split("_"),monthsShort:"yan_fev_mar_apr_may_iyn_iyl_avq_sen_okt_noy_dek".split("_"),weekdays:"Bazar_Bazar ertəsi_Ƈərşənbə axşamı_Ƈərşənbə_CĆ¼mə axşamı_CĆ¼mə_Şənbə".split("_"),weekdaysShort:"Baz_BzE_ƇAx_Ƈər_CAx_CĆ¼m_Şən".split("_"),weekdaysMin:"Bz_BE_ƇA_Ƈə_CA_CĆ¼_Şə".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bugĆ¼n saat] LT",nextDay:"[sabah saat] LT",nextWeek:"[gələn həftə] dddd [saat] LT",lastDay:"[dĆ¼nən] LT",lastWeek:"[keƧən həftə] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s əvvəl",s:"bir neƧə saniyə",ss:"%d saniyə",m:"bir dəqiqə",mm:"%d dəqiqə",h:"bir saat",hh:"%d saat",d:"bir gĆ¼n",dd:"%d gĆ¼n",M:"bir ay",MM:"%d ay",y:"bir il",yy:"%d il"},meridiemParse:/gecə|səhər|gĆ¼ndĆ¼z|axşam/,isPM:function(e){return/^(gĆ¼ndĆ¼z|axşam)$/.test(e)},meridiem:function(e,t,n){if(e<4)return"gecə";else if(e<12)return"səhər";else if(e<17)return"gĆ¼ndĆ¼z";else return"axşam"},dayOfMonthOrdinalParse:/\d{1,2}-(ıncı|inci|nci|Ć¼ncĆ¼|ncı|uncu)/,ordinal:function(e){if(e===0)return e+"-ıncı";var t=e%10,n=e%100-t,a=e>=100?100:null;return e+(r[t]||r[n]||r[a])},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function r(e,t){var n=e.split("_");return t%10===1&&t%100!==11?n[0]:t%10>=2&&t%10<=4&&(t%100<10||t%100>=20)?n[1]:n[2]}function t(e,t,n){var a={ss:t?"сŠµŠŗуŠ½Š“Š°_сŠµŠŗуŠ½Š“ы_сŠµŠŗуŠ½Š“":"сŠµŠŗуŠ½Š“у_сŠµŠŗуŠ½Š“ы_сŠµŠŗуŠ½Š“",mm:t?"хŠ²Ń–Š»Ń–Š½Š°_хŠ²Ń–Š»Ń–Š½Ń‹_хŠ²Ń–Š»Ń–Š½":"хŠ²Ń–Š»Ń–Š½Ńƒ_хŠ²Ń–Š»Ń–Š½Ń‹_хŠ²Ń–Š»Ń–Š½",hh:t?"Š³Š°Š“Š·Ń–Š½Š°_Š³Š°Š“Š·Ń–Š½Ń‹_Š³Š°Š“Š·Ń–Š½":"Š³Š°Š“Š·Ń–Š½Ńƒ_Š³Š°Š“Š·Ń–Š½Ń‹_Š³Š°Š“Š·Ń–Š½",dd:"Š“Š·ŠµŠ½ŃŒ_Š“Š½Ń–_Š“Š·Ń‘Š½",MM:"Š¼ŠµŃŃŃ†_Š¼ŠµŃŃŃ†Ń‹_Š¼ŠµŃŃŃ†Š°Ńž",yy:"Š³Š¾Š“_Š³Š°Š“ы_Š³Š°Š“Š¾Ńž"};if(n==="m")return t?"хŠ²Ń–Š»Ń–Š½Š°":"хŠ²Ń–Š»Ń–Š½Ńƒ";else if(n==="h")return t?"Š³Š°Š“Š·Ń–Š½Š°":"Š³Š°Š“Š·Ń–Š½Ńƒ";else return e+" "+r(a[n],+e)}var n;e.defineLocale("be",{months:{format:"стуŠ“Š·ŠµŠ½Ń_Š»ŃŽŃ‚Š°Š³Š°_сŠ°ŠŗŠ°Š²Ń–ŠŗŠ°_ŠŗрŠ°ŃŠ°Š²Ń–ŠŗŠ°_трŠ°ŃžŠ½Ń_чэрŠ²ŠµŠ½Ń_Š»Ń–ŠæŠµŠ½Ń_Š¶Š½Ń–ŃžŠ½Ń_Š²ŠµŃ€Š°ŃŠ½Ń_ŠŗŠ°ŃŃ‚рычŠ½Ń–ŠŗŠ°_Š»Ń–стŠ°ŠæŠ°Š“Š°_сŠ½ŠµŠ¶Š½Ń".split("_"),standalone:"стуŠ“Š·ŠµŠ½ŃŒ_Š»ŃŽŃ‚Ń‹_сŠ°ŠŗŠ°Š²Ń–Šŗ_ŠŗрŠ°ŃŠ°Š²Ń–Šŗ_трŠ°Š²ŠµŠ½ŃŒ_чэрŠ²ŠµŠ½ŃŒ_Š»Ń–ŠæŠµŠ½ŃŒ_Š¶Š½Ń–Š²ŠµŠ½ŃŒ_Š²ŠµŃ€Š°ŃŠµŠ½ŃŒ_ŠŗŠ°ŃŃ‚рычŠ½Ń–Šŗ_Š»Ń–стŠ°ŠæŠ°Š“_сŠ½ŠµŠ¶Š°Š½ŃŒ".split("_")},monthsShort:"стуŠ“_Š»ŃŽŃ‚_сŠ°Šŗ_ŠŗрŠ°Ń_трŠ°Š²_чэрŠ²_Š»Ń–Šæ_Š¶Š½Ń–Š²_Š²ŠµŃ€_ŠŗŠ°ŃŃ‚_Š»Ń–ст_сŠ½ŠµŠ¶".split("_"),weekdays:{format:"Š½ŃŠ“Š·ŠµŠ»ŃŽ_ŠæŠ°Š½ŃŠ“Š·ŠµŠ»Š°Šŗ_Š°ŃžŃ‚Š¾Ń€Š°Šŗ_сŠµŃ€Š°Š“у_чŠ°Ń†Š²ŠµŃ€_ŠæятŠ½Ń–Ń†Ńƒ_суŠ±Š¾Ń‚Ńƒ".split("_"),standalone:"Š½ŃŠ“Š·ŠµŠ»Ń_ŠæŠ°Š½ŃŠ“Š·ŠµŠ»Š°Šŗ_Š°ŃžŃ‚Š¾Ń€Š°Šŗ_сŠµŃ€Š°Š“Š°_чŠ°Ń†Š²ŠµŃ€_ŠæятŠ½Ń–цŠ°_суŠ±Š¾Ń‚Š°".split("_"),isFormat:/\[ ?[Š£ŃƒŃž] ?(?:Š¼Ń–Š½ŃƒŠ»ŃƒŃŽ|Š½Š°ŃŃ‚ŃƒŠæŠ½ŃƒŃŽ)? ?\] ?dddd/},weekdaysShort:"Š½Š“_ŠæŠ½_Š°Ń‚_ср_чц_Šæт_сŠ±".split("_"),weekdaysMin:"Š½Š“_ŠæŠ½_Š°Ń‚_ср_чц_Šæт_сŠ±".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY Š³.",LLL:"D MMMM YYYY Š³., HH:mm",LLLL:"dddd, D MMMM YYYY Š³., HH:mm"},calendar:{sameDay:"[Š”ёŠ½Š½Ń ў] LT",nextDay:"[Š—Š°ŃžŃ‚Ń€Š° ў] LT",lastDay:"[Š£Ń‡Š¾Ń€Š° ў] LT",nextWeek:function(){return"[Š£] dddd [ў] LT"},lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return"[Š£ Š¼Ń–Š½ŃƒŠ»ŃƒŃŽ] dddd [ў] LT";case 1:case 2:case 4:return"[Š£ Š¼Ń–Š½ŃƒŠ»Ń‹] dddd [ў] LT"}},sameElse:"L"},relativeTime:{future:"ŠæрŠ°Š· %s",past:"%s тŠ°Š¼Ńƒ",s:"Š½ŠµŠŗŠ°Š»ŃŒŠŗі сŠµŠŗуŠ½Š“",m:t,mm:t,h:t,hh:t,d:"Š“Š·ŠµŠ½ŃŒ",dd:t,M:"Š¼ŠµŃŃŃ†",MM:t,y:"Š³Š¾Š“",yy:t},meridiemParse:/Š½Š¾Ń‡Ń‹|рŠ°Š½Ń–цы|Š“Š½Ń|Š²ŠµŃ‡Š°Ń€Š°/,isPM:function(e){return/^(Š“Š½Ń|Š²ŠµŃ‡Š°Ń€Š°)$/.test(e)},meridiem:function(e,t,n){if(e<4)return"Š½Š¾Ń‡Ń‹";else if(e<12)return"рŠ°Š½Ń–цы";else if(e<17)return"Š“Š½Ń";else return"Š²ŠµŃ‡Š°Ń€Š°"},dayOfMonthOrdinalParse:/\d{1,2}-(і|ы|Š³Š°)/,ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":case"w":case"W":return(e%10===2||e%10===3)&&e%100!==12&&e%100!==13?e+"-і":e+"-ы";case"D":return e+"-Š³Š°";default:return e}},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function r(e,t){var n=e.split("_");return t%10===1&&t%100!==11?n[0]:t%10>=2&&t%10<=4&&(t%100<10||t%100>=20)?n[1]:n[2]}function t(e,t,n){var a={ss:t?"сŠµŠŗуŠ½Š“Š°_сŠµŠŗуŠ½Š“ы_сŠµŠŗуŠ½Š“":"сŠµŠŗуŠ½Š“у_сŠµŠŗуŠ½Š“ы_сŠµŠŗуŠ½Š“",mm:t?"хŠ²Ń–Š»Ń–Š½Š°_хŠ²Ń–Š»Ń–Š½Ń‹_хŠ²Ń–Š»Ń–Š½":"хŠ²Ń–Š»Ń–Š½Ńƒ_хŠ²Ń–Š»Ń–Š½Ń‹_хŠ²Ń–Š»Ń–Š½",hh:t?"Š³Š°Š“Š·Ń–Š½Š°_Š³Š°Š“Š·Ń–Š½Ń‹_Š³Š°Š“Š·Ń–Š½":"Š³Š°Š“Š·Ń–Š½Ńƒ_Š³Š°Š“Š·Ń–Š½Ń‹_Š³Š°Š“Š·Ń–Š½",dd:"Š“Š·ŠµŠ½ŃŒ_Š“Š½Ń–_Š“Š·Ń‘Š½",MM:"Š¼ŠµŃŃŃ†_Š¼ŠµŃŃŃ†Ń‹_Š¼ŠµŃŃŃ†Š°Ńž",yy:"Š³Š¾Š“_Š³Š°Š“ы_Š³Š°Š“Š¾Ńž"};if(n==="m")return t?"хŠ²Ń–Š»Ń–Š½Š°":"хŠ²Ń–Š»Ń–Š½Ńƒ";else if(n==="h")return t?"Š³Š°Š“Š·Ń–Š½Š°":"Š³Š°Š“Š·Ń–Š½Ńƒ";else return e+" "+r(a[n],+e)}var n;e.defineLocale("be",{months:{format:"стуŠ“Š·ŠµŠ½Ń_Š»ŃŽŃ‚Š°Š³Š°_сŠ°ŠŗŠ°Š²Ń–ŠŗŠ°_ŠŗрŠ°ŃŠ°Š²Ń–ŠŗŠ°_трŠ°ŃžŠ½Ń_чэрŠ²ŠµŠ½Ń_Š»Ń–ŠæŠµŠ½Ń_Š¶Š½Ń–ŃžŠ½Ń_Š²ŠµŃ€Š°ŃŠ½Ń_ŠŗŠ°ŃŃ‚рычŠ½Ń–ŠŗŠ°_Š»Ń–стŠ°ŠæŠ°Š“Š°_сŠ½ŠµŠ¶Š½Ń".split("_"),standalone:"стуŠ“Š·ŠµŠ½ŃŒ_Š»ŃŽŃ‚Ń‹_сŠ°ŠŗŠ°Š²Ń–Šŗ_ŠŗрŠ°ŃŠ°Š²Ń–Šŗ_трŠ°Š²ŠµŠ½ŃŒ_чэрŠ²ŠµŠ½ŃŒ_Š»Ń–ŠæŠµŠ½ŃŒ_Š¶Š½Ń–Š²ŠµŠ½ŃŒ_Š²ŠµŃ€Š°ŃŠµŠ½ŃŒ_ŠŗŠ°ŃŃ‚рычŠ½Ń–Šŗ_Š»Ń–стŠ°ŠæŠ°Š“_сŠ½ŠµŠ¶Š°Š½ŃŒ".split("_")},monthsShort:"стуŠ“_Š»ŃŽŃ‚_сŠ°Šŗ_ŠŗрŠ°Ń_трŠ°Š²_чэрŠ²_Š»Ń–Šæ_Š¶Š½Ń–Š²_Š²ŠµŃ€_ŠŗŠ°ŃŃ‚_Š»Ń–ст_сŠ½ŠµŠ¶".split("_"),weekdays:{format:"Š½ŃŠ“Š·ŠµŠ»ŃŽ_ŠæŠ°Š½ŃŠ“Š·ŠµŠ»Š°Šŗ_Š°ŃžŃ‚Š¾Ń€Š°Šŗ_сŠµŃ€Š°Š“у_чŠ°Ń†Š²ŠµŃ€_ŠæятŠ½Ń–Ń†Ńƒ_суŠ±Š¾Ń‚Ńƒ".split("_"),standalone:"Š½ŃŠ“Š·ŠµŠ»Ń_ŠæŠ°Š½ŃŠ“Š·ŠµŠ»Š°Šŗ_Š°ŃžŃ‚Š¾Ń€Š°Šŗ_сŠµŃ€Š°Š“Š°_чŠ°Ń†Š²ŠµŃ€_ŠæятŠ½Ń–цŠ°_суŠ±Š¾Ń‚Š°".split("_"),isFormat:/\[ ?[Š£ŃƒŃž] ?(?:Š¼Ń–Š½ŃƒŠ»ŃƒŃŽ|Š½Š°ŃŃ‚ŃƒŠæŠ½ŃƒŃŽ)? ?\] ?dddd/},weekdaysShort:"Š½Š“_ŠæŠ½_Š°Ń‚_ср_чц_Šæт_сŠ±".split("_"),weekdaysMin:"Š½Š“_ŠæŠ½_Š°Ń‚_ср_чц_Šæт_сŠ±".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY Š³.",LLL:"D MMMM YYYY Š³., HH:mm",LLLL:"dddd, D MMMM YYYY Š³., HH:mm"},calendar:{sameDay:"[Š”ёŠ½Š½Ń ў] LT",nextDay:"[Š—Š°ŃžŃ‚Ń€Š° ў] LT",lastDay:"[Š£Ń‡Š¾Ń€Š° ў] LT",nextWeek:function(){return"[Š£] dddd [ў] LT"},lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return"[Š£ Š¼Ń–Š½ŃƒŠ»ŃƒŃŽ] dddd [ў] LT";case 1:case 2:case 4:return"[Š£ Š¼Ń–Š½ŃƒŠ»Ń‹] dddd [ў] LT"}},sameElse:"L"},relativeTime:{future:"ŠæрŠ°Š· %s",past:"%s тŠ°Š¼Ńƒ",s:"Š½ŠµŠŗŠ°Š»ŃŒŠŗі сŠµŠŗуŠ½Š“",m:t,mm:t,h:t,hh:t,d:"Š“Š·ŠµŠ½ŃŒ",dd:t,M:"Š¼ŠµŃŃŃ†",MM:t,y:"Š³Š¾Š“",yy:t},meridiemParse:/Š½Š¾Ń‡Ń‹|рŠ°Š½Ń–цы|Š“Š½Ń|Š²ŠµŃ‡Š°Ń€Š°/,isPM:function(e){return/^(Š“Š½Ń|Š²ŠµŃ‡Š°Ń€Š°)$/.test(e)},meridiem:function(e,t,n){if(e<4)return"Š½Š¾Ń‡Ń‹";else if(e<12)return"рŠ°Š½Ń–цы";else if(e<17)return"Š“Š½Ń";else return"Š²ŠµŃ‡Š°Ń€Š°"},dayOfMonthOrdinalParse:/\d{1,2}-(і|ы|Š³Š°)/,ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":case"w":case"W":return(e%10===2||e%10===3)&&e%100!==12&&e%100!==13?e+"-і":e+"-ы";case"D":return e+"-Š³Š°";default:return e}},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("bg",{months:"яŠ½ŃƒŠ°Ń€Šø_фŠµŠ²Ń€ŃƒŠ°Ń€Šø_Š¼Š°Ń€Ń‚_Š°ŠæрŠøŠ»_Š¼Š°Š¹_юŠ½Šø_юŠ»Šø_Š°Š²Š³ŃƒŃŃ‚_сŠµŠæтŠµŠ¼Š²Ń€Šø_Š¾ŠŗтŠ¾Š¼Š²Ń€Šø_Š½Š¾ŠµŠ¼Š²Ń€Šø_Š“ŠµŠŗŠµŠ¼Š²Ń€Šø".split("_"),monthsShort:"яŠ½Ńƒ_фŠµŠ²_Š¼Š°Ń€_Š°Šæр_Š¼Š°Š¹_юŠ½Šø_юŠ»Šø_Š°Š²Š³_сŠµŠæ_Š¾Šŗт_Š½Š¾Šµ_Š“ŠµŠŗ".split("_"),weekdays:"Š½ŠµŠ“ŠµŠ»Ń_ŠæŠ¾Š½ŠµŠ“ŠµŠ»Š½ŠøŠŗ_Š²Ń‚Š¾Ń€Š½ŠøŠŗ_сряŠ“Š°_чŠµŃ‚Š²ŃŠŃ€Ń‚ŃŠŠŗ_ŠæŠµŃ‚ŃŠŠŗ_съŠ±Š¾Ń‚Š°".split("_"),weekdaysShort:"Š½ŠµŠ“_ŠæŠ¾Š½_Š²Ń‚Š¾_сря_чŠµŃ‚_ŠæŠµŃ‚_съŠ±".split("_"),weekdaysMin:"Š½Š“_ŠæŠ½_Š²Ń‚_ср_чт_Šæт_сŠ±".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[Š”Š½ŠµŃ Š²] LT",nextDay:"[Š£Ń‚Ń€Šµ Š²] LT",nextWeek:"dddd [Š²] LT",lastDay:"[Š’чŠµŃ€Š° Š²] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[ŠœŠøŠ½Š°Š»Š°Ń‚Š°] dddd [Š²] LT";case 1:case 2:case 4:case 5:return"[ŠœŠøŠ½Š°Š»Šøя] dddd [Š²] LT"}},sameElse:"L"},relativeTime:{future:"сŠ»ŠµŠ“ %s",past:"ŠæрŠµŠ“Šø %s",s:"Š½ŃŠŗŠ¾Š»ŠŗŠ¾ сŠµŠŗуŠ½Š“Šø",ss:"%d сŠµŠŗуŠ½Š“Šø",m:"Š¼ŠøŠ½ŃƒŃ‚Š°",mm:"%d Š¼ŠøŠ½ŃƒŃ‚Šø",h:"чŠ°Ń",hh:"%d чŠ°ŃŠ°",d:"Š“ŠµŠ½",dd:"%d Š“ŠµŠ½Š°",w:"сŠµŠ“Š¼ŠøцŠ°",ww:"%d сŠµŠ“Š¼ŠøцŠø",M:"Š¼ŠµŃŠµŃ†",MM:"%d Š¼ŠµŃŠµŃ†Š°",y:"Š³Š¾Š“ŠøŠ½Š°",yy:"%d Š³Š¾Š“ŠøŠ½Šø"},dayOfMonthOrdinalParse:/\d{1,2}-(ŠµŠ²|ŠµŠ½|тŠø|Š²Šø|рŠø|Š¼Šø)/,ordinal:function(e){var t=e%10,n=e%100;if(e===0)return e+"-ŠµŠ²";else if(n===0)return e+"-ŠµŠ½";else if(n>10&&n<20)return e+"-тŠø";else if(t===1)return e+"-Š²Šø";else if(t===2)return e+"-рŠø";else if(t===7||t===8)return e+"-Š¼Šø";else return e+"-тŠø"},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("bg",{months:"яŠ½ŃƒŠ°Ń€Šø_фŠµŠ²Ń€ŃƒŠ°Ń€Šø_Š¼Š°Ń€Ń‚_Š°ŠæрŠøŠ»_Š¼Š°Š¹_юŠ½Šø_юŠ»Šø_Š°Š²Š³ŃƒŃŃ‚_сŠµŠæтŠµŠ¼Š²Ń€Šø_Š¾ŠŗтŠ¾Š¼Š²Ń€Šø_Š½Š¾ŠµŠ¼Š²Ń€Šø_Š“ŠµŠŗŠµŠ¼Š²Ń€Šø".split("_"),monthsShort:"яŠ½Ńƒ_фŠµŠ²_Š¼Š°Ń€_Š°Šæр_Š¼Š°Š¹_юŠ½Šø_юŠ»Šø_Š°Š²Š³_сŠµŠæ_Š¾Šŗт_Š½Š¾Šµ_Š“ŠµŠŗ".split("_"),weekdays:"Š½ŠµŠ“ŠµŠ»Ń_ŠæŠ¾Š½ŠµŠ“ŠµŠ»Š½ŠøŠŗ_Š²Ń‚Š¾Ń€Š½ŠøŠŗ_сряŠ“Š°_чŠµŃ‚Š²ŃŠŃ€Ń‚ŃŠŠŗ_ŠæŠµŃ‚ŃŠŠŗ_съŠ±Š¾Ń‚Š°".split("_"),weekdaysShort:"Š½ŠµŠ“_ŠæŠ¾Š½_Š²Ń‚Š¾_сря_чŠµŃ‚_ŠæŠµŃ‚_съŠ±".split("_"),weekdaysMin:"Š½Š“_ŠæŠ½_Š²Ń‚_ср_чт_Šæт_сŠ±".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[Š”Š½ŠµŃ Š²] LT",nextDay:"[Š£Ń‚Ń€Šµ Š²] LT",nextWeek:"dddd [Š²] LT",lastDay:"[Š’чŠµŃ€Š° Š²] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[ŠœŠøŠ½Š°Š»Š°Ń‚Š°] dddd [Š²] LT";case 1:case 2:case 4:case 5:return"[ŠœŠøŠ½Š°Š»Šøя] dddd [Š²] LT"}},sameElse:"L"},relativeTime:{future:"сŠ»ŠµŠ“ %s",past:"ŠæрŠµŠ“Šø %s",s:"Š½ŃŠŗŠ¾Š»ŠŗŠ¾ сŠµŠŗуŠ½Š“Šø",ss:"%d сŠµŠŗуŠ½Š“Šø",m:"Š¼ŠøŠ½ŃƒŃ‚Š°",mm:"%d Š¼ŠøŠ½ŃƒŃ‚Šø",h:"чŠ°Ń",hh:"%d чŠ°ŃŠ°",d:"Š“ŠµŠ½",dd:"%d Š“ŠµŠ½Š°",w:"сŠµŠ“Š¼ŠøцŠ°",ww:"%d сŠµŠ“Š¼ŠøцŠø",M:"Š¼ŠµŃŠµŃ†",MM:"%d Š¼ŠµŃŠµŃ†Š°",y:"Š³Š¾Š“ŠøŠ½Š°",yy:"%d Š³Š¾Š“ŠøŠ½Šø"},dayOfMonthOrdinalParse:/\d{1,2}-(ŠµŠ²|ŠµŠ½|тŠø|Š²Šø|рŠø|Š¼Šø)/,ordinal:function(e){var t=e%10,n=e%100;if(e===0)return e+"-ŠµŠ²";else if(n===0)return e+"-ŠµŠ½";else if(n>10&&n<20)return e+"-тŠø";else if(t===1)return e+"-Š²Šø";else if(t===2)return e+"-рŠø";else if(t===7||t===8)return e+"-Š¼Šø";else return e+"-тŠø"},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("bm",{months:"Zanwuyekalo_Fewuruyekalo_Marisikalo_Awirilikalo_Mɛkalo_Zuwɛnkalo_Zuluyekalo_Utikalo_Sɛtanburukalo_ɔkutɔburukalo_Nowanburukalo_Desanburukalo".split("_"),monthsShort:"Zan_Few_Mar_Awi_Mɛ_Zuw_Zul_Uti_Sɛt_ɔku_Now_Des".split("_"),weekdays:"Kari_Ntɛnɛn_Tarata_Araba_Alamisa_Juma_Sibiri".split("_"),weekdaysShort:"Kar_Ntɛ_Tar_Ara_Ala_Jum_Sib".split("_"),weekdaysMin:"Ka_Nt_Ta_Ar_Al_Ju_Si".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"MMMM [tile] D [san] YYYY",LLL:"MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm",LLLL:"dddd MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm"},calendar:{sameDay:"[Bi lɛrɛ] LT",nextDay:"[Sini lɛrɛ] LT",nextWeek:"dddd [don lɛrɛ] LT",lastDay:"[Kunu lɛrɛ] LT",lastWeek:"dddd [tɛmɛnen lɛrɛ] LT",sameElse:"L"},relativeTime:{future:"%s kɔnɔ",past:"a bɛ %s bɔ",s:"sanga dama dama",ss:"sekondi %d",m:"miniti kelen",mm:"miniti %d",h:"lɛrɛ kelen",hh:"lɛrɛ %d",d:"tile kelen",dd:"tile %d",M:"kalo kelen",MM:"kalo %d",y:"san kelen",yy:"san %d"},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("bm",{months:"Zanwuyekalo_Fewuruyekalo_Marisikalo_Awirilikalo_Mɛkalo_Zuwɛnkalo_Zuluyekalo_Utikalo_Sɛtanburukalo_ɔkutɔburukalo_Nowanburukalo_Desanburukalo".split("_"),monthsShort:"Zan_Few_Mar_Awi_Mɛ_Zuw_Zul_Uti_Sɛt_ɔku_Now_Des".split("_"),weekdays:"Kari_Ntɛnɛn_Tarata_Araba_Alamisa_Juma_Sibiri".split("_"),weekdaysShort:"Kar_Ntɛ_Tar_Ara_Ala_Jum_Sib".split("_"),weekdaysMin:"Ka_Nt_Ta_Ar_Al_Ju_Si".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"MMMM [tile] D [san] YYYY",LLL:"MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm",LLLL:"dddd MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm"},calendar:{sameDay:"[Bi lɛrɛ] LT",nextDay:"[Sini lɛrɛ] LT",nextWeek:"dddd [don lɛrɛ] LT",lastDay:"[Kunu lɛrɛ] LT",lastWeek:"dddd [tɛmɛnen lɛrɛ] LT",sameElse:"L"},relativeTime:{future:"%s kɔnɔ",past:"a bɛ %s bɔ",s:"sanga dama dama",ss:"sekondi %d",m:"miniti kelen",mm:"miniti %d",h:"lɛrɛ kelen",hh:"lɛrɛ %d",d:"tile kelen",dd:"tile %d",M:"kalo kelen",MM:"kalo %d",y:"san kelen",yy:"san %d"},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"ą§§",2:"ą§Ø",3:"ą§©",4:"ą§Ŗ",5:"ą§«",6:"ą§¬",7:"ą§­",8:"ą§®",9:"ą§Æ",0:"ą§¦"},n={"ą§§":"1","ą§Ø":"2","ą§©":"3","ą§Ŗ":"4","ą§«":"5","ą§¬":"6","ą§­":"7","ą§®":"8","ą§Æ":"9","ą§¦":"0"},a;e.defineLocale("bn",{months:"ą¦œą¦¾ą¦Øą§ą§Ÿą¦¾ą¦°ą¦æ_ą¦«ą§‡ą¦¬ą§ą¦°ą§ą§Ÿą¦¾ą¦°ą¦æ_ą¦®ą¦¾ą¦°ą§ą¦š_ą¦ą¦Ŗą§ą¦°ą¦æą¦²_ą¦®ą§‡_ą¦œą§ą¦Ø_ą¦œą§ą¦²ą¦¾ą¦‡_ą¦†ą¦—ą¦øą§ą¦Ÿ_ą¦øą§‡ą¦Ŗą§ą¦Ÿą§‡ą¦®ą§ą¦¬ą¦°_ą¦…ą¦•ą§ą¦Ÿą§‹ą¦¬ą¦°_ą¦Øą¦­ą§‡ą¦®ą§ą¦¬ą¦°_ą¦”ą¦æą¦øą§‡ą¦®ą§ą¦¬ą¦°".split("_"),monthsShort:"ą¦œą¦¾ą¦Øą§_ą¦«ą§‡ą¦¬ą§ą¦°ą§_ą¦®ą¦¾ą¦°ą§ą¦š_ą¦ą¦Ŗą§ą¦°ą¦æą¦²_ą¦®ą§‡_ą¦œą§ą¦Ø_ą¦œą§ą¦²ą¦¾ą¦‡_ą¦†ą¦—ą¦øą§ą¦Ÿ_ą¦øą§‡ą¦Ŗą§ą¦Ÿ_ą¦…ą¦•ą§ą¦Ÿą§‹_ą¦Øą¦­ą§‡_ą¦”ą¦æą¦øą§‡".split("_"),weekdays:"ą¦°ą¦¬ą¦æą¦¬ą¦¾ą¦°_ą¦øą§‹ą¦®ą¦¬ą¦¾ą¦°_ą¦®ą¦™ą§ą¦—ą¦²ą¦¬ą¦¾ą¦°_ą¦¬ą§ą¦§ą¦¬ą¦¾ą¦°_ą¦¬ą§ƒą¦¹ą¦øą§ą¦Ŗą¦¤ą¦æą¦¬ą¦¾ą¦°_ą¦¶ą§ą¦•ą§ą¦°ą¦¬ą¦¾ą¦°_ą¦¶ą¦Øą¦æą¦¬ą¦¾ą¦°".split("_"),weekdaysShort:"ą¦°ą¦¬ą¦æ_ą¦øą§‹ą¦®_ą¦®ą¦™ą§ą¦—ą¦²_ą¦¬ą§ą¦§_ą¦¬ą§ƒą¦¹ą¦øą§ą¦Ŗą¦¤ą¦æ_ą¦¶ą§ą¦•ą§ą¦°_ą¦¶ą¦Øą¦æ".split("_"),weekdaysMin:"ą¦°ą¦¬ą¦æ_ą¦øą§‹ą¦®_ą¦®ą¦™ą§ą¦—ą¦²_ą¦¬ą§ą¦§_ą¦¬ą§ƒą¦¹_ą¦¶ą§ą¦•ą§ą¦°_ą¦¶ą¦Øą¦æ".split("_"),longDateFormat:{LT:"A h:mm ą¦øą¦®ą§Ÿ",LTS:"A h:mm:ss ą¦øą¦®ą§Ÿ",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ą¦øą¦®ą§Ÿ",LLLL:"dddd, D MMMM YYYY, A h:mm ą¦øą¦®ą§Ÿ"},calendar:{sameDay:"[ą¦†ą¦œ] LT",nextDay:"[ą¦†ą¦—ą¦¾ą¦®ą§€ą¦•ą¦¾ą¦²] LT",nextWeek:"dddd, LT",lastDay:"[ą¦—ą¦¤ą¦•ą¦¾ą¦²] LT",lastWeek:"[ą¦—ą¦¤] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą¦Ŗą¦°ą§‡",past:"%s ą¦†ą¦—ą§‡",s:"ą¦•ą§Ÿą§‡ą¦• ą¦øą§‡ą¦•ą§‡ą¦Øą§ą¦”",ss:"%d ą¦øą§‡ą¦•ą§‡ą¦Øą§ą¦”",m:"ą¦ą¦• ą¦®ą¦æą¦Øą¦æą¦Ÿ",mm:"%d ą¦®ą¦æą¦Øą¦æą¦Ÿ",h:"ą¦ą¦• ą¦˜ą¦Øą§ą¦Ÿą¦¾",hh:"%d ą¦˜ą¦Øą§ą¦Ÿą¦¾",d:"ą¦ą¦• ą¦¦ą¦æą¦Ø",dd:"%d ą¦¦ą¦æą¦Ø",M:"ą¦ą¦• ą¦®ą¦¾ą¦ø",MM:"%d ą¦®ą¦¾ą¦ø",y:"ą¦ą¦• ą¦¬ą¦›ą¦°",yy:"%d ą¦¬ą¦›ą¦°"},preparse:function(e){return e.replace(/[ą§§ą§Øą§©ą§Ŗą§«ą§¬ą§­ą§®ą§Æą§¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą¦°ą¦¾ą¦¤|ą¦øą¦•ą¦¾ą¦²|ą¦¦ą§ą¦Ŗą§ą¦°|ą¦¬ą¦æą¦•ą¦¾ą¦²|ą¦°ą¦¾ą¦¤/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą¦°ą¦¾ą¦¤"&&e>=4||t==="ą¦¦ą§ą¦Ŗą§ą¦°"&&e<5||t==="ą¦¬ą¦æą¦•ą¦¾ą¦²")return e+12;else return e},meridiem:function(e,t,n){if(e<4)return"ą¦°ą¦¾ą¦¤";else if(e<10)return"ą¦øą¦•ą¦¾ą¦²";else if(e<17)return"ą¦¦ą§ą¦Ŗą§ą¦°";else if(e<20)return"ą¦¬ą¦æą¦•ą¦¾ą¦²";else return"ą¦°ą¦¾ą¦¤"},week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"ą§§",2:"ą§Ø",3:"ą§©",4:"ą§Ŗ",5:"ą§«",6:"ą§¬",7:"ą§­",8:"ą§®",9:"ą§Æ",0:"ą§¦"},n={"ą§§":"1","ą§Ø":"2","ą§©":"3","ą§Ŗ":"4","ą§«":"5","ą§¬":"6","ą§­":"7","ą§®":"8","ą§Æ":"9","ą§¦":"0"},a;e.defineLocale("bn",{months:"ą¦œą¦¾ą¦Øą§ą§Ÿą¦¾ą¦°ą¦æ_ą¦«ą§‡ą¦¬ą§ą¦°ą§ą§Ÿą¦¾ą¦°ą¦æ_ą¦®ą¦¾ą¦°ą§ą¦š_ą¦ą¦Ŗą§ą¦°ą¦æą¦²_ą¦®ą§‡_ą¦œą§ą¦Ø_ą¦œą§ą¦²ą¦¾ą¦‡_ą¦†ą¦—ą¦øą§ą¦Ÿ_ą¦øą§‡ą¦Ŗą§ą¦Ÿą§‡ą¦®ą§ą¦¬ą¦°_ą¦…ą¦•ą§ą¦Ÿą§‹ą¦¬ą¦°_ą¦Øą¦­ą§‡ą¦®ą§ą¦¬ą¦°_ą¦”ą¦æą¦øą§‡ą¦®ą§ą¦¬ą¦°".split("_"),monthsShort:"ą¦œą¦¾ą¦Øą§_ą¦«ą§‡ą¦¬ą§ą¦°ą§_ą¦®ą¦¾ą¦°ą§ą¦š_ą¦ą¦Ŗą§ą¦°ą¦æą¦²_ą¦®ą§‡_ą¦œą§ą¦Ø_ą¦œą§ą¦²ą¦¾ą¦‡_ą¦†ą¦—ą¦øą§ą¦Ÿ_ą¦øą§‡ą¦Ŗą§ą¦Ÿ_ą¦…ą¦•ą§ą¦Ÿą§‹_ą¦Øą¦­ą§‡_ą¦”ą¦æą¦øą§‡".split("_"),weekdays:"ą¦°ą¦¬ą¦æą¦¬ą¦¾ą¦°_ą¦øą§‹ą¦®ą¦¬ą¦¾ą¦°_ą¦®ą¦™ą§ą¦—ą¦²ą¦¬ą¦¾ą¦°_ą¦¬ą§ą¦§ą¦¬ą¦¾ą¦°_ą¦¬ą§ƒą¦¹ą¦øą§ą¦Ŗą¦¤ą¦æą¦¬ą¦¾ą¦°_ą¦¶ą§ą¦•ą§ą¦°ą¦¬ą¦¾ą¦°_ą¦¶ą¦Øą¦æą¦¬ą¦¾ą¦°".split("_"),weekdaysShort:"ą¦°ą¦¬ą¦æ_ą¦øą§‹ą¦®_ą¦®ą¦™ą§ą¦—ą¦²_ą¦¬ą§ą¦§_ą¦¬ą§ƒą¦¹ą¦øą§ą¦Ŗą¦¤ą¦æ_ą¦¶ą§ą¦•ą§ą¦°_ą¦¶ą¦Øą¦æ".split("_"),weekdaysMin:"ą¦°ą¦¬ą¦æ_ą¦øą§‹ą¦®_ą¦®ą¦™ą§ą¦—ą¦²_ą¦¬ą§ą¦§_ą¦¬ą§ƒą¦¹_ą¦¶ą§ą¦•ą§ą¦°_ą¦¶ą¦Øą¦æ".split("_"),longDateFormat:{LT:"A h:mm ą¦øą¦®ą§Ÿ",LTS:"A h:mm:ss ą¦øą¦®ą§Ÿ",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ą¦øą¦®ą§Ÿ",LLLL:"dddd, D MMMM YYYY, A h:mm ą¦øą¦®ą§Ÿ"},calendar:{sameDay:"[ą¦†ą¦œ] LT",nextDay:"[ą¦†ą¦—ą¦¾ą¦®ą§€ą¦•ą¦¾ą¦²] LT",nextWeek:"dddd, LT",lastDay:"[ą¦—ą¦¤ą¦•ą¦¾ą¦²] LT",lastWeek:"[ą¦—ą¦¤] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą¦Ŗą¦°ą§‡",past:"%s ą¦†ą¦—ą§‡",s:"ą¦•ą§Ÿą§‡ą¦• ą¦øą§‡ą¦•ą§‡ą¦Øą§ą¦”",ss:"%d ą¦øą§‡ą¦•ą§‡ą¦Øą§ą¦”",m:"ą¦ą¦• ą¦®ą¦æą¦Øą¦æą¦Ÿ",mm:"%d ą¦®ą¦æą¦Øą¦æą¦Ÿ",h:"ą¦ą¦• ą¦˜ą¦Øą§ą¦Ÿą¦¾",hh:"%d ą¦˜ą¦Øą§ą¦Ÿą¦¾",d:"ą¦ą¦• ą¦¦ą¦æą¦Ø",dd:"%d ą¦¦ą¦æą¦Ø",M:"ą¦ą¦• ą¦®ą¦¾ą¦ø",MM:"%d ą¦®ą¦¾ą¦ø",y:"ą¦ą¦• ą¦¬ą¦›ą¦°",yy:"%d ą¦¬ą¦›ą¦°"},preparse:function(e){return e.replace(/[ą§§ą§Øą§©ą§Ŗą§«ą§¬ą§­ą§®ą§Æą§¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą¦°ą¦¾ą¦¤|ą¦øą¦•ą¦¾ą¦²|ą¦¦ą§ą¦Ŗą§ą¦°|ą¦¬ą¦æą¦•ą¦¾ą¦²|ą¦°ą¦¾ą¦¤/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą¦°ą¦¾ą¦¤"&&e>=4||t==="ą¦¦ą§ą¦Ŗą§ą¦°"&&e<5||t==="ą¦¬ą¦æą¦•ą¦¾ą¦²")return e+12;else return e},meridiem:function(e,t,n){if(e<4)return"ą¦°ą¦¾ą¦¤";else if(e<10)return"ą¦øą¦•ą¦¾ą¦²";else if(e<17)return"ą¦¦ą§ą¦Ŗą§ą¦°";else if(e<20)return"ą¦¬ą¦æą¦•ą¦¾ą¦²";else return"ą¦°ą¦¾ą¦¤"},week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"ą§§",2:"ą§Ø",3:"ą§©",4:"ą§Ŗ",5:"ą§«",6:"ą§¬",7:"ą§­",8:"ą§®",9:"ą§Æ",0:"ą§¦"},n={"ą§§":"1","ą§Ø":"2","ą§©":"3","ą§Ŗ":"4","ą§«":"5","ą§¬":"6","ą§­":"7","ą§®":"8","ą§Æ":"9","ą§¦":"0"},a;e.defineLocale("bn-bd",{months:"ą¦œą¦¾ą¦Øą§ą§Ÿą¦¾ą¦°ą¦æ_ą¦«ą§‡ą¦¬ą§ą¦°ą§ą§Ÿą¦¾ą¦°ą¦æ_ą¦®ą¦¾ą¦°ą§ą¦š_ą¦ą¦Ŗą§ą¦°ą¦æą¦²_ą¦®ą§‡_ą¦œą§ą¦Ø_ą¦œą§ą¦²ą¦¾ą¦‡_ą¦†ą¦—ą¦øą§ą¦Ÿ_ą¦øą§‡ą¦Ŗą§ą¦Ÿą§‡ą¦®ą§ą¦¬ą¦°_ą¦…ą¦•ą§ą¦Ÿą§‹ą¦¬ą¦°_ą¦Øą¦­ą§‡ą¦®ą§ą¦¬ą¦°_ą¦”ą¦æą¦øą§‡ą¦®ą§ą¦¬ą¦°".split("_"),monthsShort:"ą¦œą¦¾ą¦Øą§_ą¦«ą§‡ą¦¬ą§ą¦°ą§_ą¦®ą¦¾ą¦°ą§ą¦š_ą¦ą¦Ŗą§ą¦°ą¦æą¦²_ą¦®ą§‡_ą¦œą§ą¦Ø_ą¦œą§ą¦²ą¦¾ą¦‡_ą¦†ą¦—ą¦øą§ą¦Ÿ_ą¦øą§‡ą¦Ŗą§ą¦Ÿ_ą¦…ą¦•ą§ą¦Ÿą§‹_ą¦Øą¦­ą§‡_ą¦”ą¦æą¦øą§‡".split("_"),weekdays:"ą¦°ą¦¬ą¦æą¦¬ą¦¾ą¦°_ą¦øą§‹ą¦®ą¦¬ą¦¾ą¦°_ą¦®ą¦™ą§ą¦—ą¦²ą¦¬ą¦¾ą¦°_ą¦¬ą§ą¦§ą¦¬ą¦¾ą¦°_ą¦¬ą§ƒą¦¹ą¦øą§ą¦Ŗą¦¤ą¦æą¦¬ą¦¾ą¦°_ą¦¶ą§ą¦•ą§ą¦°ą¦¬ą¦¾ą¦°_ą¦¶ą¦Øą¦æą¦¬ą¦¾ą¦°".split("_"),weekdaysShort:"ą¦°ą¦¬ą¦æ_ą¦øą§‹ą¦®_ą¦®ą¦™ą§ą¦—ą¦²_ą¦¬ą§ą¦§_ą¦¬ą§ƒą¦¹ą¦øą§ą¦Ŗą¦¤ą¦æ_ą¦¶ą§ą¦•ą§ą¦°_ą¦¶ą¦Øą¦æ".split("_"),weekdaysMin:"ą¦°ą¦¬ą¦æ_ą¦øą§‹ą¦®_ą¦®ą¦™ą§ą¦—ą¦²_ą¦¬ą§ą¦§_ą¦¬ą§ƒą¦¹_ą¦¶ą§ą¦•ą§ą¦°_ą¦¶ą¦Øą¦æ".split("_"),longDateFormat:{LT:"A h:mm ą¦øą¦®ą§Ÿ",LTS:"A h:mm:ss ą¦øą¦®ą§Ÿ",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ą¦øą¦®ą§Ÿ",LLLL:"dddd, D MMMM YYYY, A h:mm ą¦øą¦®ą§Ÿ"},calendar:{sameDay:"[ą¦†ą¦œ] LT",nextDay:"[ą¦†ą¦—ą¦¾ą¦®ą§€ą¦•ą¦¾ą¦²] LT",nextWeek:"dddd, LT",lastDay:"[ą¦—ą¦¤ą¦•ą¦¾ą¦²] LT",lastWeek:"[ą¦—ą¦¤] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą¦Ŗą¦°ą§‡",past:"%s ą¦†ą¦—ą§‡",s:"ą¦•ą§Ÿą§‡ą¦• ą¦øą§‡ą¦•ą§‡ą¦Øą§ą¦”",ss:"%d ą¦øą§‡ą¦•ą§‡ą¦Øą§ą¦”",m:"ą¦ą¦• ą¦®ą¦æą¦Øą¦æą¦Ÿ",mm:"%d ą¦®ą¦æą¦Øą¦æą¦Ÿ",h:"ą¦ą¦• ą¦˜ą¦Øą§ą¦Ÿą¦¾",hh:"%d ą¦˜ą¦Øą§ą¦Ÿą¦¾",d:"ą¦ą¦• ą¦¦ą¦æą¦Ø",dd:"%d ą¦¦ą¦æą¦Ø",M:"ą¦ą¦• ą¦®ą¦¾ą¦ø",MM:"%d ą¦®ą¦¾ą¦ø",y:"ą¦ą¦• ą¦¬ą¦›ą¦°",yy:"%d ą¦¬ą¦›ą¦°"},preparse:function(e){return e.replace(/[ą§§ą§Øą§©ą§Ŗą§«ą§¬ą§­ą§®ą§Æą§¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą¦°ą¦¾ą¦¤|ą¦­ą§‹ą¦°|ą¦øą¦•ą¦¾ą¦²|ą¦¦ą§ą¦Ŗą§ą¦°|ą¦¬ą¦æą¦•ą¦¾ą¦²|ą¦øą¦Øą§ą¦§ą§ą¦Æą¦¾|ą¦°ą¦¾ą¦¤/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą¦°ą¦¾ą¦¤")return e<4?e:e+12;else if(t==="ą¦­ą§‹ą¦°")return e;else if(t==="ą¦øą¦•ą¦¾ą¦²")return e;else if(t==="ą¦¦ą§ą¦Ŗą§ą¦°")return e>=3?e:e+12;else if(t==="ą¦¬ą¦æą¦•ą¦¾ą¦²")return e+12;else if(t==="ą¦øą¦Øą§ą¦§ą§ą¦Æą¦¾")return e+12},meridiem:function(e,t,n){if(e<4)return"ą¦°ą¦¾ą¦¤";else if(e<6)return"ą¦­ą§‹ą¦°";else if(e<12)return"ą¦øą¦•ą¦¾ą¦²";else if(e<15)return"ą¦¦ą§ą¦Ŗą§ą¦°";else if(e<18)return"ą¦¬ą¦æą¦•ą¦¾ą¦²";else if(e<20)return"ą¦øą¦Øą§ą¦§ą§ą¦Æą¦¾";else return"ą¦°ą¦¾ą¦¤"},week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"ą§§",2:"ą§Ø",3:"ą§©",4:"ą§Ŗ",5:"ą§«",6:"ą§¬",7:"ą§­",8:"ą§®",9:"ą§Æ",0:"ą§¦"},n={"ą§§":"1","ą§Ø":"2","ą§©":"3","ą§Ŗ":"4","ą§«":"5","ą§¬":"6","ą§­":"7","ą§®":"8","ą§Æ":"9","ą§¦":"0"},a;e.defineLocale("bn-bd",{months:"ą¦œą¦¾ą¦Øą§ą§Ÿą¦¾ą¦°ą¦æ_ą¦«ą§‡ą¦¬ą§ą¦°ą§ą§Ÿą¦¾ą¦°ą¦æ_ą¦®ą¦¾ą¦°ą§ą¦š_ą¦ą¦Ŗą§ą¦°ą¦æą¦²_ą¦®ą§‡_ą¦œą§ą¦Ø_ą¦œą§ą¦²ą¦¾ą¦‡_ą¦†ą¦—ą¦øą§ą¦Ÿ_ą¦øą§‡ą¦Ŗą§ą¦Ÿą§‡ą¦®ą§ą¦¬ą¦°_ą¦…ą¦•ą§ą¦Ÿą§‹ą¦¬ą¦°_ą¦Øą¦­ą§‡ą¦®ą§ą¦¬ą¦°_ą¦”ą¦æą¦øą§‡ą¦®ą§ą¦¬ą¦°".split("_"),monthsShort:"ą¦œą¦¾ą¦Øą§_ą¦«ą§‡ą¦¬ą§ą¦°ą§_ą¦®ą¦¾ą¦°ą§ą¦š_ą¦ą¦Ŗą§ą¦°ą¦æą¦²_ą¦®ą§‡_ą¦œą§ą¦Ø_ą¦œą§ą¦²ą¦¾ą¦‡_ą¦†ą¦—ą¦øą§ą¦Ÿ_ą¦øą§‡ą¦Ŗą§ą¦Ÿ_ą¦…ą¦•ą§ą¦Ÿą§‹_ą¦Øą¦­ą§‡_ą¦”ą¦æą¦øą§‡".split("_"),weekdays:"ą¦°ą¦¬ą¦æą¦¬ą¦¾ą¦°_ą¦øą§‹ą¦®ą¦¬ą¦¾ą¦°_ą¦®ą¦™ą§ą¦—ą¦²ą¦¬ą¦¾ą¦°_ą¦¬ą§ą¦§ą¦¬ą¦¾ą¦°_ą¦¬ą§ƒą¦¹ą¦øą§ą¦Ŗą¦¤ą¦æą¦¬ą¦¾ą¦°_ą¦¶ą§ą¦•ą§ą¦°ą¦¬ą¦¾ą¦°_ą¦¶ą¦Øą¦æą¦¬ą¦¾ą¦°".split("_"),weekdaysShort:"ą¦°ą¦¬ą¦æ_ą¦øą§‹ą¦®_ą¦®ą¦™ą§ą¦—ą¦²_ą¦¬ą§ą¦§_ą¦¬ą§ƒą¦¹ą¦øą§ą¦Ŗą¦¤ą¦æ_ą¦¶ą§ą¦•ą§ą¦°_ą¦¶ą¦Øą¦æ".split("_"),weekdaysMin:"ą¦°ą¦¬ą¦æ_ą¦øą§‹ą¦®_ą¦®ą¦™ą§ą¦—ą¦²_ą¦¬ą§ą¦§_ą¦¬ą§ƒą¦¹_ą¦¶ą§ą¦•ą§ą¦°_ą¦¶ą¦Øą¦æ".split("_"),longDateFormat:{LT:"A h:mm ą¦øą¦®ą§Ÿ",LTS:"A h:mm:ss ą¦øą¦®ą§Ÿ",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ą¦øą¦®ą§Ÿ",LLLL:"dddd, D MMMM YYYY, A h:mm ą¦øą¦®ą§Ÿ"},calendar:{sameDay:"[ą¦†ą¦œ] LT",nextDay:"[ą¦†ą¦—ą¦¾ą¦®ą§€ą¦•ą¦¾ą¦²] LT",nextWeek:"dddd, LT",lastDay:"[ą¦—ą¦¤ą¦•ą¦¾ą¦²] LT",lastWeek:"[ą¦—ą¦¤] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą¦Ŗą¦°ą§‡",past:"%s ą¦†ą¦—ą§‡",s:"ą¦•ą§Ÿą§‡ą¦• ą¦øą§‡ą¦•ą§‡ą¦Øą§ą¦”",ss:"%d ą¦øą§‡ą¦•ą§‡ą¦Øą§ą¦”",m:"ą¦ą¦• ą¦®ą¦æą¦Øą¦æą¦Ÿ",mm:"%d ą¦®ą¦æą¦Øą¦æą¦Ÿ",h:"ą¦ą¦• ą¦˜ą¦Øą§ą¦Ÿą¦¾",hh:"%d ą¦˜ą¦Øą§ą¦Ÿą¦¾",d:"ą¦ą¦• ą¦¦ą¦æą¦Ø",dd:"%d ą¦¦ą¦æą¦Ø",M:"ą¦ą¦• ą¦®ą¦¾ą¦ø",MM:"%d ą¦®ą¦¾ą¦ø",y:"ą¦ą¦• ą¦¬ą¦›ą¦°",yy:"%d ą¦¬ą¦›ą¦°"},preparse:function(e){return e.replace(/[ą§§ą§Øą§©ą§Ŗą§«ą§¬ą§­ą§®ą§Æą§¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą¦°ą¦¾ą¦¤|ą¦­ą§‹ą¦°|ą¦øą¦•ą¦¾ą¦²|ą¦¦ą§ą¦Ŗą§ą¦°|ą¦¬ą¦æą¦•ą¦¾ą¦²|ą¦øą¦Øą§ą¦§ą§ą¦Æą¦¾|ą¦°ą¦¾ą¦¤/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą¦°ą¦¾ą¦¤")return e<4?e:e+12;else if(t==="ą¦­ą§‹ą¦°")return e;else if(t==="ą¦øą¦•ą¦¾ą¦²")return e;else if(t==="ą¦¦ą§ą¦Ŗą§ą¦°")return e>=3?e:e+12;else if(t==="ą¦¬ą¦æą¦•ą¦¾ą¦²")return e+12;else if(t==="ą¦øą¦Øą§ą¦§ą§ą¦Æą¦¾")return e+12},meridiem:function(e,t,n){if(e<4)return"ą¦°ą¦¾ą¦¤";else if(e<6)return"ą¦­ą§‹ą¦°";else if(e<12)return"ą¦øą¦•ą¦¾ą¦²";else if(e<15)return"ą¦¦ą§ą¦Ŗą§ą¦°";else if(e<18)return"ą¦¬ą¦æą¦•ą¦¾ą¦²";else if(e<20)return"ą¦øą¦Øą§ą¦§ą§ą¦Æą¦¾";else return"ą¦°ą¦¾ą¦¤"},week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"ą¼”",2:"ą¼¢",3:"ą¼£",4:"ą¼¤",5:"ą¼„",6:"ą¼¦",7:"ą¼§",8:"ą¼Ø",9:"ą¼©",0:"ą¼ "},n={"ą¼”":"1","ą¼¢":"2","ą¼£":"3","ą¼¤":"4","ą¼„":"5","ą¼¦":"6","ą¼§":"7","ą¼Ø":"8","ą¼©":"9","ą¼ ":"0"},a;e.defineLocale("bo",{months:"ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½‘ą½„ą¼‹ą½”ą½¼_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½‚ą½‰ą½²ą½¦ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½‚ą½¦ą½“ą½˜ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½–ą½žą½²ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½£ą¾”ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½‘ą¾²ą½“ą½‚ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½–ą½‘ą½“ą½“ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½–ą½¢ą¾’ą¾±ą½‘ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½‘ą½‚ą½“ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½–ą½…ą½“ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½–ą½…ą½“ą¼‹ą½‚ą½…ą½²ą½‚ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½–ą½…ą½“ą¼‹ą½‚ą½‰ą½²ą½¦ą¼‹ą½”".split("_"),monthsShort:"ą½Ÿą¾³ą¼‹1_ą½Ÿą¾³ą¼‹2_ą½Ÿą¾³ą¼‹3_ą½Ÿą¾³ą¼‹4_ą½Ÿą¾³ą¼‹5_ą½Ÿą¾³ą¼‹6_ą½Ÿą¾³ą¼‹7_ą½Ÿą¾³ą¼‹8_ą½Ÿą¾³ą¼‹9_ą½Ÿą¾³ą¼‹10_ą½Ÿą¾³ą¼‹11_ą½Ÿą¾³ą¼‹12".split("_"),monthsShortRegex:/^(ą½Ÿą¾³ą¼‹\d{1,2})/,monthsParseExact:true,weekdays:"ą½‚ą½Ÿą½ ą¼‹ą½‰ą½²ą¼‹ą½˜ą¼‹_ą½‚ą½Ÿą½ ą¼‹ą½Ÿą¾³ą¼‹ą½–ą¼‹_ą½‚ą½Ÿą½ ą¼‹ą½˜ą½²ą½‚ą¼‹ą½‘ą½˜ą½¢ą¼‹_ą½‚ą½Ÿą½ ą¼‹ą½£ą¾·ą½‚ą¼‹ą½”ą¼‹_ą½‚ą½Ÿą½ ą¼‹ą½•ą½“ą½¢ą¼‹ą½–ą½“_ą½‚ą½Ÿą½ ą¼‹ą½”ą¼‹ą½¦ą½„ą½¦ą¼‹_ą½‚ą½Ÿą½ ą¼‹ą½¦ą¾¤ą½ŗą½“ą¼‹ą½”ą¼‹".split("_"),weekdaysShort:"ą½‰ą½²ą¼‹ą½˜ą¼‹_ą½Ÿą¾³ą¼‹ą½–ą¼‹_ą½˜ą½²ą½‚ą¼‹ą½‘ą½˜ą½¢ą¼‹_ą½£ą¾·ą½‚ą¼‹ą½”ą¼‹_ą½•ą½“ą½¢ą¼‹ą½–ą½“_ą½”ą¼‹ą½¦ą½„ą½¦ą¼‹_ą½¦ą¾¤ą½ŗą½“ą¼‹ą½”ą¼‹".split("_"),weekdaysMin:"ą½‰ą½²_ą½Ÿą¾³_ą½˜ą½²ą½‚_ą½£ą¾·ą½‚_ą½•ą½“ą½¢_ą½¦ą½„ą½¦_ą½¦ą¾¤ą½ŗą½“".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[ą½‘ą½²ą¼‹ą½¢ą½²ą½„] LT",nextDay:"[ą½¦ą½„ą¼‹ą½‰ą½²ą½“] LT",nextWeek:"[ą½–ą½‘ą½“ą½“ą¼‹ą½•ą¾²ą½‚ą¼‹ą½¢ą¾—ą½ŗą½¦ą¼‹ą½˜], LT",lastDay:"[ą½ą¼‹ą½¦ą½„] LT",lastWeek:"[ą½–ą½‘ą½“ą½“ą¼‹ą½•ą¾²ą½‚ą¼‹ą½˜ą½ą½ ą¼‹ą½˜] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą½£ą¼‹",past:"%s ą½¦ą¾”ą½“ą¼‹ą½£",s:"ą½£ą½˜ą¼‹ą½¦ą½„",ss:"%d ą½¦ą¾ą½¢ą¼‹ą½†ą¼",m:"ą½¦ą¾ą½¢ą¼‹ą½˜ą¼‹ą½‚ą½…ą½²ą½‚",mm:"%d ą½¦ą¾ą½¢ą¼‹ą½˜",h:"ą½†ą½“ą¼‹ą½šą½¼ą½‘ą¼‹ą½‚ą½…ą½²ą½‚",hh:"%d ą½†ą½“ą¼‹ą½šą½¼ą½‘",d:"ą½‰ą½²ą½“ą¼‹ą½‚ą½…ą½²ą½‚",dd:"%d ą½‰ą½²ą½“ą¼‹",M:"ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½‚ą½…ą½²ą½‚",MM:"%d ą½Ÿą¾³ą¼‹ą½–",y:"ą½£ą½¼ą¼‹ą½‚ą½…ą½²ą½‚",yy:"%d ą½£ą½¼"},preparse:function(e){return e.replace(/[ą¼”ą¼¢ą¼£ą¼¤ą¼„ą¼¦ą¼§ą¼Øą¼©ą¼ ]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą½˜ą½šą½“ą¼‹ą½˜ą½¼|ą½žą½¼ą½‚ą½¦ą¼‹ą½€ą½¦|ą½‰ą½²ą½“ą¼‹ą½‚ą½“ą½„|ą½‘ą½‚ą½¼ą½„ą¼‹ą½‘ą½‚|ą½˜ą½šą½“ą¼‹ą½˜ą½¼/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą½˜ą½šą½“ą¼‹ą½˜ą½¼"&&e>=4||t==="ą½‰ą½²ą½“ą¼‹ą½‚ą½“ą½„"&&e<5||t==="ą½‘ą½‚ą½¼ą½„ą¼‹ą½‘ą½‚")return e+12;else return e},meridiem:function(e,t,n){if(e<4)return"ą½˜ą½šą½“ą¼‹ą½˜ą½¼";else if(e<10)return"ą½žą½¼ą½‚ą½¦ą¼‹ą½€ą½¦";else if(e<17)return"ą½‰ą½²ą½“ą¼‹ą½‚ą½“ą½„";else if(e<20)return"ą½‘ą½‚ą½¼ą½„ą¼‹ą½‘ą½‚";else return"ą½˜ą½šą½“ą¼‹ą½˜ą½¼"},week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"ą¼”",2:"ą¼¢",3:"ą¼£",4:"ą¼¤",5:"ą¼„",6:"ą¼¦",7:"ą¼§",8:"ą¼Ø",9:"ą¼©",0:"ą¼ "},n={"ą¼”":"1","ą¼¢":"2","ą¼£":"3","ą¼¤":"4","ą¼„":"5","ą¼¦":"6","ą¼§":"7","ą¼Ø":"8","ą¼©":"9","ą¼ ":"0"},a;e.defineLocale("bo",{months:"ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½‘ą½„ą¼‹ą½”ą½¼_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½‚ą½‰ą½²ą½¦ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½‚ą½¦ą½“ą½˜ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½–ą½žą½²ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½£ą¾”ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½‘ą¾²ą½“ą½‚ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½–ą½‘ą½“ą½“ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½–ą½¢ą¾’ą¾±ą½‘ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½‘ą½‚ą½“ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½–ą½…ą½“ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½–ą½…ą½“ą¼‹ą½‚ą½…ą½²ą½‚ą¼‹ą½”_ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½–ą½…ą½“ą¼‹ą½‚ą½‰ą½²ą½¦ą¼‹ą½”".split("_"),monthsShort:"ą½Ÿą¾³ą¼‹1_ą½Ÿą¾³ą¼‹2_ą½Ÿą¾³ą¼‹3_ą½Ÿą¾³ą¼‹4_ą½Ÿą¾³ą¼‹5_ą½Ÿą¾³ą¼‹6_ą½Ÿą¾³ą¼‹7_ą½Ÿą¾³ą¼‹8_ą½Ÿą¾³ą¼‹9_ą½Ÿą¾³ą¼‹10_ą½Ÿą¾³ą¼‹11_ą½Ÿą¾³ą¼‹12".split("_"),monthsShortRegex:/^(ą½Ÿą¾³ą¼‹\d{1,2})/,monthsParseExact:true,weekdays:"ą½‚ą½Ÿą½ ą¼‹ą½‰ą½²ą¼‹ą½˜ą¼‹_ą½‚ą½Ÿą½ ą¼‹ą½Ÿą¾³ą¼‹ą½–ą¼‹_ą½‚ą½Ÿą½ ą¼‹ą½˜ą½²ą½‚ą¼‹ą½‘ą½˜ą½¢ą¼‹_ą½‚ą½Ÿą½ ą¼‹ą½£ą¾·ą½‚ą¼‹ą½”ą¼‹_ą½‚ą½Ÿą½ ą¼‹ą½•ą½“ą½¢ą¼‹ą½–ą½“_ą½‚ą½Ÿą½ ą¼‹ą½”ą¼‹ą½¦ą½„ą½¦ą¼‹_ą½‚ą½Ÿą½ ą¼‹ą½¦ą¾¤ą½ŗą½“ą¼‹ą½”ą¼‹".split("_"),weekdaysShort:"ą½‰ą½²ą¼‹ą½˜ą¼‹_ą½Ÿą¾³ą¼‹ą½–ą¼‹_ą½˜ą½²ą½‚ą¼‹ą½‘ą½˜ą½¢ą¼‹_ą½£ą¾·ą½‚ą¼‹ą½”ą¼‹_ą½•ą½“ą½¢ą¼‹ą½–ą½“_ą½”ą¼‹ą½¦ą½„ą½¦ą¼‹_ą½¦ą¾¤ą½ŗą½“ą¼‹ą½”ą¼‹".split("_"),weekdaysMin:"ą½‰ą½²_ą½Ÿą¾³_ą½˜ą½²ą½‚_ą½£ą¾·ą½‚_ą½•ą½“ą½¢_ą½¦ą½„ą½¦_ą½¦ą¾¤ą½ŗą½“".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[ą½‘ą½²ą¼‹ą½¢ą½²ą½„] LT",nextDay:"[ą½¦ą½„ą¼‹ą½‰ą½²ą½“] LT",nextWeek:"[ą½–ą½‘ą½“ą½“ą¼‹ą½•ą¾²ą½‚ą¼‹ą½¢ą¾—ą½ŗą½¦ą¼‹ą½˜], LT",lastDay:"[ą½ą¼‹ą½¦ą½„] LT",lastWeek:"[ą½–ą½‘ą½“ą½“ą¼‹ą½•ą¾²ą½‚ą¼‹ą½˜ą½ą½ ą¼‹ą½˜] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą½£ą¼‹",past:"%s ą½¦ą¾”ą½“ą¼‹ą½£",s:"ą½£ą½˜ą¼‹ą½¦ą½„",ss:"%d ą½¦ą¾ą½¢ą¼‹ą½†ą¼",m:"ą½¦ą¾ą½¢ą¼‹ą½˜ą¼‹ą½‚ą½…ą½²ą½‚",mm:"%d ą½¦ą¾ą½¢ą¼‹ą½˜",h:"ą½†ą½“ą¼‹ą½šą½¼ą½‘ą¼‹ą½‚ą½…ą½²ą½‚",hh:"%d ą½†ą½“ą¼‹ą½šą½¼ą½‘",d:"ą½‰ą½²ą½“ą¼‹ą½‚ą½…ą½²ą½‚",dd:"%d ą½‰ą½²ą½“ą¼‹",M:"ą½Ÿą¾³ą¼‹ą½–ą¼‹ą½‚ą½…ą½²ą½‚",MM:"%d ą½Ÿą¾³ą¼‹ą½–",y:"ą½£ą½¼ą¼‹ą½‚ą½…ą½²ą½‚",yy:"%d ą½£ą½¼"},preparse:function(e){return e.replace(/[ą¼”ą¼¢ą¼£ą¼¤ą¼„ą¼¦ą¼§ą¼Øą¼©ą¼ ]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą½˜ą½šą½“ą¼‹ą½˜ą½¼|ą½žą½¼ą½‚ą½¦ą¼‹ą½€ą½¦|ą½‰ą½²ą½“ą¼‹ą½‚ą½“ą½„|ą½‘ą½‚ą½¼ą½„ą¼‹ą½‘ą½‚|ą½˜ą½šą½“ą¼‹ą½˜ą½¼/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą½˜ą½šą½“ą¼‹ą½˜ą½¼"&&e>=4||t==="ą½‰ą½²ą½“ą¼‹ą½‚ą½“ą½„"&&e<5||t==="ą½‘ą½‚ą½¼ą½„ą¼‹ą½‘ą½‚")return e+12;else return e},meridiem:function(e,t,n){if(e<4)return"ą½˜ą½šą½“ą¼‹ą½˜ą½¼";else if(e<10)return"ą½žą½¼ą½‚ą½¦ą¼‹ą½€ą½¦";else if(e<17)return"ą½‰ą½²ą½“ą¼‹ą½‚ą½“ą½„";else if(e<20)return"ą½‘ą½‚ą½¼ą½„ą¼‹ą½‘ą½‚";else return"ą½˜ą½šą½“ą¼‹ą½˜ą½¼"},week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function t(e,t,n){var a={mm:"munutenn",MM:"miz",dd:"devezh"};return e+" "+r(a[n],e)}function n(e){switch(a(e)){case 1:case 3:case 4:case 5:case 9:return e+" bloaz";default:return e+" vloaz"}}function a(e){if(e>9)return a(e%10);return e}function r(e,t){if(t===2)return o(e);return e}function o(e){var t={m:"v",b:"v",d:"z"};if(t[e.charAt(0)]===undefined)return e;return t[e.charAt(0)]+e.substring(1)}var i=[/^gen/i,/^c[Ź¼\']hwe/i,/^meu/i,/^ebr/i,/^mae/i,/^(mez|eve)/i,/^gou/i,/^eos/i,/^gwe/i,/^her/i,/^du/i,/^ker/i],l=/^(genver|c[Ź¼\']hwevrer|meurzh|ebrel|mae|mezheven|gouere|eost|gwengolo|here|du|kerzu|gen|c[Ź¼\']hwe|meu|ebr|mae|eve|gou|eos|gwe|her|du|ker)/i,s=/^(genver|c[Ź¼\']hwevrer|meurzh|ebrel|mae|mezheven|gouere|eost|gwengolo|here|du|kerzu)/i,u=/^(gen|c[Ź¼\']hwe|meu|ebr|mae|eve|gou|eos|gwe|her|du|ker)/i,d=[/^sul/i,/^lun/i,/^meurzh/i,/^merc[Ź¼\']her/i,/^yaou/i,/^gwener/i,/^sadorn/i],c=[/^Sul/i,/^Lun/i,/^Meu/i,/^Mer/i,/^Yao/i,/^Gwe/i,/^Sad/i],f=[/^Su/i,/^Lu/i,/^Me([^r]|$)/i,/^Mer/i,/^Ya/i,/^Gw/i,/^Sa/i],p;e.defineLocale("br",{months:"Genver_CŹ¼hwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu".split("_"),monthsShort:"Gen_CŹ¼hwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker".split("_"),weekdays:"Sul_Lun_Meurzh_MercŹ¼her_Yaou_Gwener_Sadorn".split("_"),weekdaysShort:"Sul_Lun_Meu_Mer_Yao_Gwe_Sad".split("_"),weekdaysMin:"Su_Lu_Me_Mer_Ya_Gw_Sa".split("_"),weekdaysParse:f,fullWeekdaysParse:d,shortWeekdaysParse:c,minWeekdaysParse:f,monthsRegex:l,monthsShortRegex:l,monthsStrictRegex:s,monthsShortStrictRegex:u,monthsParse:i,longMonthsParse:i,shortMonthsParse:i,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [a viz] MMMM YYYY",LLL:"D [a viz] MMMM YYYY HH:mm",LLLL:"dddd, D [a viz] MMMM YYYY HH:mm"},calendar:{sameDay:"[Hiziv da] LT",nextDay:"[WarcŹ¼hoazh da] LT",nextWeek:"dddd [da] LT",lastDay:"[DecŹ¼h da] LT",lastWeek:"dddd [paset da] LT",sameElse:"L"},relativeTime:{future:"a-benn %s",past:"%s Ź¼zo",s:"un nebeud segondennoĆ¹",ss:"%d eilenn",m:"ur vunutenn",mm:t,h:"un eur",hh:"%d eur",d:"un devezh",dd:t,M:"ur miz",MM:t,y:"ur bloaz",yy:n},dayOfMonthOrdinalParse:/\d{1,2}(aƱ|vet)/,ordinal:function(e){var t=e===1?"aƱ":"vet";return e+t},week:{dow:1,doy:4},meridiemParse:/a.m.|g.m./,isPM:function(e){return e==="g.m."},meridiem:function(e,t,n){return e<12?"a.m.":"g.m."}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function t(e,t,n){var a={mm:"munutenn",MM:"miz",dd:"devezh"};return e+" "+r(a[n],e)}function n(e){switch(a(e)){case 1:case 3:case 4:case 5:case 9:return e+" bloaz";default:return e+" vloaz"}}function a(e){if(e>9)return a(e%10);return e}function r(e,t){if(t===2)return o(e);return e}function o(e){var t={m:"v",b:"v",d:"z"};if(t[e.charAt(0)]===undefined)return e;return t[e.charAt(0)]+e.substring(1)}var i=[/^gen/i,/^c[Ź¼\']hwe/i,/^meu/i,/^ebr/i,/^mae/i,/^(mez|eve)/i,/^gou/i,/^eos/i,/^gwe/i,/^her/i,/^du/i,/^ker/i],l=/^(genver|c[Ź¼\']hwevrer|meurzh|ebrel|mae|mezheven|gouere|eost|gwengolo|here|du|kerzu|gen|c[Ź¼\']hwe|meu|ebr|mae|eve|gou|eos|gwe|her|du|ker)/i,s=/^(genver|c[Ź¼\']hwevrer|meurzh|ebrel|mae|mezheven|gouere|eost|gwengolo|here|du|kerzu)/i,u=/^(gen|c[Ź¼\']hwe|meu|ebr|mae|eve|gou|eos|gwe|her|du|ker)/i,d=[/^sul/i,/^lun/i,/^meurzh/i,/^merc[Ź¼\']her/i,/^yaou/i,/^gwener/i,/^sadorn/i],c=[/^Sul/i,/^Lun/i,/^Meu/i,/^Mer/i,/^Yao/i,/^Gwe/i,/^Sad/i],f=[/^Su/i,/^Lu/i,/^Me([^r]|$)/i,/^Mer/i,/^Ya/i,/^Gw/i,/^Sa/i],p;e.defineLocale("br",{months:"Genver_CŹ¼hwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu".split("_"),monthsShort:"Gen_CŹ¼hwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker".split("_"),weekdays:"Sul_Lun_Meurzh_MercŹ¼her_Yaou_Gwener_Sadorn".split("_"),weekdaysShort:"Sul_Lun_Meu_Mer_Yao_Gwe_Sad".split("_"),weekdaysMin:"Su_Lu_Me_Mer_Ya_Gw_Sa".split("_"),weekdaysParse:f,fullWeekdaysParse:d,shortWeekdaysParse:c,minWeekdaysParse:f,monthsRegex:l,monthsShortRegex:l,monthsStrictRegex:s,monthsShortStrictRegex:u,monthsParse:i,longMonthsParse:i,shortMonthsParse:i,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [a viz] MMMM YYYY",LLL:"D [a viz] MMMM YYYY HH:mm",LLLL:"dddd, D [a viz] MMMM YYYY HH:mm"},calendar:{sameDay:"[Hiziv da] LT",nextDay:"[WarcŹ¼hoazh da] LT",nextWeek:"dddd [da] LT",lastDay:"[DecŹ¼h da] LT",lastWeek:"dddd [paset da] LT",sameElse:"L"},relativeTime:{future:"a-benn %s",past:"%s Ź¼zo",s:"un nebeud segondennoĆ¹",ss:"%d eilenn",m:"ur vunutenn",mm:t,h:"un eur",hh:"%d eur",d:"un devezh",dd:t,M:"ur miz",MM:t,y:"ur bloaz",yy:n},dayOfMonthOrdinalParse:/\d{1,2}(aƱ|vet)/,ordinal:function(e){var t=e===1?"aƱ":"vet";return e+t},week:{dow:1,doy:4},meridiemParse:/a.m.|g.m./,isPM:function(e){return e==="g.m."},meridiem:function(e,t,n){return e<12?"a.m.":"g.m."}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function t(e,t,n){var a=e+" ";switch(n){case"ss":if(e===1)a+="sekunda";else if(e===2||e===3||e===4)a+="sekunde";else a+="sekundi";return a;case"m":return t?"jedna minuta":"jedne minute";case"mm":if(e===1)a+="minuta";else if(e===2||e===3||e===4)a+="minute";else a+="minuta";return a;case"h":return t?"jedan sat":"jednog sata";case"hh":if(e===1)a+="sat";else if(e===2||e===3||e===4)a+="sata";else a+="sati";return a;case"dd":if(e===1)a+="dan";else a+="dana";return a;case"MM":if(e===1)a+="mjesec";else if(e===2||e===3||e===4)a+="mjeseca";else a+="mjeseci";return a;case"yy":if(e===1)a+="godina";else if(e===2||e===3||e===4)a+="godine";else a+="godina";return a}}var n;e.defineLocale("bs",{months:"januar_februar_mart_april_maj_juni_juli_august_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._aug._sep._okt._nov._dec.".split("_"),monthsParseExact:true,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:case 3:return"[proÅ”lu] dddd [u] LT";case 6:return"[proÅ”le] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[proÅ”li] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",ss:t,m:t,mm:t,h:t,hh:t,d:"dan",dd:t,M:"mjesec",MM:t,y:"godinu",yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function t(e,t,n){var a=e+" ";switch(n){case"ss":if(e===1)a+="sekunda";else if(e===2||e===3||e===4)a+="sekunde";else a+="sekundi";return a;case"m":return t?"jedna minuta":"jedne minute";case"mm":if(e===1)a+="minuta";else if(e===2||e===3||e===4)a+="minute";else a+="minuta";return a;case"h":return t?"jedan sat":"jednog sata";case"hh":if(e===1)a+="sat";else if(e===2||e===3||e===4)a+="sata";else a+="sati";return a;case"dd":if(e===1)a+="dan";else a+="dana";return a;case"MM":if(e===1)a+="mjesec";else if(e===2||e===3||e===4)a+="mjeseca";else a+="mjeseci";return a;case"yy":if(e===1)a+="godina";else if(e===2||e===3||e===4)a+="godine";else a+="godina";return a}}var n;e.defineLocale("bs",{months:"januar_februar_mart_april_maj_juni_juli_august_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._aug._sep._okt._nov._dec.".split("_"),monthsParseExact:true,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:case 3:return"[proÅ”lu] dddd [u] LT";case 6:return"[proÅ”le] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[proÅ”li] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",ss:t,m:t,mm:t,h:t,hh:t,d:"dan",dd:t,M:"mjesec",MM:t,y:"godinu",yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("ca",{months:{standalone:"gener_febrer_marƧ_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre".split("_"),format:"de gener_de febrer_de marƧ_d'abril_de maig_de juny_de juliol_d'agost_de setembre_d'octubre_de novembre_de desembre".split("_"),isFormat:/D[oD]?(\s)+MMMM/},monthsShort:"gen._febr._marƧ_abr._maig_juny_jul._ag._set._oct._nov._des.".split("_"),monthsParseExact:true,weekdays:"diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte".split("_"),weekdaysShort:"dg._dl._dt._dc._dj._dv._ds.".split("_"),weekdaysMin:"dg_dl_dt_dc_dj_dv_ds".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [de] YYYY",ll:"D MMM YYYY",LLL:"D MMMM [de] YYYY [a les] H:mm",lll:"D MMM YYYY, H:mm",LLLL:"dddd D MMMM [de] YYYY [a les] H:mm",llll:"ddd D MMM YYYY, H:mm"},calendar:{sameDay:function(){return"[avui a "+(this.hours()!==1?"les":"la")+"] LT"},nextDay:function(){return"[demĆ  a "+(this.hours()!==1?"les":"la")+"] LT"},nextWeek:function(){return"dddd [a "+(this.hours()!==1?"les":"la")+"] LT"},lastDay:function(){return"[ahir a "+(this.hours()!==1?"les":"la")+"] LT"},lastWeek:function(){return"[el] dddd [passat a "+(this.hours()!==1?"les":"la")+"] LT"},sameElse:"L"},relativeTime:{future:"d'aquĆ­ %s",past:"fa %s",s:"uns segons",ss:"%d segons",m:"un minut",mm:"%d minuts",h:"una hora",hh:"%d hores",d:"un dia",dd:"%d dies",M:"un mes",MM:"%d mesos",y:"un any",yy:"%d anys"},dayOfMonthOrdinalParse:/\d{1,2}(r|n|t|ĆØ|a)/,ordinal:function(e,t){var n=e===1?"r":e===2?"n":e===3?"r":e===4?"t":"ĆØ";if(t==="w"||t==="W")n="a";return e+n},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("ca",{months:{standalone:"gener_febrer_marƧ_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre".split("_"),format:"de gener_de febrer_de marƧ_d'abril_de maig_de juny_de juliol_d'agost_de setembre_d'octubre_de novembre_de desembre".split("_"),isFormat:/D[oD]?(\s)+MMMM/},monthsShort:"gen._febr._marƧ_abr._maig_juny_jul._ag._set._oct._nov._des.".split("_"),monthsParseExact:true,weekdays:"diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte".split("_"),weekdaysShort:"dg._dl._dt._dc._dj._dv._ds.".split("_"),weekdaysMin:"dg_dl_dt_dc_dj_dv_ds".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [de] YYYY",ll:"D MMM YYYY",LLL:"D MMMM [de] YYYY [a les] H:mm",lll:"D MMM YYYY, H:mm",LLLL:"dddd D MMMM [de] YYYY [a les] H:mm",llll:"ddd D MMM YYYY, H:mm"},calendar:{sameDay:function(){return"[avui a "+(this.hours()!==1?"les":"la")+"] LT"},nextDay:function(){return"[demĆ  a "+(this.hours()!==1?"les":"la")+"] LT"},nextWeek:function(){return"dddd [a "+(this.hours()!==1?"les":"la")+"] LT"},lastDay:function(){return"[ahir a "+(this.hours()!==1?"les":"la")+"] LT"},lastWeek:function(){return"[el] dddd [passat a "+(this.hours()!==1?"les":"la")+"] LT"},sameElse:"L"},relativeTime:{future:"d'aquĆ­ %s",past:"fa %s",s:"uns segons",ss:"%d segons",m:"un minut",mm:"%d minuts",h:"una hora",hh:"%d hores",d:"un dia",dd:"%d dies",M:"un mes",MM:"%d mesos",y:"un any",yy:"%d anys"},dayOfMonthOrdinalParse:/\d{1,2}(r|n|t|ĆØ|a)/,ordinal:function(e,t){var n=e===1?"r":e===2?"n":e===3?"r":e===4?"t":"ĆØ";if(t==="w"||t==="W")n="a";return e+n},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={format:"leden_Ćŗnor_březen_duben_květen_červen_červenec_srpen_zĆ”Å™Ć­_Å™Ć­jen_listopad_prosinec".split("_"),standalone:"ledna_Ćŗnora_března_dubna_května_června_července_srpna_zĆ”Å™Ć­_Å™Ć­jna_listopadu_prosince".split("_")},n="led_Ćŗno_bře_dub_kvě_čvn_čvc_srp_zĆ”Å™_Å™Ć­j_lis_pro".split("_"),a=[/^led/i,/^Ćŗno/i,/^bře/i,/^dub/i,/^kvě/i,/^(čvn|červen$|června)/i,/^(čvc|červenec|července)/i,/^srp/i,/^zĆ”Å™/i,/^Å™Ć­j/i,/^lis/i,/^pro/i],r=/^(leden|Ćŗnor|březen|duben|květen|červenec|července|červen|června|srpen|zĆ”Å™Ć­|Å™Ć­jen|listopad|prosinec|led|Ćŗno|bře|dub|kvě|čvn|čvc|srp|zĆ”Å™|Å™Ć­j|lis|pro)/i,o;function i(e){return e>1&&e<5&&~~(e/10)!==1}function l(e,t,n,a){var r=e+" ";switch(n){case"s":return t||a?"pĆ”r sekund":"pĆ”r sekundami";case"ss":if(t||a)return r+(i(e)?"sekundy":"sekund");else return r+"sekundami";case"m":return t?"minuta":a?"minutu":"minutou";case"mm":if(t||a)return r+(i(e)?"minuty":"minut");else return r+"minutami";case"h":return t?"hodina":a?"hodinu":"hodinou";case"hh":if(t||a)return r+(i(e)?"hodiny":"hodin");else return r+"hodinami";case"d":return t||a?"den":"dnem";case"dd":if(t||a)return r+(i(e)?"dny":"dnĆ­");else return r+"dny";case"M":return t||a?"měsĆ­c":"měsĆ­cem";case"MM":if(t||a)return r+(i(e)?"měsĆ­ce":"měsĆ­cÅÆ");else return r+"měsĆ­ci";case"y":return t||a?"rok":"rokem";case"yy":if(t||a)return r+(i(e)?"roky":"let");else return r+"lety"}}e.defineLocale("cs",{months:t,monthsShort:n,monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(leden|ledna|Ćŗnora|Ćŗnor|březen|března|duben|dubna|květen|května|červenec|července|červen|června|srpen|srpna|zĆ”Å™Ć­|Å™Ć­jen|Å™Ć­jna|listopadu|listopad|prosinec|prosince)/i,monthsShortStrictRegex:/^(led|Ćŗno|bře|dub|kvě|čvn|čvc|srp|zĆ”Å™|Å™Ć­j|lis|pro)/i,monthsParse:a,longMonthsParse:a,shortMonthsParse:a,weekdays:"neděle_pondělĆ­_ĆŗterĆ½_středa_čtvrtek_pĆ”tek_sobota".split("_"),weekdaysShort:"ne_po_Ćŗt_st_čt_pĆ”_so".split("_"),weekdaysMin:"ne_po_Ćŗt_st_čt_pĆ”_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd D. MMMM YYYY H:mm",l:"D. M. YYYY"},calendar:{sameDay:"[dnes v] LT",nextDay:"[zĆ­tra v] LT",nextWeek:function(){switch(this.day()){case 0:return"[v neděli v] LT";case 1:case 2:return"[v] dddd [v] LT";case 3:return"[ve středu v] LT";case 4:return"[ve čtvrtek v] LT";case 5:return"[v pĆ”tek v] LT";case 6:return"[v sobotu v] LT"}},lastDay:"[včera v] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulou neděli v] LT";case 1:case 2:return"[minulĆ©] dddd [v] LT";case 3:return"[minulou středu v] LT";case 4:case 5:return"[minulĆ½] dddd [v] LT";case 6:return"[minulou sobotu v] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"před %s",s:l,ss:l,m:l,mm:l,h:l,hh:l,d:l,dd:l,M:l,MM:l,y:l,yy:l},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={format:"leden_Ćŗnor_březen_duben_květen_červen_červenec_srpen_zĆ”Å™Ć­_Å™Ć­jen_listopad_prosinec".split("_"),standalone:"ledna_Ćŗnora_března_dubna_května_června_července_srpna_zĆ”Å™Ć­_Å™Ć­jna_listopadu_prosince".split("_")},n="led_Ćŗno_bře_dub_kvě_čvn_čvc_srp_zĆ”Å™_Å™Ć­j_lis_pro".split("_"),a=[/^led/i,/^Ćŗno/i,/^bře/i,/^dub/i,/^kvě/i,/^(čvn|červen$|června)/i,/^(čvc|červenec|července)/i,/^srp/i,/^zĆ”Å™/i,/^Å™Ć­j/i,/^lis/i,/^pro/i],r=/^(leden|Ćŗnor|březen|duben|květen|červenec|července|červen|června|srpen|zĆ”Å™Ć­|Å™Ć­jen|listopad|prosinec|led|Ćŗno|bře|dub|kvě|čvn|čvc|srp|zĆ”Å™|Å™Ć­j|lis|pro)/i,o;function i(e){return e>1&&e<5&&~~(e/10)!==1}function l(e,t,n,a){var r=e+" ";switch(n){case"s":return t||a?"pĆ”r sekund":"pĆ”r sekundami";case"ss":if(t||a)return r+(i(e)?"sekundy":"sekund");else return r+"sekundami";case"m":return t?"minuta":a?"minutu":"minutou";case"mm":if(t||a)return r+(i(e)?"minuty":"minut");else return r+"minutami";case"h":return t?"hodina":a?"hodinu":"hodinou";case"hh":if(t||a)return r+(i(e)?"hodiny":"hodin");else return r+"hodinami";case"d":return t||a?"den":"dnem";case"dd":if(t||a)return r+(i(e)?"dny":"dnĆ­");else return r+"dny";case"M":return t||a?"měsĆ­c":"měsĆ­cem";case"MM":if(t||a)return r+(i(e)?"měsĆ­ce":"měsĆ­cÅÆ");else return r+"měsĆ­ci";case"y":return t||a?"rok":"rokem";case"yy":if(t||a)return r+(i(e)?"roky":"let");else return r+"lety"}}e.defineLocale("cs",{months:t,monthsShort:n,monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(leden|ledna|Ćŗnora|Ćŗnor|březen|března|duben|dubna|květen|května|červenec|července|červen|června|srpen|srpna|zĆ”Å™Ć­|Å™Ć­jen|Å™Ć­jna|listopadu|listopad|prosinec|prosince)/i,monthsShortStrictRegex:/^(led|Ćŗno|bře|dub|kvě|čvn|čvc|srp|zĆ”Å™|Å™Ć­j|lis|pro)/i,monthsParse:a,longMonthsParse:a,shortMonthsParse:a,weekdays:"neděle_pondělĆ­_ĆŗterĆ½_středa_čtvrtek_pĆ”tek_sobota".split("_"),weekdaysShort:"ne_po_Ćŗt_st_čt_pĆ”_so".split("_"),weekdaysMin:"ne_po_Ćŗt_st_čt_pĆ”_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd D. MMMM YYYY H:mm",l:"D. M. YYYY"},calendar:{sameDay:"[dnes v] LT",nextDay:"[zĆ­tra v] LT",nextWeek:function(){switch(this.day()){case 0:return"[v neděli v] LT";case 1:case 2:return"[v] dddd [v] LT";case 3:return"[ve středu v] LT";case 4:return"[ve čtvrtek v] LT";case 5:return"[v pĆ”tek v] LT";case 6:return"[v sobotu v] LT"}},lastDay:"[včera v] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulou neděli v] LT";case 1:case 2:return"[minulĆ©] dddd [v] LT";case 3:return"[minulou středu v] LT";case 4:case 5:return"[minulĆ½] dddd [v] LT";case 6:return"[minulou sobotu v] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"před %s",s:l,ss:l,m:l,mm:l,h:l,hh:l,d:l,dd:l,M:l,MM:l,y:l,yy:l},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("cv",{months:"ŠŗӑрŠ»Š°Ń‡_Š½Š°Ń€Ó‘с_Šæуш_Š°ŠŗŠ°_Š¼Š°Š¹_Ņ«Ó—ртŠ¼Šµ_утӑ_Ņ«ŃƒŃ€Š»Š°_Š°Š²Ó‘Š½_юŠæŠ°_Ń‡Ó³Šŗ_рŠ°ŃˆŃ‚Š°Š²".split("_"),monthsShort:"Šŗӑр_Š½Š°Ń€_Šæуш_Š°ŠŗŠ°_Š¼Š°Š¹_Ņ«Ó—Ń€_утӑ_Ņ«ŃƒŃ€_Š°Š²Š½_юŠæŠ°_Ń‡Ó³Šŗ_рŠ°Ńˆ".split("_"),weekdays:"Š²Ń‹Ń€ŃŠ°Ń€Š½ŠøŠŗуŠ½_туŠ½Ń‚ŠøŠŗуŠ½_ытŠ»Š°Ń€ŠøŠŗуŠ½_юŠ½ŠŗуŠ½_ŠŗӗŅ«Š½ŠµŃ€Š½ŠøŠŗуŠ½_эрŠ½ŠµŠŗуŠ½_шӑŠ¼Š°Ń‚ŠŗуŠ½".split("_"),weekdaysShort:"Š²Ń‹Ń€_туŠ½_ытŠ»_юŠ½_ŠŗӗŅ«_эрŠ½_шӑŠ¼".split("_"),weekdaysMin:"Š²Ń€_тŠ½_ыт_юŠ½_ŠŗŅ«_эр_шŠ¼".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"YYYY [Ņ«ŃƒŠ»Ń…Šø] MMMM [уŠ¹Ó‘Ń…Ó—Š½] D[-Š¼Ó—ŃˆÓ—]",LLL:"YYYY [Ņ«ŃƒŠ»Ń…Šø] MMMM [уŠ¹Ó‘Ń…Ó—Š½] D[-Š¼Ó—ŃˆÓ—], HH:mm",LLLL:"dddd, YYYY [Ņ«ŃƒŠ»Ń…Šø] MMMM [уŠ¹Ó‘Ń…Ó—Š½] D[-Š¼Ó—ŃˆÓ—], HH:mm"},calendar:{sameDay:"[ŠŸŠ°ŃŠ½] LT [сŠµŃ…ŠµŃ‚Ń€Šµ]",nextDay:"[Š«Ń€Š°Š½] LT [сŠµŃ…ŠµŃ‚Ń€Šµ]",lastDay:"[ӖŠ½ŠµŃ€] LT [сŠµŃ…ŠµŃ‚Ń€Šµ]",nextWeek:"[ŅŖŠøтŠµŃ] dddd LT [сŠµŃ…ŠµŃ‚Ń€Šµ]",lastWeek:"[Š˜Ń€Ń‚Š½Ó—] dddd LT [сŠµŃ…ŠµŃ‚Ń€Šµ]",sameElse:"L"},relativeTime:{future:function(e){var t=/сŠµŃ…ŠµŃ‚$/i.exec(e)?"рŠµŠ½":/Ņ«ŃƒŠ»$/i.exec(e)?"тŠ°Š½":"рŠ°Š½";return e+t},past:"%s ŠŗŠ°ŃŠ»Š»Š°",s:"Šæӗр-ŠøŠŗ Ņ«ŠµŠŗŠŗуŠ½Ń‚",ss:"%d Ņ«ŠµŠŗŠŗуŠ½Ń‚",m:"Šæӗр Š¼ŠøŠ½ŃƒŃ‚",mm:"%d Š¼ŠøŠ½ŃƒŃ‚",h:"Šæӗр сŠµŃ…ŠµŃ‚",hh:"%d сŠµŃ…ŠµŃ‚",d:"Šæӗр ŠŗуŠ½",dd:"%d ŠŗуŠ½",M:"Šæӗр уŠ¹Ó‘Ń…",MM:"%d уŠ¹Ó‘Ń…",y:"Šæӗр Ņ«ŃƒŠ»",yy:"%d Ņ«ŃƒŠ»"},dayOfMonthOrdinalParse:/\d{1,2}-Š¼Ó—Ńˆ/,ordinal:"%d-Š¼Ó—Ńˆ",week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("cv",{months:"ŠŗӑрŠ»Š°Ń‡_Š½Š°Ń€Ó‘с_Šæуш_Š°ŠŗŠ°_Š¼Š°Š¹_Ņ«Ó—ртŠ¼Šµ_утӑ_Ņ«ŃƒŃ€Š»Š°_Š°Š²Ó‘Š½_юŠæŠ°_Ń‡Ó³Šŗ_рŠ°ŃˆŃ‚Š°Š²".split("_"),monthsShort:"Šŗӑр_Š½Š°Ń€_Šæуш_Š°ŠŗŠ°_Š¼Š°Š¹_Ņ«Ó—Ń€_утӑ_Ņ«ŃƒŃ€_Š°Š²Š½_юŠæŠ°_Ń‡Ó³Šŗ_рŠ°Ńˆ".split("_"),weekdays:"Š²Ń‹Ń€ŃŠ°Ń€Š½ŠøŠŗуŠ½_туŠ½Ń‚ŠøŠŗуŠ½_ытŠ»Š°Ń€ŠøŠŗуŠ½_юŠ½ŠŗуŠ½_ŠŗӗŅ«Š½ŠµŃ€Š½ŠøŠŗуŠ½_эрŠ½ŠµŠŗуŠ½_шӑŠ¼Š°Ń‚ŠŗуŠ½".split("_"),weekdaysShort:"Š²Ń‹Ń€_туŠ½_ытŠ»_юŠ½_ŠŗӗŅ«_эрŠ½_шӑŠ¼".split("_"),weekdaysMin:"Š²Ń€_тŠ½_ыт_юŠ½_ŠŗŅ«_эр_шŠ¼".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"YYYY [Ņ«ŃƒŠ»Ń…Šø] MMMM [уŠ¹Ó‘Ń…Ó—Š½] D[-Š¼Ó—ŃˆÓ—]",LLL:"YYYY [Ņ«ŃƒŠ»Ń…Šø] MMMM [уŠ¹Ó‘Ń…Ó—Š½] D[-Š¼Ó—ŃˆÓ—], HH:mm",LLLL:"dddd, YYYY [Ņ«ŃƒŠ»Ń…Šø] MMMM [уŠ¹Ó‘Ń…Ó—Š½] D[-Š¼Ó—ŃˆÓ—], HH:mm"},calendar:{sameDay:"[ŠŸŠ°ŃŠ½] LT [сŠµŃ…ŠµŃ‚Ń€Šµ]",nextDay:"[Š«Ń€Š°Š½] LT [сŠµŃ…ŠµŃ‚Ń€Šµ]",lastDay:"[ӖŠ½ŠµŃ€] LT [сŠµŃ…ŠµŃ‚Ń€Šµ]",nextWeek:"[ŅŖŠøтŠµŃ] dddd LT [сŠµŃ…ŠµŃ‚Ń€Šµ]",lastWeek:"[Š˜Ń€Ń‚Š½Ó—] dddd LT [сŠµŃ…ŠµŃ‚Ń€Šµ]",sameElse:"L"},relativeTime:{future:function(e){var t=/сŠµŃ…ŠµŃ‚$/i.exec(e)?"рŠµŠ½":/Ņ«ŃƒŠ»$/i.exec(e)?"тŠ°Š½":"рŠ°Š½";return e+t},past:"%s ŠŗŠ°ŃŠ»Š»Š°",s:"Šæӗр-ŠøŠŗ Ņ«ŠµŠŗŠŗуŠ½Ń‚",ss:"%d Ņ«ŠµŠŗŠŗуŠ½Ń‚",m:"Šæӗр Š¼ŠøŠ½ŃƒŃ‚",mm:"%d Š¼ŠøŠ½ŃƒŃ‚",h:"Šæӗр сŠµŃ…ŠµŃ‚",hh:"%d сŠµŃ…ŠµŃ‚",d:"Šæӗр ŠŗуŠ½",dd:"%d ŠŗуŠ½",M:"Šæӗр уŠ¹Ó‘Ń…",MM:"%d уŠ¹Ó‘Ń…",y:"Šæӗр Ņ«ŃƒŠ»",yy:"%d Ņ«ŃƒŠ»"},dayOfMonthOrdinalParse:/\d{1,2}-Š¼Ó—Ńˆ/,ordinal:"%d-Š¼Ó—Ńˆ",week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("cy",{months:"Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr".split("_"),monthsShort:"Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag".split("_"),weekdays:"Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn".split("_"),weekdaysShort:"Sul_Llun_Maw_Mer_Iau_Gwe_Sad".split("_"),weekdaysMin:"Su_Ll_Ma_Me_Ia_Gw_Sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Heddiw am] LT",nextDay:"[Yfory am] LT",nextWeek:"dddd [am] LT",lastDay:"[Ddoe am] LT",lastWeek:"dddd [diwethaf am] LT",sameElse:"L"},relativeTime:{future:"mewn %s",past:"%s yn Ć“l",s:"ychydig eiliadau",ss:"%d eiliad",m:"munud",mm:"%d munud",h:"awr",hh:"%d awr",d:"diwrnod",dd:"%d diwrnod",M:"mis",MM:"%d mis",y:"blwyddyn",yy:"%d flynedd"},dayOfMonthOrdinalParse:/\d{1,2}(fed|ain|af|il|ydd|ed|eg)/,ordinal:function(e){var t=e,n="",a=["","af","il","ydd","ydd","ed","ed","ed","fed","fed","fed","eg","fed","eg","eg","fed","eg","eg","fed","eg","fed"];if(t>20)if(t===40||t===50||t===60||t===80||t===100)n="fed";else n="ain";else if(t>0)n=a[t];return e+n},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("cy",{months:"Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr".split("_"),monthsShort:"Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag".split("_"),weekdays:"Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn".split("_"),weekdaysShort:"Sul_Llun_Maw_Mer_Iau_Gwe_Sad".split("_"),weekdaysMin:"Su_Ll_Ma_Me_Ia_Gw_Sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Heddiw am] LT",nextDay:"[Yfory am] LT",nextWeek:"dddd [am] LT",lastDay:"[Ddoe am] LT",lastWeek:"dddd [diwethaf am] LT",sameElse:"L"},relativeTime:{future:"mewn %s",past:"%s yn Ć“l",s:"ychydig eiliadau",ss:"%d eiliad",m:"munud",mm:"%d munud",h:"awr",hh:"%d awr",d:"diwrnod",dd:"%d diwrnod",M:"mis",MM:"%d mis",y:"blwyddyn",yy:"%d flynedd"},dayOfMonthOrdinalParse:/\d{1,2}(fed|ain|af|il|ydd|ed|eg)/,ordinal:function(e){var t=e,n="",a=["","af","il","ydd","ydd","ed","ed","ed","fed","fed","fed","eg","fed","eg","eg","fed","eg","eg","fed","eg","fed"];if(t>20)if(t===40||t===50||t===60||t===80||t===100)n="fed";else n="ain";else if(t>0)n=a[t];return e+n},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("da",{months:"januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"sĆøndag_mandag_tirsdag_onsdag_torsdag_fredag_lĆørdag".split("_"),weekdaysShort:"sĆøn_man_tir_ons_tor_fre_lĆør".split("_"),weekdaysMin:"sĆø_ma_ti_on_to_fr_lĆø".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd [d.] D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"pĆ„ dddd [kl.] LT",lastDay:"[i gĆ„r kl.] LT",lastWeek:"[i] dddd[s kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"fĆ„ sekunder",ss:"%d sekunder",m:"et minut",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dage",M:"en mĆ„ned",MM:"%d mĆ„neder",y:"et Ć„r",yy:"%d Ć„r"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("da",{months:"januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"sĆøndag_mandag_tirsdag_onsdag_torsdag_fredag_lĆørdag".split("_"),weekdaysShort:"sĆøn_man_tir_ons_tor_fre_lĆør".split("_"),weekdaysMin:"sĆø_ma_ti_on_to_fr_lĆø".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd [d.] D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"pĆ„ dddd [kl.] LT",lastDay:"[i gĆ„r kl.] LT",lastWeek:"[i] dddd[s kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"fĆ„ sekunder",ss:"%d sekunder",m:"et minut",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dage",M:"en mĆ„ned",MM:"%d mĆ„neder",y:"et Ć„r",yy:"%d Ć„r"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function t(e,t,n,a){var r={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],w:["eine Woche","einer Woche"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return t?r[n][0]:r[n][1]}var n;e.defineLocale("de",{months:"Januar_Februar_MƤrz_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Feb._MƤrz_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.".split("_"),monthsParseExact:true,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",ss:"%d Sekunden",m:t,mm:"%d Minuten",h:t,hh:"%d Stunden",d:t,dd:t,w:t,ww:"%d Wochen",M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function t(e,t,n,a){var r={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],w:["eine Woche","einer Woche"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return t?r[n][0]:r[n][1]}var n;e.defineLocale("de",{months:"Januar_Februar_MƤrz_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Feb._MƤrz_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.".split("_"),monthsParseExact:true,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",ss:"%d Sekunden",m:t,mm:"%d Minuten",h:t,hh:"%d Stunden",d:t,dd:t,w:t,ww:"%d Wochen",M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function t(e,t,n,a){var r={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],w:["eine Woche","einer Woche"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return t?r[n][0]:r[n][1]}var n;e.defineLocale("de-at",{months:"JƤnner_Februar_MƤrz_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"JƤn._Feb._MƤrz_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.".split("_"),monthsParseExact:true,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",ss:"%d Sekunden",m:t,mm:"%d Minuten",h:t,hh:"%d Stunden",d:t,dd:t,w:t,ww:"%d Wochen",M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function t(e,t,n,a){var r={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],w:["eine Woche","einer Woche"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return t?r[n][0]:r[n][1]}var n;e.defineLocale("de-at",{months:"JƤnner_Februar_MƤrz_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"JƤn._Feb._MƤrz_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.".split("_"),monthsParseExact:true,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",ss:"%d Sekunden",m:t,mm:"%d Minuten",h:t,hh:"%d Stunden",d:t,dd:t,w:t,ww:"%d Wochen",M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function t(e,t,n,a){var r={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],w:["eine Woche","einer Woche"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return t?r[n][0]:r[n][1]}var n;e.defineLocale("de-ch",{months:"Januar_Februar_MƤrz_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Feb._MƤrz_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.".split("_"),monthsParseExact:true,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",ss:"%d Sekunden",m:t,mm:"%d Minuten",h:t,hh:"%d Stunden",d:t,dd:t,w:t,ww:"%d Wochen",M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function t(e,t,n,a){var r={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],w:["eine Woche","einer Woche"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return t?r[n][0]:r[n][1]}var n;e.defineLocale("de-ch",{months:"Januar_Februar_MƤrz_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Feb._MƤrz_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.".split("_"),monthsParseExact:true,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",ss:"%d Sekunden",m:t,mm:"%d Minuten",h:t,hh:"%d Stunden",d:t,dd:t,w:t,ww:"%d Wochen",M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t=["Ž–Ž¬Ž‚ŽŖŽ‡Ž¦ŽƒŽ©","ŽŠŽ¬Ž„Ž°ŽƒŽŖŽ‡Ž¦ŽƒŽ©","Ž‰Ž§ŽƒŽØŽ—ŽŖ","Ž‡Ž­Ž•Ž°ŽƒŽ©ŽŽŖ","Ž‰Ž­","Ž–Ž«Ž‚Ž°","Ž–ŽŖŽŽ¦Ž‡ŽØ","Ž‡ŽÆŽŽŽ¦ŽŽ°Ž“ŽŖ","ŽŽ¬Ž•Ž°Ž“Ž¬Ž‰Ž°Ž„Ž¦ŽƒŽŖ","Ž‡Ž®Ž†Ž°Ž“ŽÆŽ„Ž¦ŽƒŽŖ","Ž‚Ž®ŽˆŽ¬Ž‰Ž°Ž„Ž¦ŽƒŽŖ","Ž‘ŽØŽŽ¬Ž‰Ž°Ž„Ž¦ŽƒŽŖ"],n=["Ž‡Ž§Ž‹ŽØŽ‡Ž°ŽŒŽ¦","Ž€ŽÆŽ‰Ž¦","Ž‡Ž¦Ž‚Ž°ŽŽŽ§ŽƒŽ¦","Ž„ŽŖŽ‹Ž¦","Ž„ŽŖŽƒŽ§ŽŽ°ŽŠŽ¦ŽŒŽØ","Ž€ŽŖŽ†ŽŖŽƒŽŖ","Ž€Ž®Ž‚ŽØŽ€ŽØŽƒŽŖ"],a;e.defineLocale("dv",{months:t,monthsShort:t,weekdays:n,weekdaysShort:n,weekdaysMin:"Ž‡Ž§Ž‹ŽØ_Ž€ŽÆŽ‰Ž¦_Ž‡Ž¦Ž‚Ž°_Ž„ŽŖŽ‹Ž¦_Ž„ŽŖŽƒŽ§_Ž€ŽŖŽ†ŽŖ_Ž€Ž®Ž‚ŽØ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/M/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/Ž‰Ž†|Ž‰ŽŠ/,isPM:function(e){return"Ž‰ŽŠ"===e},meridiem:function(e,t,n){if(e<12)return"Ž‰Ž†";else return"Ž‰ŽŠ"},calendar:{sameDay:"[Ž‰ŽØŽ‡Ž¦Ž‹ŽŖ] LT",nextDay:"[Ž‰Ž§Ž‹Ž¦Ž‰Ž§] LT",nextWeek:"dddd LT",lastDay:"[Ž‡ŽØŽ‡Ž°Ž”Ž¬] LT",lastWeek:"[ŽŠŽ§Ž‡ŽØŽŒŽŖŽˆŽØ] dddd LT",sameElse:"L"},relativeTime:{future:"ŽŒŽ¬ŽƒŽ­ŽŽŽ¦Ž‡ŽØ %s",past:"Ž†ŽŖŽƒŽØŽ‚Ž° %s",s:"ŽŽØŽ†ŽŖŽ‚Ž°ŽŒŽŖŽ†Ž®Ž…Ž¬Ž‡Ž°",ss:"d% ŽŽØŽ†ŽŖŽ‚Ž°ŽŒŽŖ",m:"Ž‰ŽØŽ‚ŽØŽ“Ž¬Ž‡Ž°",mm:"Ž‰ŽØŽ‚ŽØŽ“ŽŖ %d",h:"ŽŽŽ¦Ž‘ŽØŽ‡ŽØŽƒŽ¬Ž‡Ž°",hh:"ŽŽŽ¦Ž‘ŽØŽ‡ŽØŽƒŽŖ %d",d:"Ž‹ŽŖŽˆŽ¦Ž€Ž¬Ž‡Ž°",dd:"Ž‹ŽŖŽˆŽ¦ŽŽ° %d",M:"Ž‰Ž¦Ž€Ž¬Ž‡Ž°",MM:"Ž‰Ž¦ŽŽ° %d",y:"Ž‡Ž¦Ž€Ž¦ŽƒŽ¬Ž‡Ž°",yy:"Ž‡Ž¦Ž€Ž¦ŽƒŽŖ %d"},preparse:function(e){return e.replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/,/g,"ŲŒ")},week:{dow:7,doy:12}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t=["Ž–Ž¬Ž‚ŽŖŽ‡Ž¦ŽƒŽ©","ŽŠŽ¬Ž„Ž°ŽƒŽŖŽ‡Ž¦ŽƒŽ©","Ž‰Ž§ŽƒŽØŽ—ŽŖ","Ž‡Ž­Ž•Ž°ŽƒŽ©ŽŽŖ","Ž‰Ž­","Ž–Ž«Ž‚Ž°","Ž–ŽŖŽŽ¦Ž‡ŽØ","Ž‡ŽÆŽŽŽ¦ŽŽ°Ž“ŽŖ","ŽŽ¬Ž•Ž°Ž“Ž¬Ž‰Ž°Ž„Ž¦ŽƒŽŖ","Ž‡Ž®Ž†Ž°Ž“ŽÆŽ„Ž¦ŽƒŽŖ","Ž‚Ž®ŽˆŽ¬Ž‰Ž°Ž„Ž¦ŽƒŽŖ","Ž‘ŽØŽŽ¬Ž‰Ž°Ž„Ž¦ŽƒŽŖ"],n=["Ž‡Ž§Ž‹ŽØŽ‡Ž°ŽŒŽ¦","Ž€ŽÆŽ‰Ž¦","Ž‡Ž¦Ž‚Ž°ŽŽŽ§ŽƒŽ¦","Ž„ŽŖŽ‹Ž¦","Ž„ŽŖŽƒŽ§ŽŽ°ŽŠŽ¦ŽŒŽØ","Ž€ŽŖŽ†ŽŖŽƒŽŖ","Ž€Ž®Ž‚ŽØŽ€ŽØŽƒŽŖ"],a;e.defineLocale("dv",{months:t,monthsShort:t,weekdays:n,weekdaysShort:n,weekdaysMin:"Ž‡Ž§Ž‹ŽØ_Ž€ŽÆŽ‰Ž¦_Ž‡Ž¦Ž‚Ž°_Ž„ŽŖŽ‹Ž¦_Ž„ŽŖŽƒŽ§_Ž€ŽŖŽ†ŽŖ_Ž€Ž®Ž‚ŽØ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/M/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/Ž‰Ž†|Ž‰ŽŠ/,isPM:function(e){return"Ž‰ŽŠ"===e},meridiem:function(e,t,n){if(e<12)return"Ž‰Ž†";else return"Ž‰ŽŠ"},calendar:{sameDay:"[Ž‰ŽØŽ‡Ž¦Ž‹ŽŖ] LT",nextDay:"[Ž‰Ž§Ž‹Ž¦Ž‰Ž§] LT",nextWeek:"dddd LT",lastDay:"[Ž‡ŽØŽ‡Ž°Ž”Ž¬] LT",lastWeek:"[ŽŠŽ§Ž‡ŽØŽŒŽŖŽˆŽØ] dddd LT",sameElse:"L"},relativeTime:{future:"ŽŒŽ¬ŽƒŽ­ŽŽŽ¦Ž‡ŽØ %s",past:"Ž†ŽŖŽƒŽØŽ‚Ž° %s",s:"ŽŽØŽ†ŽŖŽ‚Ž°ŽŒŽŖŽ†Ž®Ž…Ž¬Ž‡Ž°",ss:"d% ŽŽØŽ†ŽŖŽ‚Ž°ŽŒŽŖ",m:"Ž‰ŽØŽ‚ŽØŽ“Ž¬Ž‡Ž°",mm:"Ž‰ŽØŽ‚ŽØŽ“ŽŖ %d",h:"ŽŽŽ¦Ž‘ŽØŽ‡ŽØŽƒŽ¬Ž‡Ž°",hh:"ŽŽŽ¦Ž‘ŽØŽ‡ŽØŽƒŽŖ %d",d:"Ž‹ŽŖŽˆŽ¦Ž€Ž¬Ž‡Ž°",dd:"Ž‹ŽŖŽˆŽ¦ŽŽ° %d",M:"Ž‰Ž¦Ž€Ž¬Ž‡Ž°",MM:"Ž‰Ž¦ŽŽ° %d",y:"Ž‡Ž¦Ž€Ž¦ŽƒŽ¬Ž‡Ž°",yy:"Ž‡Ž¦Ž€Ž¦ŽƒŽŖ %d"},preparse:function(e){return e.replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/,/g,"ŲŒ")},week:{dow:7,doy:12}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function r(e){return typeof Function!=="undefined"&&e instanceof Function||Object.prototype.toString.call(e)==="[object Function]"}var t;e.defineLocale("el",{monthsNominativeEl:"Ī™Ī±Ī½ĪæĻ…Ī¬ĻĪ¹ĪæĻ‚_Ī¦ĪµĪ²ĻĪæĻ…Ī¬ĻĪ¹ĪæĻ‚_ĪœĪ¬ĻĻ„Ī¹ĪæĻ‚_Ī‘Ļ€ĻĪÆĪ»Ī¹ĪæĻ‚_ĪœĪ¬Ī¹ĪæĻ‚_Ī™ĪæĻĪ½Ī¹ĪæĻ‚_Ī™ĪæĻĪ»Ī¹ĪæĻ‚_Ī‘ĻĪ³ĪæĻ…ĻƒĻ„ĪæĻ‚_Ī£ĪµĻ€Ļ„Ī­Ī¼Ī²ĻĪ¹ĪæĻ‚_ĪŸĪŗĻ„ĻŽĪ²ĻĪ¹ĪæĻ‚_ĪĪæĪ­Ī¼Ī²ĻĪ¹ĪæĻ‚_Ī”ĪµĪŗĪ­Ī¼Ī²ĻĪ¹ĪæĻ‚".split("_"),monthsGenitiveEl:"Ī™Ī±Ī½ĪæĻ…Ī±ĻĪÆĪæĻ…_Ī¦ĪµĪ²ĻĪæĻ…Ī±ĻĪÆĪæĻ…_ĪœĪ±ĻĻ„ĪÆĪæĻ…_Ī‘Ļ€ĻĪ¹Ī»ĪÆĪæĻ…_ĪœĪ±ĪĪæĻ…_Ī™ĪæĻ…Ī½ĪÆĪæĻ…_Ī™ĪæĻ…Ī»ĪÆĪæĻ…_Ī‘Ļ…Ī³ĪæĻĻƒĻ„ĪæĻ…_Ī£ĪµĻ€Ļ„ĪµĪ¼Ī²ĻĪÆĪæĻ…_ĪŸĪŗĻ„Ļ‰Ī²ĻĪÆĪæĻ…_ĪĪæĪµĪ¼Ī²ĻĪÆĪæĻ…_Ī”ĪµĪŗĪµĪ¼Ī²ĻĪÆĪæĻ…".split("_"),months:function(e,t){if(!e)return this._monthsNominativeEl;else if(typeof t==="string"&&/D/.test(t.substring(0,t.indexOf("MMMM"))))return this._monthsGenitiveEl[e.month()];else return this._monthsNominativeEl[e.month()]},monthsShort:"Ī™Ī±Ī½_Ī¦ĪµĪ²_ĪœĪ±Ļ_Ī‘Ļ€Ļ_ĪœĪ±ĻŠ_Ī™ĪæĻ…Ī½_Ī™ĪæĻ…Ī»_Ī‘Ļ…Ī³_Ī£ĪµĻ€_ĪŸĪŗĻ„_ĪĪæĪµ_Ī”ĪµĪŗ".split("_"),weekdays:"ĪšĻ…ĻĪ¹Ī±ĪŗĪ®_Ī”ĪµĻ…Ļ„Ī­ĻĪ±_Ī¤ĻĪÆĻ„Ī·_Ī¤ĪµĻ„Ī¬ĻĻ„Ī·_Ī Ī­Ī¼Ļ€Ļ„Ī·_Ī Ī±ĻĪ±ĻƒĪŗĪµĻ…Ī®_Ī£Ī¬Ī²Ī²Ī±Ļ„Īæ".split("_"),weekdaysShort:"ĪšĻ…Ļ_Ī”ĪµĻ…_Ī¤ĻĪ¹_Ī¤ĪµĻ„_Ī ĪµĪ¼_Ī Ī±Ļ_Ī£Ī±Ī²".split("_"),weekdaysMin:"ĪšĻ…_Ī”Īµ_Ī¤Ļ_Ī¤Īµ_Ī Īµ_Ī Ī±_Ī£Ī±".split("_"),meridiem:function(e,t,n){if(e>11)return n?"Ī¼Ī¼":"ĪœĪœ";else return n?"Ļ€Ī¼":"Ī Īœ"},isPM:function(e){return(e+"").toLowerCase()[0]==="Ī¼"},meridiemParse:/[Ī Īœ]\.?Īœ?\.?/i,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendarEl:{sameDay:"[Ī£Ī®Ī¼ĪµĻĪ± {}] LT",nextDay:"[Ī‘ĻĻĪ¹Īæ {}] LT",nextWeek:"dddd [{}] LT",lastDay:"[Ī§ĪøĪµĻ‚ {}] LT",lastWeek:function(){switch(this.day()){case 6:return"[Ļ„Īæ Ļ€ĻĪæĪ·Ī³ĪæĻĪ¼ĪµĪ½Īæ] dddd [{}] LT";default:return"[Ļ„Ī·Ī½ Ļ€ĻĪæĪ·Ī³ĪæĻĪ¼ĪµĪ½Ī·] dddd [{}] LT"}},sameElse:"L"},calendar:function(e,t){var n=this._calendarEl[e],a=t&&t.hours();if(r(n))n=n.apply(t);return n.replace("{}",a%12===1?"ĻƒĻ„Ī·":"ĻƒĻ„Ī¹Ļ‚")},relativeTime:{future:"ĻƒĪµ %s",past:"%s Ļ€ĻĪ¹Ī½",s:"Ī»ĪÆĪ³Ī± Ī“ĪµĻ…Ļ„ĪµĻĻŒĪ»ĪµĻ€Ļ„Ī±",ss:"%d Ī“ĪµĻ…Ļ„ĪµĻĻŒĪ»ĪµĻ€Ļ„Ī±",m:"Ī­Ī½Ī± Ī»ĪµĻ€Ļ„ĻŒ",mm:"%d Ī»ĪµĻ€Ļ„Ī¬",h:"Ī¼ĪÆĪ± ĻŽĻĪ±",hh:"%d ĻŽĻĪµĻ‚",d:"Ī¼ĪÆĪ± Ī¼Ī­ĻĪ±",dd:"%d Ī¼Ī­ĻĪµĻ‚",M:"Ī­Ī½Ī±Ļ‚ Ī¼Ī®Ī½Ī±Ļ‚",MM:"%d Ī¼Ī®Ī½ĪµĻ‚",y:"Ī­Ī½Ī±Ļ‚ Ļ‡ĻĻŒĪ½ĪæĻ‚",yy:"%d Ļ‡ĻĻŒĪ½Ī¹Ī±"},dayOfMonthOrdinalParse:/\d{1,2}Ī·/,ordinal:"%dĪ·",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function r(e){return typeof Function!=="undefined"&&e instanceof Function||Object.prototype.toString.call(e)==="[object Function]"}var t;e.defineLocale("el",{monthsNominativeEl:"Ī™Ī±Ī½ĪæĻ…Ī¬ĻĪ¹ĪæĻ‚_Ī¦ĪµĪ²ĻĪæĻ…Ī¬ĻĪ¹ĪæĻ‚_ĪœĪ¬ĻĻ„Ī¹ĪæĻ‚_Ī‘Ļ€ĻĪÆĪ»Ī¹ĪæĻ‚_ĪœĪ¬Ī¹ĪæĻ‚_Ī™ĪæĻĪ½Ī¹ĪæĻ‚_Ī™ĪæĻĪ»Ī¹ĪæĻ‚_Ī‘ĻĪ³ĪæĻ…ĻƒĻ„ĪæĻ‚_Ī£ĪµĻ€Ļ„Ī­Ī¼Ī²ĻĪ¹ĪæĻ‚_ĪŸĪŗĻ„ĻŽĪ²ĻĪ¹ĪæĻ‚_ĪĪæĪ­Ī¼Ī²ĻĪ¹ĪæĻ‚_Ī”ĪµĪŗĪ­Ī¼Ī²ĻĪ¹ĪæĻ‚".split("_"),monthsGenitiveEl:"Ī™Ī±Ī½ĪæĻ…Ī±ĻĪÆĪæĻ…_Ī¦ĪµĪ²ĻĪæĻ…Ī±ĻĪÆĪæĻ…_ĪœĪ±ĻĻ„ĪÆĪæĻ…_Ī‘Ļ€ĻĪ¹Ī»ĪÆĪæĻ…_ĪœĪ±ĪĪæĻ…_Ī™ĪæĻ…Ī½ĪÆĪæĻ…_Ī™ĪæĻ…Ī»ĪÆĪæĻ…_Ī‘Ļ…Ī³ĪæĻĻƒĻ„ĪæĻ…_Ī£ĪµĻ€Ļ„ĪµĪ¼Ī²ĻĪÆĪæĻ…_ĪŸĪŗĻ„Ļ‰Ī²ĻĪÆĪæĻ…_ĪĪæĪµĪ¼Ī²ĻĪÆĪæĻ…_Ī”ĪµĪŗĪµĪ¼Ī²ĻĪÆĪæĻ…".split("_"),months:function(e,t){if(!e)return this._monthsNominativeEl;else if(typeof t==="string"&&/D/.test(t.substring(0,t.indexOf("MMMM"))))return this._monthsGenitiveEl[e.month()];else return this._monthsNominativeEl[e.month()]},monthsShort:"Ī™Ī±Ī½_Ī¦ĪµĪ²_ĪœĪ±Ļ_Ī‘Ļ€Ļ_ĪœĪ±ĻŠ_Ī™ĪæĻ…Ī½_Ī™ĪæĻ…Ī»_Ī‘Ļ…Ī³_Ī£ĪµĻ€_ĪŸĪŗĻ„_ĪĪæĪµ_Ī”ĪµĪŗ".split("_"),weekdays:"ĪšĻ…ĻĪ¹Ī±ĪŗĪ®_Ī”ĪµĻ…Ļ„Ī­ĻĪ±_Ī¤ĻĪÆĻ„Ī·_Ī¤ĪµĻ„Ī¬ĻĻ„Ī·_Ī Ī­Ī¼Ļ€Ļ„Ī·_Ī Ī±ĻĪ±ĻƒĪŗĪµĻ…Ī®_Ī£Ī¬Ī²Ī²Ī±Ļ„Īæ".split("_"),weekdaysShort:"ĪšĻ…Ļ_Ī”ĪµĻ…_Ī¤ĻĪ¹_Ī¤ĪµĻ„_Ī ĪµĪ¼_Ī Ī±Ļ_Ī£Ī±Ī²".split("_"),weekdaysMin:"ĪšĻ…_Ī”Īµ_Ī¤Ļ_Ī¤Īµ_Ī Īµ_Ī Ī±_Ī£Ī±".split("_"),meridiem:function(e,t,n){if(e>11)return n?"Ī¼Ī¼":"ĪœĪœ";else return n?"Ļ€Ī¼":"Ī Īœ"},isPM:function(e){return(e+"").toLowerCase()[0]==="Ī¼"},meridiemParse:/[Ī Īœ]\.?Īœ?\.?/i,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendarEl:{sameDay:"[Ī£Ī®Ī¼ĪµĻĪ± {}] LT",nextDay:"[Ī‘ĻĻĪ¹Īæ {}] LT",nextWeek:"dddd [{}] LT",lastDay:"[Ī§ĪøĪµĻ‚ {}] LT",lastWeek:function(){switch(this.day()){case 6:return"[Ļ„Īæ Ļ€ĻĪæĪ·Ī³ĪæĻĪ¼ĪµĪ½Īæ] dddd [{}] LT";default:return"[Ļ„Ī·Ī½ Ļ€ĻĪæĪ·Ī³ĪæĻĪ¼ĪµĪ½Ī·] dddd [{}] LT"}},sameElse:"L"},calendar:function(e,t){var n=this._calendarEl[e],a=t&&t.hours();if(r(n))n=n.apply(t);return n.replace("{}",a%12===1?"ĻƒĻ„Ī·":"ĻƒĻ„Ī¹Ļ‚")},relativeTime:{future:"ĻƒĪµ %s",past:"%s Ļ€ĻĪ¹Ī½",s:"Ī»ĪÆĪ³Ī± Ī“ĪµĻ…Ļ„ĪµĻĻŒĪ»ĪµĻ€Ļ„Ī±",ss:"%d Ī“ĪµĻ…Ļ„ĪµĻĻŒĪ»ĪµĻ€Ļ„Ī±",m:"Ī­Ī½Ī± Ī»ĪµĻ€Ļ„ĻŒ",mm:"%d Ī»ĪµĻ€Ļ„Ī¬",h:"Ī¼ĪÆĪ± ĻŽĻĪ±",hh:"%d ĻŽĻĪµĻ‚",d:"Ī¼ĪÆĪ± Ī¼Ī­ĻĪ±",dd:"%d Ī¼Ī­ĻĪµĻ‚",M:"Ī­Ī½Ī±Ļ‚ Ī¼Ī®Ī½Ī±Ļ‚",MM:"%d Ī¼Ī®Ī½ĪµĻ‚",y:"Ī­Ī½Ī±Ļ‚ Ļ‡ĻĻŒĪ½ĪæĻ‚",yy:"%d Ļ‡ĻĻŒĪ½Ī¹Ī±"},dayOfMonthOrdinalParse:/\d{1,2}Ī·/,ordinal:"%dĪ·",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("en-au",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:0,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("en-au",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:0,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("en-ca",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"YYYY-MM-DD",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("en-ca",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"YYYY-MM-DD",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("en-gb",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("en-gb",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("en-ie",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("en-ie",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("en-il",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("en-il",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("en-in",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("en-in",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("en-nz",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("en-nz",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("en-sg",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("en-sg",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("eo",{months:"januaro_februaro_marto_aprilo_majo_junio_julio_aÅ­gusto_septembro_oktobro_novembro_decembro".split("_"),monthsShort:"jan_feb_mart_apr_maj_jun_jul_aÅ­g_sept_okt_nov_dec".split("_"),weekdays:"dimanĉo_lundo_mardo_merkredo_ĵaÅ­do_vendredo_sabato".split("_"),weekdaysShort:"dim_lun_mard_merk_ĵaÅ­_ven_sab".split("_"),weekdaysMin:"di_lu_ma_me_ĵa_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"[la] D[-an de] MMMM, YYYY",LLL:"[la] D[-an de] MMMM, YYYY HH:mm",LLLL:"dddd[n], [la] D[-an de] MMMM, YYYY HH:mm",llll:"ddd, [la] D[-an de] MMM, YYYY HH:mm"},meridiemParse:/[ap]\.t\.m/i,isPM:function(e){return e.charAt(0).toLowerCase()==="p"},meridiem:function(e,t,n){if(e>11)return n?"p.t.m.":"P.T.M.";else return n?"a.t.m.":"A.T.M."},calendar:{sameDay:"[HodiaÅ­ je] LT",nextDay:"[MorgaÅ­ je] LT",nextWeek:"dddd[n je] LT",lastDay:"[HieraÅ­ je] LT",lastWeek:"[pasintan] dddd[n je] LT",sameElse:"L"},relativeTime:{future:"post %s",past:"antaÅ­ %s",s:"kelkaj sekundoj",ss:"%d sekundoj",m:"unu minuto",mm:"%d minutoj",h:"unu horo",hh:"%d horoj",d:"unu tago",dd:"%d tagoj",M:"unu monato",MM:"%d monatoj",y:"unu jaro",yy:"%d jaroj"},dayOfMonthOrdinalParse:/\d{1,2}a/,ordinal:"%da",week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("eo",{months:"januaro_februaro_marto_aprilo_majo_junio_julio_aÅ­gusto_septembro_oktobro_novembro_decembro".split("_"),monthsShort:"jan_feb_mart_apr_maj_jun_jul_aÅ­g_sept_okt_nov_dec".split("_"),weekdays:"dimanĉo_lundo_mardo_merkredo_ĵaÅ­do_vendredo_sabato".split("_"),weekdaysShort:"dim_lun_mard_merk_ĵaÅ­_ven_sab".split("_"),weekdaysMin:"di_lu_ma_me_ĵa_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"[la] D[-an de] MMMM, YYYY",LLL:"[la] D[-an de] MMMM, YYYY HH:mm",LLLL:"dddd[n], [la] D[-an de] MMMM, YYYY HH:mm",llll:"ddd, [la] D[-an de] MMM, YYYY HH:mm"},meridiemParse:/[ap]\.t\.m/i,isPM:function(e){return e.charAt(0).toLowerCase()==="p"},meridiem:function(e,t,n){if(e>11)return n?"p.t.m.":"P.T.M.";else return n?"a.t.m.":"A.T.M."},calendar:{sameDay:"[HodiaÅ­ je] LT",nextDay:"[MorgaÅ­ je] LT",nextWeek:"dddd[n je] LT",lastDay:"[HieraÅ­ je] LT",lastWeek:"[pasintan] dddd[n je] LT",sameElse:"L"},relativeTime:{future:"post %s",past:"antaÅ­ %s",s:"kelkaj sekundoj",ss:"%d sekundoj",m:"unu minuto",mm:"%d minutoj",h:"unu horo",hh:"%d horoj",d:"unu tago",dd:"%d tagoj",M:"unu monato",MM:"%d monatoj",y:"unu jaro",yy:"%d jaroj"},dayOfMonthOrdinalParse:/\d{1,2}a/,ordinal:"%da",week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var n="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),a="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),t=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],r=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,o;e.defineLocale("es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,t){if(!e)return n;else if(/-MMM-/.test(t))return a[e.month()];else return n[e.month()]},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"domingo_lunes_martes_miĆ©rcoles_jueves_viernes_sĆ”bado".split("_"),weekdaysShort:"dom._lun._mar._miĆ©._jue._vie._sĆ”b.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoy a la"+(this.hours()!==1?"s":"")+"] LT"},nextDay:function(){return"[maƱana a la"+(this.hours()!==1?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(this.hours()!==1?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(this.hours()!==1?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(this.hours()!==1?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un dĆ­a",dd:"%d dĆ­as",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un aƱo",yy:"%d aƱos"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4},invalidDate:"Fecha invĆ”lida"})}(n(8))},function(e,t,n){!function(e){"use strict"; +var n="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),a="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),t=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],r=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,o;e.defineLocale("es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,t){if(!e)return n;else if(/-MMM-/.test(t))return a[e.month()];else return n[e.month()]},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"domingo_lunes_martes_miĆ©rcoles_jueves_viernes_sĆ”bado".split("_"),weekdaysShort:"dom._lun._mar._miĆ©._jue._vie._sĆ”b.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoy a la"+(this.hours()!==1?"s":"")+"] LT"},nextDay:function(){return"[maƱana a la"+(this.hours()!==1?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(this.hours()!==1?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(this.hours()!==1?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(this.hours()!==1?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un dĆ­a",dd:"%d dĆ­as",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un aƱo",yy:"%d aƱos"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4},invalidDate:"Fecha invĆ”lida"})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var n="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),a="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),t=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],r=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,o;e.defineLocale("es-do",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,t){if(!e)return n;else if(/-MMM-/.test(t))return a[e.month()];else return n[e.month()]},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"domingo_lunes_martes_miĆ©rcoles_jueves_viernes_sĆ”bado".split("_"),weekdaysShort:"dom._lun._mar._miĆ©._jue._vie._sĆ”b.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY h:mm A",LLLL:"dddd, D [de] MMMM [de] YYYY h:mm A"},calendar:{sameDay:function(){return"[hoy a la"+(this.hours()!==1?"s":"")+"] LT"},nextDay:function(){return"[maƱana a la"+(this.hours()!==1?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(this.hours()!==1?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(this.hours()!==1?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(this.hours()!==1?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un dĆ­a",dd:"%d dĆ­as",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un aƱo",yy:"%d aƱos"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var n="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),a="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),t=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],r=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,o;e.defineLocale("es-do",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,t){if(!e)return n;else if(/-MMM-/.test(t))return a[e.month()];else return n[e.month()]},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"domingo_lunes_martes_miĆ©rcoles_jueves_viernes_sĆ”bado".split("_"),weekdaysShort:"dom._lun._mar._miĆ©._jue._vie._sĆ”b.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY h:mm A",LLLL:"dddd, D [de] MMMM [de] YYYY h:mm A"},calendar:{sameDay:function(){return"[hoy a la"+(this.hours()!==1?"s":"")+"] LT"},nextDay:function(){return"[maƱana a la"+(this.hours()!==1?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(this.hours()!==1?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(this.hours()!==1?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(this.hours()!==1?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un dĆ­a",dd:"%d dĆ­as",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un aƱo",yy:"%d aƱos"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var n="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),a="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),t=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],r=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,o;e.defineLocale("es-mx",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,t){if(!e)return n;else if(/-MMM-/.test(t))return a[e.month()];else return n[e.month()]},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"domingo_lunes_martes_miĆ©rcoles_jueves_viernes_sĆ”bado".split("_"),weekdaysShort:"dom._lun._mar._miĆ©._jue._vie._sĆ”b.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoy a la"+(this.hours()!==1?"s":"")+"] LT"},nextDay:function(){return"[maƱana a la"+(this.hours()!==1?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(this.hours()!==1?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(this.hours()!==1?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(this.hours()!==1?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un dĆ­a",dd:"%d dĆ­as",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un aƱo",yy:"%d aƱos"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:0,doy:4},invalidDate:"Fecha invĆ”lida"})}(n(8))},function(e,t,n){!function(e){"use strict"; +var n="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),a="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),t=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],r=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,o;e.defineLocale("es-mx",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,t){if(!e)return n;else if(/-MMM-/.test(t))return a[e.month()];else return n[e.month()]},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"domingo_lunes_martes_miĆ©rcoles_jueves_viernes_sĆ”bado".split("_"),weekdaysShort:"dom._lun._mar._miĆ©._jue._vie._sĆ”b.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoy a la"+(this.hours()!==1?"s":"")+"] LT"},nextDay:function(){return"[maƱana a la"+(this.hours()!==1?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(this.hours()!==1?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(this.hours()!==1?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(this.hours()!==1?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un dĆ­a",dd:"%d dĆ­as",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un aƱo",yy:"%d aƱos"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:0,doy:4},invalidDate:"Fecha invĆ”lida"})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var n="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),a="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),t=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],r=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,o;e.defineLocale("es-us",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,t){if(!e)return n;else if(/-MMM-/.test(t))return a[e.month()];else return n[e.month()]},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"domingo_lunes_martes_miĆ©rcoles_jueves_viernes_sĆ”bado".split("_"),weekdaysShort:"dom._lun._mar._miĆ©._jue._vie._sĆ”b.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"MM/DD/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY h:mm A",LLLL:"dddd, D [de] MMMM [de] YYYY h:mm A"},calendar:{sameDay:function(){return"[hoy a la"+(this.hours()!==1?"s":"")+"] LT"},nextDay:function(){return"[maƱana a la"+(this.hours()!==1?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(this.hours()!==1?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(this.hours()!==1?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(this.hours()!==1?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un dĆ­a",dd:"%d dĆ­as",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un aƱo",yy:"%d aƱos"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var n="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),a="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),t=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],r=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,o;e.defineLocale("es-us",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,t){if(!e)return n;else if(/-MMM-/.test(t))return a[e.month()];else return n[e.month()]},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"domingo_lunes_martes_miĆ©rcoles_jueves_viernes_sĆ”bado".split("_"),weekdaysShort:"dom._lun._mar._miĆ©._jue._vie._sĆ”b.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"MM/DD/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY h:mm A",LLLL:"dddd, D [de] MMMM [de] YYYY h:mm A"},calendar:{sameDay:function(){return"[hoy a la"+(this.hours()!==1?"s":"")+"] LT"},nextDay:function(){return"[maƱana a la"+(this.hours()!==1?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(this.hours()!==1?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(this.hours()!==1?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(this.hours()!==1?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un dĆ­a",dd:"%d dĆ­as",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un aƱo",yy:"%d aƱos"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function t(e,t,n,a){var r={s:["mƵne sekundi","mƵni sekund","paar sekundit"],ss:[e+"sekundi",e+"sekundit"],m:["Ć¼he minuti","Ć¼ks minut"],mm:[e+" minuti",e+" minutit"],h:["Ć¼he tunni","tund aega","Ć¼ks tund"],hh:[e+" tunni",e+" tundi"],d:["Ć¼he pƤeva","Ć¼ks pƤev"],M:["kuu aja","kuu aega","Ć¼ks kuu"],MM:[e+" kuu",e+" kuud"],y:["Ć¼he aasta","aasta","Ć¼ks aasta"],yy:[e+" aasta",e+" aastat"]};if(t)return r[n][2]?r[n][2]:r[n][1];return a?r[n][0]:r[n][1]}var n;e.defineLocale("et",{months:"jaanuar_veebruar_mƤrts_aprill_mai_juuni_juuli_august_september_oktoober_november_detsember".split("_"),monthsShort:"jaan_veebr_mƤrts_apr_mai_juuni_juuli_aug_sept_okt_nov_dets".split("_"),weekdays:"pĆ¼hapƤev_esmaspƤev_teisipƤev_kolmapƤev_neljapƤev_reede_laupƤev".split("_"),weekdaysShort:"P_E_T_K_N_R_L".split("_"),weekdaysMin:"P_E_T_K_N_R_L".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[TƤna,] LT",nextDay:"[Homme,] LT",nextWeek:"[JƤrgmine] dddd LT",lastDay:"[Eile,] LT",lastWeek:"[Eelmine] dddd LT",sameElse:"L"},relativeTime:{future:"%s pƤrast",past:"%s tagasi",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:"%d pƤeva",M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function t(e,t,n,a){var r={s:["mƵne sekundi","mƵni sekund","paar sekundit"],ss:[e+"sekundi",e+"sekundit"],m:["Ć¼he minuti","Ć¼ks minut"],mm:[e+" minuti",e+" minutit"],h:["Ć¼he tunni","tund aega","Ć¼ks tund"],hh:[e+" tunni",e+" tundi"],d:["Ć¼he pƤeva","Ć¼ks pƤev"],M:["kuu aja","kuu aega","Ć¼ks kuu"],MM:[e+" kuu",e+" kuud"],y:["Ć¼he aasta","aasta","Ć¼ks aasta"],yy:[e+" aasta",e+" aastat"]};if(t)return r[n][2]?r[n][2]:r[n][1];return a?r[n][0]:r[n][1]}var n;e.defineLocale("et",{months:"jaanuar_veebruar_mƤrts_aprill_mai_juuni_juuli_august_september_oktoober_november_detsember".split("_"),monthsShort:"jaan_veebr_mƤrts_apr_mai_juuni_juuli_aug_sept_okt_nov_dets".split("_"),weekdays:"pĆ¼hapƤev_esmaspƤev_teisipƤev_kolmapƤev_neljapƤev_reede_laupƤev".split("_"),weekdaysShort:"P_E_T_K_N_R_L".split("_"),weekdaysMin:"P_E_T_K_N_R_L".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[TƤna,] LT",nextDay:"[Homme,] LT",nextWeek:"[JƤrgmine] dddd LT",lastDay:"[Eile,] LT",lastWeek:"[Eelmine] dddd LT",sameElse:"L"},relativeTime:{future:"%s pƤrast",past:"%s tagasi",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:"%d pƤeva",M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("eu",{months:"urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua".split("_"),monthsShort:"urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.".split("_"),monthsParseExact:true,weekdays:"igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata".split("_"),weekdaysShort:"ig._al._ar._az._og._ol._lr.".split("_"),weekdaysMin:"ig_al_ar_az_og_ol_lr".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY[ko] MMMM[ren] D[a]",LLL:"YYYY[ko] MMMM[ren] D[a] HH:mm",LLLL:"dddd, YYYY[ko] MMMM[ren] D[a] HH:mm",l:"YYYY-M-D",ll:"YYYY[ko] MMM D[a]",lll:"YYYY[ko] MMM D[a] HH:mm",llll:"ddd, YYYY[ko] MMM D[a] HH:mm"},calendar:{sameDay:"[gaur] LT[etan]",nextDay:"[bihar] LT[etan]",nextWeek:"dddd LT[etan]",lastDay:"[atzo] LT[etan]",lastWeek:"[aurreko] dddd LT[etan]",sameElse:"L"},relativeTime:{future:"%s barru",past:"duela %s",s:"segundo batzuk",ss:"%d segundo",m:"minutu bat",mm:"%d minutu",h:"ordu bat",hh:"%d ordu",d:"egun bat",dd:"%d egun",M:"hilabete bat",MM:"%d hilabete",y:"urte bat",yy:"%d urte"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("eu",{months:"urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua".split("_"),monthsShort:"urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.".split("_"),monthsParseExact:true,weekdays:"igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata".split("_"),weekdaysShort:"ig._al._ar._az._og._ol._lr.".split("_"),weekdaysMin:"ig_al_ar_az_og_ol_lr".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY[ko] MMMM[ren] D[a]",LLL:"YYYY[ko] MMMM[ren] D[a] HH:mm",LLLL:"dddd, YYYY[ko] MMMM[ren] D[a] HH:mm",l:"YYYY-M-D",ll:"YYYY[ko] MMM D[a]",lll:"YYYY[ko] MMM D[a] HH:mm",llll:"ddd, YYYY[ko] MMM D[a] HH:mm"},calendar:{sameDay:"[gaur] LT[etan]",nextDay:"[bihar] LT[etan]",nextWeek:"dddd LT[etan]",lastDay:"[atzo] LT[etan]",lastWeek:"[aurreko] dddd LT[etan]",sameElse:"L"},relativeTime:{future:"%s barru",past:"duela %s",s:"segundo batzuk",ss:"%d segundo",m:"minutu bat",mm:"%d minutu",h:"ordu bat",hh:"%d ordu",d:"egun bat",dd:"%d egun",M:"hilabete bat",MM:"%d hilabete",y:"urte bat",yy:"%d urte"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"Ū±",2:"Ū²",3:"Ū³",4:"Ū“",5:"Ūµ",6:"Ū¶",7:"Ū·",8:"Ūø",9:"Ū¹",0:"Ū°"},n={"Ū±":"1","Ū²":"2","Ū³":"3","Ū“":"4","Ūµ":"5","Ū¶":"6","Ū·":"7","Ūø":"8","Ū¹":"9","Ū°":"0"},a;e.defineLocale("fa",{months:"Ś˜Ų§Ł†ŁˆŪŒŁ‡_ŁŁˆŲ±ŪŒŁ‡_Ł…Ų§Ų±Ų³_Ų¢ŁˆŲ±ŪŒŁ„_Ł…Ł‡_Ś˜ŁˆŲ¦Ł†_Ś˜ŁˆŲ¦ŪŒŁ‡_Ų§ŁˆŲŖ_Ų³Ł¾ŲŖŲ§Ł…ŲØŲ±_Ų§Ś©ŲŖŲØŲ±_Ł†ŁˆŲ§Ł…ŲØŲ±_ŲÆŲ³Ų§Ł…ŲØŲ±".split("_"),monthsShort:"Ś˜Ų§Ł†ŁˆŪŒŁ‡_ŁŁˆŲ±ŪŒŁ‡_Ł…Ų§Ų±Ų³_Ų¢ŁˆŲ±ŪŒŁ„_Ł…Ł‡_Ś˜ŁˆŲ¦Ł†_Ś˜ŁˆŲ¦ŪŒŁ‡_Ų§ŁˆŲŖ_Ų³Ł¾ŲŖŲ§Ł…ŲØŲ±_Ų§Ś©ŲŖŲØŲ±_Ł†ŁˆŲ§Ł…ŲØŲ±_ŲÆŲ³Ų§Ł…ŲØŲ±".split("_"),weekdays:"ŪŒŚ©ā€ŒŲ“Ł†ŲØŁ‡_ŲÆŁˆŲ“Ł†ŲØŁ‡_Ų³Ł‡ā€ŒŲ“Ł†ŲØŁ‡_Ś†Ł‡Ų§Ų±Ų“Ł†ŲØŁ‡_Ł¾Ł†Ų¬ā€ŒŲ“Ł†ŲØŁ‡_Ų¬Ł…Ų¹Ł‡_Ų“Ł†ŲØŁ‡".split("_"),weekdaysShort:"ŪŒŚ©ā€ŒŲ“Ł†ŲØŁ‡_ŲÆŁˆŲ“Ł†ŲØŁ‡_Ų³Ł‡ā€ŒŲ“Ł†ŲØŁ‡_Ś†Ł‡Ų§Ų±Ų“Ł†ŲØŁ‡_Ł¾Ł†Ų¬ā€ŒŲ“Ł†ŲØŁ‡_Ų¬Ł…Ų¹Ł‡_Ų“Ł†ŲØŁ‡".split("_"),weekdaysMin:"ŪŒ_ŲÆ_Ų³_Ś†_Ł¾_Ų¬_Ų“".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/Ł‚ŲØŁ„ Ų§Ų² ŲøŁ‡Ų±|ŲØŲ¹ŲÆ Ų§Ų² ŲøŁ‡Ų±/,isPM:function(e){return/ŲØŲ¹ŲÆ Ų§Ų² ŲøŁ‡Ų±/.test(e)},meridiem:function(e,t,n){if(e<12)return"Ł‚ŲØŁ„ Ų§Ų² ŲøŁ‡Ų±";else return"ŲØŲ¹ŲÆ Ų§Ų² ŲøŁ‡Ų±"},calendar:{sameDay:"[Ų§Ł…Ų±ŁˆŲ² Ų³Ų§Ų¹ŲŖ] LT",nextDay:"[ŁŲ±ŲÆŲ§ Ų³Ų§Ų¹ŲŖ] LT",nextWeek:"dddd [Ų³Ų§Ų¹ŲŖ] LT",lastDay:"[ŲÆŪŒŲ±ŁˆŲ² Ų³Ų§Ų¹ŲŖ] LT",lastWeek:"dddd [Ł¾ŪŒŲ“] [Ų³Ų§Ų¹ŲŖ] LT",sameElse:"L"},relativeTime:{future:"ŲÆŲ± %s",past:"%s Ł¾ŪŒŲ“",s:"Ś†Ł†ŲÆ Ų«Ų§Ł†ŪŒŁ‡",ss:"%d Ų«Ų§Ł†ŪŒŁ‡",m:"ŪŒŚ© ŲÆŁ‚ŪŒŁ‚Ł‡",mm:"%d ŲÆŁ‚ŪŒŁ‚Ł‡",h:"ŪŒŚ© Ų³Ų§Ų¹ŲŖ",hh:"%d Ų³Ų§Ų¹ŲŖ",d:"ŪŒŚ© Ų±ŁˆŲ²",dd:"%d Ų±ŁˆŲ²",M:"ŪŒŚ© Ł…Ų§Ł‡",MM:"%d Ł…Ų§Ł‡",y:"ŪŒŚ© Ų³Ų§Ł„",yy:"%d Ų³Ų§Ł„"},preparse:function(e){return e.replace(/[Ū°-Ū¹]/g,function(e){return n[e]}).replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"ŲŒ")},dayOfMonthOrdinalParse:/\d{1,2}Ł…/,ordinal:"%dŁ…",week:{dow:6,doy:12}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"Ū±",2:"Ū²",3:"Ū³",4:"Ū“",5:"Ūµ",6:"Ū¶",7:"Ū·",8:"Ūø",9:"Ū¹",0:"Ū°"},n={"Ū±":"1","Ū²":"2","Ū³":"3","Ū“":"4","Ūµ":"5","Ū¶":"6","Ū·":"7","Ūø":"8","Ū¹":"9","Ū°":"0"},a;e.defineLocale("fa",{months:"Ś˜Ų§Ł†ŁˆŪŒŁ‡_ŁŁˆŲ±ŪŒŁ‡_Ł…Ų§Ų±Ų³_Ų¢ŁˆŲ±ŪŒŁ„_Ł…Ł‡_Ś˜ŁˆŲ¦Ł†_Ś˜ŁˆŲ¦ŪŒŁ‡_Ų§ŁˆŲŖ_Ų³Ł¾ŲŖŲ§Ł…ŲØŲ±_Ų§Ś©ŲŖŲØŲ±_Ł†ŁˆŲ§Ł…ŲØŲ±_ŲÆŲ³Ų§Ł…ŲØŲ±".split("_"),monthsShort:"Ś˜Ų§Ł†ŁˆŪŒŁ‡_ŁŁˆŲ±ŪŒŁ‡_Ł…Ų§Ų±Ų³_Ų¢ŁˆŲ±ŪŒŁ„_Ł…Ł‡_Ś˜ŁˆŲ¦Ł†_Ś˜ŁˆŲ¦ŪŒŁ‡_Ų§ŁˆŲŖ_Ų³Ł¾ŲŖŲ§Ł…ŲØŲ±_Ų§Ś©ŲŖŲØŲ±_Ł†ŁˆŲ§Ł…ŲØŲ±_ŲÆŲ³Ų§Ł…ŲØŲ±".split("_"),weekdays:"ŪŒŚ©ā€ŒŲ“Ł†ŲØŁ‡_ŲÆŁˆŲ“Ł†ŲØŁ‡_Ų³Ł‡ā€ŒŲ“Ł†ŲØŁ‡_Ś†Ł‡Ų§Ų±Ų“Ł†ŲØŁ‡_Ł¾Ł†Ų¬ā€ŒŲ“Ł†ŲØŁ‡_Ų¬Ł…Ų¹Ł‡_Ų“Ł†ŲØŁ‡".split("_"),weekdaysShort:"ŪŒŚ©ā€ŒŲ“Ł†ŲØŁ‡_ŲÆŁˆŲ“Ł†ŲØŁ‡_Ų³Ł‡ā€ŒŲ“Ł†ŲØŁ‡_Ś†Ł‡Ų§Ų±Ų“Ł†ŲØŁ‡_Ł¾Ł†Ų¬ā€ŒŲ“Ł†ŲØŁ‡_Ų¬Ł…Ų¹Ł‡_Ų“Ł†ŲØŁ‡".split("_"),weekdaysMin:"ŪŒ_ŲÆ_Ų³_Ś†_Ł¾_Ų¬_Ų“".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/Ł‚ŲØŁ„ Ų§Ų² ŲøŁ‡Ų±|ŲØŲ¹ŲÆ Ų§Ų² ŲøŁ‡Ų±/,isPM:function(e){return/ŲØŲ¹ŲÆ Ų§Ų² ŲøŁ‡Ų±/.test(e)},meridiem:function(e,t,n){if(e<12)return"Ł‚ŲØŁ„ Ų§Ų² ŲøŁ‡Ų±";else return"ŲØŲ¹ŲÆ Ų§Ų² ŲøŁ‡Ų±"},calendar:{sameDay:"[Ų§Ł…Ų±ŁˆŲ² Ų³Ų§Ų¹ŲŖ] LT",nextDay:"[ŁŲ±ŲÆŲ§ Ų³Ų§Ų¹ŲŖ] LT",nextWeek:"dddd [Ų³Ų§Ų¹ŲŖ] LT",lastDay:"[ŲÆŪŒŲ±ŁˆŲ² Ų³Ų§Ų¹ŲŖ] LT",lastWeek:"dddd [Ł¾ŪŒŲ“] [Ų³Ų§Ų¹ŲŖ] LT",sameElse:"L"},relativeTime:{future:"ŲÆŲ± %s",past:"%s Ł¾ŪŒŲ“",s:"Ś†Ł†ŲÆ Ų«Ų§Ł†ŪŒŁ‡",ss:"%d Ų«Ų§Ł†ŪŒŁ‡",m:"ŪŒŚ© ŲÆŁ‚ŪŒŁ‚Ł‡",mm:"%d ŲÆŁ‚ŪŒŁ‚Ł‡",h:"ŪŒŚ© Ų³Ų§Ų¹ŲŖ",hh:"%d Ų³Ų§Ų¹ŲŖ",d:"ŪŒŚ© Ų±ŁˆŲ²",dd:"%d Ų±ŁˆŲ²",M:"ŪŒŚ© Ł…Ų§Ł‡",MM:"%d Ł…Ų§Ł‡",y:"ŪŒŚ© Ų³Ų§Ł„",yy:"%d Ų³Ų§Ł„"},preparse:function(e){return e.replace(/[Ū°-Ū¹]/g,function(e){return n[e]}).replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"ŲŒ")},dayOfMonthOrdinalParse:/\d{1,2}Ł…/,ordinal:"%dŁ…",week:{dow:6,doy:12}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var n="nolla yksi kaksi kolme neljƤ viisi kuusi seitsemƤn kahdeksan yhdeksƤn".split(" "),a=["nolla","yhden","kahden","kolmen","neljƤn","viiden","kuuden",n[7],n[8],n[9]],t;function r(e,t,n,a){var r="";switch(n){case"s":return a?"muutaman sekunnin":"muutama sekunti";case"ss":r=a?"sekunnin":"sekuntia";break;case"m":return a?"minuutin":"minuutti";case"mm":r=a?"minuutin":"minuuttia";break;case"h":return a?"tunnin":"tunti";case"hh":r=a?"tunnin":"tuntia";break;case"d":return a?"pƤivƤn":"pƤivƤ";case"dd":r=a?"pƤivƤn":"pƤivƤƤ";break;case"M":return a?"kuukauden":"kuukausi";case"MM":r=a?"kuukauden":"kuukautta";break;case"y":return a?"vuoden":"vuosi";case"yy":r=a?"vuoden":"vuotta";break}r=o(e,a)+" "+r;return r}function o(e,t){return e<10?t?a[e]:n[e]:e}e.defineLocale("fi",{months:"tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesƤkuu_heinƤkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),monthsShort:"tammi_helmi_maalis_huhti_touko_kesƤ_heinƤ_elo_syys_loka_marras_joulu".split("_"),weekdays:"sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),weekdaysShort:"su_ma_ti_ke_to_pe_la".split("_"),weekdaysMin:"su_ma_ti_ke_to_pe_la".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"Do MMMM[ta] YYYY",LLL:"Do MMMM[ta] YYYY, [klo] HH.mm",LLLL:"dddd, Do MMMM[ta] YYYY, [klo] HH.mm",l:"D.M.YYYY",ll:"Do MMM YYYY",lll:"Do MMM YYYY, [klo] HH.mm",llll:"ddd, Do MMM YYYY, [klo] HH.mm"},calendar:{sameDay:"[tƤnƤƤn] [klo] LT",nextDay:"[huomenna] [klo] LT",nextWeek:"dddd [klo] LT",lastDay:"[eilen] [klo] LT",lastWeek:"[viime] dddd[na] [klo] LT",sameElse:"L"},relativeTime:{future:"%s pƤƤstƤ",past:"%s sitten",s:r,ss:r,m:r,mm:r,h:r,hh:r,d:r,dd:r,M:r,MM:r,y:r,yy:r},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var n="nolla yksi kaksi kolme neljƤ viisi kuusi seitsemƤn kahdeksan yhdeksƤn".split(" "),a=["nolla","yhden","kahden","kolmen","neljƤn","viiden","kuuden",n[7],n[8],n[9]],t;function r(e,t,n,a){var r="";switch(n){case"s":return a?"muutaman sekunnin":"muutama sekunti";case"ss":r=a?"sekunnin":"sekuntia";break;case"m":return a?"minuutin":"minuutti";case"mm":r=a?"minuutin":"minuuttia";break;case"h":return a?"tunnin":"tunti";case"hh":r=a?"tunnin":"tuntia";break;case"d":return a?"pƤivƤn":"pƤivƤ";case"dd":r=a?"pƤivƤn":"pƤivƤƤ";break;case"M":return a?"kuukauden":"kuukausi";case"MM":r=a?"kuukauden":"kuukautta";break;case"y":return a?"vuoden":"vuosi";case"yy":r=a?"vuoden":"vuotta";break}r=o(e,a)+" "+r;return r}function o(e,t){return e<10?t?a[e]:n[e]:e}e.defineLocale("fi",{months:"tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesƤkuu_heinƤkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),monthsShort:"tammi_helmi_maalis_huhti_touko_kesƤ_heinƤ_elo_syys_loka_marras_joulu".split("_"),weekdays:"sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),weekdaysShort:"su_ma_ti_ke_to_pe_la".split("_"),weekdaysMin:"su_ma_ti_ke_to_pe_la".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"Do MMMM[ta] YYYY",LLL:"Do MMMM[ta] YYYY, [klo] HH.mm",LLLL:"dddd, Do MMMM[ta] YYYY, [klo] HH.mm",l:"D.M.YYYY",ll:"Do MMM YYYY",lll:"Do MMM YYYY, [klo] HH.mm",llll:"ddd, Do MMM YYYY, [klo] HH.mm"},calendar:{sameDay:"[tƤnƤƤn] [klo] LT",nextDay:"[huomenna] [klo] LT",nextWeek:"dddd [klo] LT",lastDay:"[eilen] [klo] LT",lastWeek:"[viime] dddd[na] [klo] LT",sameElse:"L"},relativeTime:{future:"%s pƤƤstƤ",past:"%s sitten",s:r,ss:r,m:r,mm:r,h:r,hh:r,d:r,dd:r,M:r,MM:r,y:r,yy:r},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("fil",{months:"Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre".split("_"),monthsShort:"Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis".split("_"),weekdays:"Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado".split("_"),weekdaysShort:"Lin_Lun_Mar_Miy_Huw_Biy_Sab".split("_"),weekdaysMin:"Li_Lu_Ma_Mi_Hu_Bi_Sab".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"MM/D/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY HH:mm",LLLL:"dddd, MMMM DD, YYYY HH:mm"},calendar:{sameDay:"LT [ngayong araw]",nextDay:"[Bukas ng] LT",nextWeek:"LT [sa susunod na] dddd",lastDay:"LT [kahapon]",lastWeek:"LT [noong nakaraang] dddd",sameElse:"L"},relativeTime:{future:"sa loob ng %s",past:"%s ang nakalipas",s:"ilang segundo",ss:"%d segundo",m:"isang minuto",mm:"%d minuto",h:"isang oras",hh:"%d oras",d:"isang araw",dd:"%d araw",M:"isang buwan",MM:"%d buwan",y:"isang taon",yy:"%d taon"},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:function(e){return e},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("fil",{months:"Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre".split("_"),monthsShort:"Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis".split("_"),weekdays:"Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado".split("_"),weekdaysShort:"Lin_Lun_Mar_Miy_Huw_Biy_Sab".split("_"),weekdaysMin:"Li_Lu_Ma_Mi_Hu_Bi_Sab".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"MM/D/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY HH:mm",LLLL:"dddd, MMMM DD, YYYY HH:mm"},calendar:{sameDay:"LT [ngayong araw]",nextDay:"[Bukas ng] LT",nextWeek:"LT [sa susunod na] dddd",lastDay:"LT [kahapon]",lastWeek:"LT [noong nakaraang] dddd",sameElse:"L"},relativeTime:{future:"sa loob ng %s",past:"%s ang nakalipas",s:"ilang segundo",ss:"%d segundo",m:"isang minuto",mm:"%d minuto",h:"isang oras",hh:"%d oras",d:"isang araw",dd:"%d araw",M:"isang buwan",MM:"%d buwan",y:"isang taon",yy:"%d taon"},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:function(e){return e},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("fo",{months:"januar_februar_mars_aprĆ­l_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"sunnudagur_mĆ”nadagur_tĆ½sdagur_mikudagur_hĆ³sdagur_frĆ­ggjadagur_leygardagur".split("_"),weekdaysShort:"sun_mĆ”n_tĆ½s_mik_hĆ³s_frĆ­_ley".split("_"),weekdaysMin:"su_mĆ”_tĆ½_mi_hĆ³_fr_le".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D. MMMM, YYYY HH:mm"},calendar:{sameDay:"[ƍ dag kl.] LT",nextDay:"[ƍ morgin kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[ƍ gjĆ”r kl.] LT",lastWeek:"[sĆ­Ć°stu] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"um %s",past:"%s sĆ­Ć°ani",s:"fĆ” sekund",ss:"%d sekundir",m:"ein minuttur",mm:"%d minuttir",h:"ein tĆ­mi",hh:"%d tĆ­mar",d:"ein dagur",dd:"%d dagar",M:"ein mĆ”naĆ°ur",MM:"%d mĆ”naĆ°ir",y:"eitt Ć”r",yy:"%d Ć”r"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("fo",{months:"januar_februar_mars_aprĆ­l_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"sunnudagur_mĆ”nadagur_tĆ½sdagur_mikudagur_hĆ³sdagur_frĆ­ggjadagur_leygardagur".split("_"),weekdaysShort:"sun_mĆ”n_tĆ½s_mik_hĆ³s_frĆ­_ley".split("_"),weekdaysMin:"su_mĆ”_tĆ½_mi_hĆ³_fr_le".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D. MMMM, YYYY HH:mm"},calendar:{sameDay:"[ƍ dag kl.] LT",nextDay:"[ƍ morgin kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[ƍ gjĆ”r kl.] LT",lastWeek:"[sĆ­Ć°stu] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"um %s",past:"%s sĆ­Ć°ani",s:"fĆ” sekund",ss:"%d sekundir",m:"ein minuttur",mm:"%d minuttir",h:"ein tĆ­mi",hh:"%d tĆ­mar",d:"ein dagur",dd:"%d dagar",M:"ein mĆ”naĆ°ur",MM:"%d mĆ”naĆ°ir",y:"eitt Ć”r",yy:"%d Ć”r"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t=/^(janvier|fĆ©vrier|mars|avril|mai|juin|juillet|aoĆ»t|septembre|octobre|novembre|dĆ©cembre)/i,n=/(janv\.?|fĆ©vr\.?|mars|avr\.?|mai|juin|juil\.?|aoĆ»t|sept\.?|oct\.?|nov\.?|dĆ©c\.?)/i,a=/(janv\.?|fĆ©vr\.?|mars|avr\.?|mai|juin|juil\.?|aoĆ»t|sept\.?|oct\.?|nov\.?|dĆ©c\.?|janvier|fĆ©vrier|mars|avril|mai|juin|juillet|aoĆ»t|septembre|octobre|novembre|dĆ©cembre)/i,r=[/^janv/i,/^fĆ©vr/i,/^mars/i,/^avr/i,/^mai/i,/^juin/i,/^juil/i,/^aoĆ»t/i,/^sept/i,/^oct/i,/^nov/i,/^dĆ©c/i],o;e.defineLocale("fr",{months:"janvier_fĆ©vrier_mars_avril_mai_juin_juillet_aoĆ»t_septembre_octobre_novembre_dĆ©cembre".split("_"),monthsShort:"janv._fĆ©vr._mars_avr._mai_juin_juil._aoĆ»t_sept._oct._nov._dĆ©c.".split("_"),monthsRegex:a,monthsShortRegex:a,monthsStrictRegex:t,monthsShortStrictRegex:n,monthsParse:r,longMonthsParse:r,shortMonthsParse:r,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"di_lu_ma_me_je_ve_sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourdā€™hui Ć ] LT",nextDay:"[Demain Ć ] LT",nextWeek:"dddd [Ć ] LT",lastDay:"[Hier Ć ] LT",lastWeek:"dddd [dernier Ć ] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",ss:"%d secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",w:"une semaine",ww:"%d semaines",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(er|)/,ordinal:function(e,t){switch(t){case"D":return e+(e===1?"er":"");default:case"M":case"Q":case"DDD":case"d":return e+(e===1?"er":"e");case"w":case"W":return e+(e===1?"re":"e")}},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t=/^(janvier|fĆ©vrier|mars|avril|mai|juin|juillet|aoĆ»t|septembre|octobre|novembre|dĆ©cembre)/i,n=/(janv\.?|fĆ©vr\.?|mars|avr\.?|mai|juin|juil\.?|aoĆ»t|sept\.?|oct\.?|nov\.?|dĆ©c\.?)/i,a=/(janv\.?|fĆ©vr\.?|mars|avr\.?|mai|juin|juil\.?|aoĆ»t|sept\.?|oct\.?|nov\.?|dĆ©c\.?|janvier|fĆ©vrier|mars|avril|mai|juin|juillet|aoĆ»t|septembre|octobre|novembre|dĆ©cembre)/i,r=[/^janv/i,/^fĆ©vr/i,/^mars/i,/^avr/i,/^mai/i,/^juin/i,/^juil/i,/^aoĆ»t/i,/^sept/i,/^oct/i,/^nov/i,/^dĆ©c/i],o;e.defineLocale("fr",{months:"janvier_fĆ©vrier_mars_avril_mai_juin_juillet_aoĆ»t_septembre_octobre_novembre_dĆ©cembre".split("_"),monthsShort:"janv._fĆ©vr._mars_avr._mai_juin_juil._aoĆ»t_sept._oct._nov._dĆ©c.".split("_"),monthsRegex:a,monthsShortRegex:a,monthsStrictRegex:t,monthsShortStrictRegex:n,monthsParse:r,longMonthsParse:r,shortMonthsParse:r,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"di_lu_ma_me_je_ve_sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourdā€™hui Ć ] LT",nextDay:"[Demain Ć ] LT",nextWeek:"dddd [Ć ] LT",lastDay:"[Hier Ć ] LT",lastWeek:"dddd [dernier Ć ] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",ss:"%d secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",w:"une semaine",ww:"%d semaines",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(er|)/,ordinal:function(e,t){switch(t){case"D":return e+(e===1?"er":"");default:case"M":case"Q":case"DDD":case"d":return e+(e===1?"er":"e");case"w":case"W":return e+(e===1?"re":"e")}},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("fr-ca",{months:"janvier_fĆ©vrier_mars_avril_mai_juin_juillet_aoĆ»t_septembre_octobre_novembre_dĆ©cembre".split("_"),monthsShort:"janv._fĆ©vr._mars_avr._mai_juin_juil._aoĆ»t_sept._oct._nov._dĆ©c.".split("_"),monthsParseExact:true,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"di_lu_ma_me_je_ve_sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourdā€™hui Ć ] LT",nextDay:"[Demain Ć ] LT",nextWeek:"dddd [Ć ] LT",lastDay:"[Hier Ć ] LT",lastWeek:"dddd [dernier Ć ] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",ss:"%d secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(er|e)/,ordinal:function(e,t){switch(t){default:case"M":case"Q":case"D":case"DDD":case"d":return e+(e===1?"er":"e");case"w":case"W":return e+(e===1?"re":"e")}}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("fr-ca",{months:"janvier_fĆ©vrier_mars_avril_mai_juin_juillet_aoĆ»t_septembre_octobre_novembre_dĆ©cembre".split("_"),monthsShort:"janv._fĆ©vr._mars_avr._mai_juin_juil._aoĆ»t_sept._oct._nov._dĆ©c.".split("_"),monthsParseExact:true,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"di_lu_ma_me_je_ve_sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourdā€™hui Ć ] LT",nextDay:"[Demain Ć ] LT",nextWeek:"dddd [Ć ] LT",lastDay:"[Hier Ć ] LT",lastWeek:"dddd [dernier Ć ] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",ss:"%d secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(er|e)/,ordinal:function(e,t){switch(t){default:case"M":case"Q":case"D":case"DDD":case"d":return e+(e===1?"er":"e");case"w":case"W":return e+(e===1?"re":"e")}}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("fr-ch",{months:"janvier_fĆ©vrier_mars_avril_mai_juin_juillet_aoĆ»t_septembre_octobre_novembre_dĆ©cembre".split("_"),monthsShort:"janv._fĆ©vr._mars_avr._mai_juin_juil._aoĆ»t_sept._oct._nov._dĆ©c.".split("_"),monthsParseExact:true,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"di_lu_ma_me_je_ve_sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourdā€™hui Ć ] LT",nextDay:"[Demain Ć ] LT",nextWeek:"dddd [Ć ] LT",lastDay:"[Hier Ć ] LT",lastWeek:"dddd [dernier Ć ] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",ss:"%d secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(er|e)/,ordinal:function(e,t){switch(t){default:case"M":case"Q":case"D":case"DDD":case"d":return e+(e===1?"er":"e");case"w":case"W":return e+(e===1?"re":"e")}},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("fr-ch",{months:"janvier_fĆ©vrier_mars_avril_mai_juin_juillet_aoĆ»t_septembre_octobre_novembre_dĆ©cembre".split("_"),monthsShort:"janv._fĆ©vr._mars_avr._mai_juin_juil._aoĆ»t_sept._oct._nov._dĆ©c.".split("_"),monthsParseExact:true,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"di_lu_ma_me_je_ve_sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourdā€™hui Ć ] LT",nextDay:"[Demain Ć ] LT",nextWeek:"dddd [Ć ] LT",lastDay:"[Hier Ć ] LT",lastWeek:"dddd [dernier Ć ] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",ss:"%d secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(er|e)/,ordinal:function(e,t){switch(t){default:case"M":case"Q":case"D":case"DDD":case"d":return e+(e===1?"er":"e");case"w":case"W":return e+(e===1?"re":"e")}},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var n="jan._feb._mrt._apr._mai_jun._jul._aug._sep._okt._nov._des.".split("_"),a="jan_feb_mrt_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),t;e.defineLocale("fy",{months:"jannewaris_febrewaris_maart_april_maaie_juny_july_augustus_septimber_oktober_novimber_desimber".split("_"),monthsShort:function(e,t){if(!e)return n;else if(/-MMM-/.test(t))return a[e.month()];else return n[e.month()]},monthsParseExact:true,weekdays:"snein_moandei_tiisdei_woansdei_tongersdei_freed_sneon".split("_"),weekdaysShort:"si._mo._ti._wo._to._fr._so.".split("_"),weekdaysMin:"Si_Mo_Ti_Wo_To_Fr_So".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[hjoed om] LT",nextDay:"[moarn om] LT",nextWeek:"dddd [om] LT",lastDay:"[juster om] LT",lastWeek:"[Ć“frĆ»ne] dddd [om] LT",sameElse:"L"},relativeTime:{future:"oer %s",past:"%s lyn",s:"in pear sekonden",ss:"%d sekonden",m:"ien minĆŗt",mm:"%d minuten",h:"ien oere",hh:"%d oeren",d:"ien dei",dd:"%d dagen",M:"ien moanne",MM:"%d moannen",y:"ien jier",yy:"%d jierren"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(e===1||e===8||e>=20?"ste":"de")},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var n="jan._feb._mrt._apr._mai_jun._jul._aug._sep._okt._nov._des.".split("_"),a="jan_feb_mrt_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),t;e.defineLocale("fy",{months:"jannewaris_febrewaris_maart_april_maaie_juny_july_augustus_septimber_oktober_novimber_desimber".split("_"),monthsShort:function(e,t){if(!e)return n;else if(/-MMM-/.test(t))return a[e.month()];else return n[e.month()]},monthsParseExact:true,weekdays:"snein_moandei_tiisdei_woansdei_tongersdei_freed_sneon".split("_"),weekdaysShort:"si._mo._ti._wo._to._fr._so.".split("_"),weekdaysMin:"Si_Mo_Ti_Wo_To_Fr_So".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[hjoed om] LT",nextDay:"[moarn om] LT",nextWeek:"dddd [om] LT",lastDay:"[juster om] LT",lastWeek:"[Ć“frĆ»ne] dddd [om] LT",sameElse:"L"},relativeTime:{future:"oer %s",past:"%s lyn",s:"in pear sekonden",ss:"%d sekonden",m:"ien minĆŗt",mm:"%d minuten",h:"ien oere",hh:"%d oeren",d:"ien dei",dd:"%d dagen",M:"ien moanne",MM:"%d moannen",y:"ien jier",yy:"%d jierren"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(e===1||e===8||e>=20?"ste":"de")},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t,n,a,r,o,i;e.defineLocale("ga",{months:["EanĆ”ir","Feabhra","MĆ”rta","AibreĆ”n","Bealtaine","Meitheamh","IĆŗil","LĆŗnasa","MeĆ”n FĆ³mhair","Deireadh FĆ³mhair","Samhain","Nollaig"],monthsShort:["Ean","Feabh","MĆ”rt","Aib","Beal","Meith","IĆŗil","LĆŗn","M.F.","D.F.","Samh","Noll"],monthsParseExact:true,weekdays:["DĆ© Domhnaigh","DĆ© Luain","DĆ© MĆ”irt","DĆ© CĆ©adaoin","DĆ©ardaoin","DĆ© hAoine","DĆ© Sathairn"],weekdaysShort:["Domh","Luan","MĆ”irt","CĆ©ad","DĆ©ar","Aoine","Sath"],weekdaysMin:["Do","Lu","MĆ”","CĆ©","DĆ©","A","Sa"],longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Inniu ag] LT",nextDay:"[AmĆ”rach ag] LT",nextWeek:"dddd [ag] LT",lastDay:"[InnĆ© ag] LT",lastWeek:"dddd [seo caite] [ag] LT",sameElse:"L"},relativeTime:{future:"i %s",past:"%s Ć³ shin",s:"cĆŗpla soicind",ss:"%d soicind",m:"nĆ³imĆ©ad",mm:"%d nĆ³imĆ©ad",h:"uair an chloig",hh:"%d uair an chloig",d:"lĆ”",dd:"%d lĆ”",M:"mĆ­",MM:"%d mĆ­onna",y:"bliain",yy:"%d bliain"},dayOfMonthOrdinalParse:/\d{1,2}(d|na|mh)/,ordinal:function(e){var t=e===1?"d":e%10===2?"na":"mh";return e+t},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t,n,a,r,o,i;e.defineLocale("ga",{months:["EanĆ”ir","Feabhra","MĆ”rta","AibreĆ”n","Bealtaine","Meitheamh","IĆŗil","LĆŗnasa","MeĆ”n FĆ³mhair","Deireadh FĆ³mhair","Samhain","Nollaig"],monthsShort:["Ean","Feabh","MĆ”rt","Aib","Beal","Meith","IĆŗil","LĆŗn","M.F.","D.F.","Samh","Noll"],monthsParseExact:true,weekdays:["DĆ© Domhnaigh","DĆ© Luain","DĆ© MĆ”irt","DĆ© CĆ©adaoin","DĆ©ardaoin","DĆ© hAoine","DĆ© Sathairn"],weekdaysShort:["Domh","Luan","MĆ”irt","CĆ©ad","DĆ©ar","Aoine","Sath"],weekdaysMin:["Do","Lu","MĆ”","CĆ©","DĆ©","A","Sa"],longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Inniu ag] LT",nextDay:"[AmĆ”rach ag] LT",nextWeek:"dddd [ag] LT",lastDay:"[InnĆ© ag] LT",lastWeek:"dddd [seo caite] [ag] LT",sameElse:"L"},relativeTime:{future:"i %s",past:"%s Ć³ shin",s:"cĆŗpla soicind",ss:"%d soicind",m:"nĆ³imĆ©ad",mm:"%d nĆ³imĆ©ad",h:"uair an chloig",hh:"%d uair an chloig",d:"lĆ”",dd:"%d lĆ”",M:"mĆ­",MM:"%d mĆ­onna",y:"bliain",yy:"%d bliain"},dayOfMonthOrdinalParse:/\d{1,2}(d|na|mh)/,ordinal:function(e){var t=e===1?"d":e%10===2?"na":"mh";return e+t},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t,n,a,r,o,i;e.defineLocale("gd",{months:["Am Faoilleach","An Gearran","Am MĆ rt","An Giblean","An CĆØitean","An t-ƒgmhios","An t-Iuchar","An LĆ¹nastal","An t-Sultain","An DĆ mhair","An t-Samhain","An DĆ¹bhlachd"],monthsShort:["Faoi","Gear","MĆ rt","Gibl","CĆØit","ƒgmh","Iuch","LĆ¹n","Sult","DĆ mh","Samh","DĆ¹bh"],monthsParseExact:true,weekdays:["DidĆ²mhnaich","Diluain","DimĆ irt","Diciadain","Diardaoin","Dihaoine","Disathairne"],weekdaysShort:["Did","Dil","Dim","Dic","Dia","Dih","Dis"],weekdaysMin:["DĆ²","Lu","MĆ ","Ci","Ar","Ha","Sa"],longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[An-diugh aig] LT",nextDay:"[A-mĆ ireach aig] LT",nextWeek:"dddd [aig] LT",lastDay:"[An-dĆØ aig] LT",lastWeek:"dddd [seo chaidh] [aig] LT",sameElse:"L"},relativeTime:{future:"ann an %s",past:"bho chionn %s",s:"beagan diogan",ss:"%d diogan",m:"mionaid",mm:"%d mionaidean",h:"uair",hh:"%d uairean",d:"latha",dd:"%d latha",M:"mƬos",MM:"%d mƬosan",y:"bliadhna",yy:"%d bliadhna"},dayOfMonthOrdinalParse:/\d{1,2}(d|na|mh)/,ordinal:function(e){var t=e===1?"d":e%10===2?"na":"mh";return e+t},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t,n,a,r,o,i;e.defineLocale("gd",{months:["Am Faoilleach","An Gearran","Am MĆ rt","An Giblean","An CĆØitean","An t-ƒgmhios","An t-Iuchar","An LĆ¹nastal","An t-Sultain","An DĆ mhair","An t-Samhain","An DĆ¹bhlachd"],monthsShort:["Faoi","Gear","MĆ rt","Gibl","CĆØit","ƒgmh","Iuch","LĆ¹n","Sult","DĆ mh","Samh","DĆ¹bh"],monthsParseExact:true,weekdays:["DidĆ²mhnaich","Diluain","DimĆ irt","Diciadain","Diardaoin","Dihaoine","Disathairne"],weekdaysShort:["Did","Dil","Dim","Dic","Dia","Dih","Dis"],weekdaysMin:["DĆ²","Lu","MĆ ","Ci","Ar","Ha","Sa"],longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[An-diugh aig] LT",nextDay:"[A-mĆ ireach aig] LT",nextWeek:"dddd [aig] LT",lastDay:"[An-dĆØ aig] LT",lastWeek:"dddd [seo chaidh] [aig] LT",sameElse:"L"},relativeTime:{future:"ann an %s",past:"bho chionn %s",s:"beagan diogan",ss:"%d diogan",m:"mionaid",mm:"%d mionaidean",h:"uair",hh:"%d uairean",d:"latha",dd:"%d latha",M:"mƬos",MM:"%d mƬosan",y:"bliadhna",yy:"%d bliadhna"},dayOfMonthOrdinalParse:/\d{1,2}(d|na|mh)/,ordinal:function(e){var t=e===1?"d":e%10===2?"na":"mh";return e+t},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("gl",{months:"xaneiro_febreiro_marzo_abril_maio_xuƱo_xullo_agosto_setembro_outubro_novembro_decembro".split("_"),monthsShort:"xan._feb._mar._abr._mai._xuƱ._xul._ago._set._out._nov._dec.".split("_"),monthsParseExact:true,weekdays:"domingo_luns_martes_mĆ©rcores_xoves_venres_sĆ”bado".split("_"),weekdaysShort:"dom._lun._mar._mĆ©r._xov._ven._sĆ”b.".split("_"),weekdaysMin:"do_lu_ma_mĆ©_xo_ve_sĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoxe "+(this.hours()!==1?"Ć”s":"Ć”")+"] LT"},nextDay:function(){return"[maƱƔ "+(this.hours()!==1?"Ć”s":"Ć”")+"] LT"},nextWeek:function(){return"dddd ["+(this.hours()!==1?"Ć”s":"a")+"] LT"},lastDay:function(){return"[onte "+(this.hours()!==1?"Ć”":"a")+"] LT"},lastWeek:function(){return"[o] dddd [pasado "+(this.hours()!==1?"Ć”s":"a")+"] LT"},sameElse:"L"},relativeTime:{future:function(e){if(e.indexOf("un")===0)return"n"+e;return"en "+e},past:"hai %s",s:"uns segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"unha hora",hh:"%d horas",d:"un dĆ­a",dd:"%d dĆ­as",M:"un mes",MM:"%d meses",y:"un ano",yy:"%d anos"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("gl",{months:"xaneiro_febreiro_marzo_abril_maio_xuƱo_xullo_agosto_setembro_outubro_novembro_decembro".split("_"),monthsShort:"xan._feb._mar._abr._mai._xuƱ._xul._ago._set._out._nov._dec.".split("_"),monthsParseExact:true,weekdays:"domingo_luns_martes_mĆ©rcores_xoves_venres_sĆ”bado".split("_"),weekdaysShort:"dom._lun._mar._mĆ©r._xov._ven._sĆ”b.".split("_"),weekdaysMin:"do_lu_ma_mĆ©_xo_ve_sĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoxe "+(this.hours()!==1?"Ć”s":"Ć”")+"] LT"},nextDay:function(){return"[maƱƔ "+(this.hours()!==1?"Ć”s":"Ć”")+"] LT"},nextWeek:function(){return"dddd ["+(this.hours()!==1?"Ć”s":"a")+"] LT"},lastDay:function(){return"[onte "+(this.hours()!==1?"Ć”":"a")+"] LT"},lastWeek:function(){return"[o] dddd [pasado "+(this.hours()!==1?"Ć”s":"a")+"] LT"},sameElse:"L"},relativeTime:{future:function(e){if(e.indexOf("un")===0)return"n"+e;return"en "+e},past:"hai %s",s:"uns segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"unha hora",hh:"%d horas",d:"un dĆ­a",dd:"%d dĆ­as",M:"un mes",MM:"%d meses",y:"un ano",yy:"%d anos"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function t(e,t,n,a){var r={s:["ą¤„ą„‹ą¤”ą¤Æą¤¾ ą¤øą„…ą¤•ą¤‚ą¤”ą¤¾ą¤‚ą¤Øą„€","ą¤„ą„‹ą¤”ą„‡ ą¤øą„…ą¤•ą¤‚ą¤”"],ss:[e+" ą¤øą„…ą¤•ą¤‚ą¤”ą¤¾ą¤‚ą¤Øą„€",e+" ą¤øą„…ą¤•ą¤‚ą¤”"],m:["ą¤ą¤•ą¤¾ ą¤®ą¤æą¤£ą¤Ÿą¤¾ą¤Ø","ą¤ą¤• ą¤®ą¤æą¤Øą„‚ą¤Ÿ"],mm:[e+" ą¤®ą¤æą¤£ą¤Ÿą¤¾ą¤‚ą¤Øą„€",e+" ą¤®ą¤æą¤£ą¤Ÿą¤¾ą¤‚"],h:["ą¤ą¤•ą¤¾ ą¤µą¤°ą¤¾ą¤Ø","ą¤ą¤• ą¤µą¤°"],hh:[e+" ą¤µą¤°ą¤¾ą¤‚ą¤Øą„€",e+" ą¤µą¤°ą¤¾ą¤‚"],d:["ą¤ą¤•ą¤¾ ą¤¦ą¤æą¤øą¤¾ą¤Ø","ą¤ą¤• ą¤¦ą„€ą¤ø"],dd:[e+" ą¤¦ą¤æą¤øą¤¾ą¤‚ą¤Øą„€",e+" ą¤¦ą„€ą¤ø"],M:["ą¤ą¤•ą¤¾ ą¤®ą„ą¤¹ą¤Æą¤Øą„ą¤Æą¤¾ą¤Ø","ą¤ą¤• ą¤®ą„ą¤¹ą¤Æą¤Øą„‹"],MM:[e+" ą¤®ą„ą¤¹ą¤Æą¤Øą„ą¤Æą¤¾ą¤Øą„€",e+" ą¤®ą„ą¤¹ą¤Æą¤Øą„‡"],y:["ą¤ą¤•ą¤¾ ą¤µą¤°ą„ą¤øą¤¾ą¤Ø","ą¤ą¤• ą¤µą¤°ą„ą¤ø"],yy:[e+" ą¤µą¤°ą„ą¤øą¤¾ą¤‚ą¤Øą„€",e+" ą¤µą¤°ą„ą¤øą¤¾ą¤‚"]};return a?r[n][0]:r[n][1]}var n;e.defineLocale("gom-deva",{months:{standalone:"ą¤œą¤¾ą¤Øą„‡ą¤µą¤¾ą¤°ą„€_ą¤«ą„‡ą¤¬ą„ą¤°ą„ą¤µą¤¾ą¤°ą„€_ą¤®ą¤¾ą¤°ą„ą¤š_ą¤ą¤Ŗą„ą¤°ą„€ą¤²_ą¤®ą„‡_ą¤œą„‚ą¤Ø_ą¤œą„ą¤²ą¤Æ_ą¤‘ą¤—ą¤øą„ą¤Ÿ_ą¤øą¤Ŗą„ą¤Ÿą„‡ą¤‚ą¤¬ą¤°_ą¤‘ą¤•ą„ą¤Ÿą„‹ą¤¬ą¤°_ą¤Øą„‹ą¤µą„ą¤¹ą„‡ą¤‚ą¤¬ą¤°_ą¤”ą¤æą¤øą„‡ą¤‚ą¤¬ą¤°".split("_"),format:"ą¤œą¤¾ą¤Øą„‡ą¤µą¤¾ą¤°ą„€ą¤šą„ą¤Æą¤¾_ą¤«ą„‡ą¤¬ą„ą¤°ą„ą¤µą¤¾ą¤°ą„€ą¤šą„ą¤Æą¤¾_ą¤®ą¤¾ą¤°ą„ą¤šą¤¾ą¤šą„ą¤Æą¤¾_ą¤ą¤Ŗą„ą¤°ą„€ą¤²ą¤¾ą¤šą„ą¤Æą¤¾_ą¤®ą„‡ą¤Æą¤¾ą¤šą„ą¤Æą¤¾_ą¤œą„‚ą¤Øą¤¾ą¤šą„ą¤Æą¤¾_ą¤œą„ą¤²ą¤Æą¤¾ą¤šą„ą¤Æą¤¾_ą¤‘ą¤—ą¤øą„ą¤Ÿą¤¾ą¤šą„ą¤Æą¤¾_ą¤øą¤Ŗą„ą¤Ÿą„‡ą¤‚ą¤¬ą¤°ą¤¾ą¤šą„ą¤Æą¤¾_ą¤‘ą¤•ą„ą¤Ÿą„‹ą¤¬ą¤°ą¤¾ą¤šą„ą¤Æą¤¾_ą¤Øą„‹ą¤µą„ą¤¹ą„‡ą¤‚ą¤¬ą¤°ą¤¾ą¤šą„ą¤Æą¤¾_ą¤”ą¤æą¤øą„‡ą¤‚ą¤¬ą¤°ą¤¾ą¤šą„ą¤Æą¤¾".split("_"),isFormat:/MMMM(\s)+D[oD]?/},monthsShort:"ą¤œą¤¾ą¤Øą„‡._ą¤«ą„‡ą¤¬ą„ą¤°ą„._ą¤®ą¤¾ą¤°ą„ą¤š_ą¤ą¤Ŗą„ą¤°ą„€._ą¤®ą„‡_ą¤œą„‚ą¤Ø_ą¤œą„ą¤²._ą¤‘ą¤—._ą¤øą¤Ŗą„ą¤Ÿą„‡ą¤‚._ą¤‘ą¤•ą„ą¤Ÿą„‹._ą¤Øą„‹ą¤µą„ą¤¹ą„‡ą¤‚._ą¤”ą¤æą¤øą„‡ą¤‚.".split("_"),monthsParseExact:true,weekdays:"ą¤†ą¤Æą¤¤ą¤¾ą¤°_ą¤øą„‹ą¤®ą¤¾ą¤°_ą¤®ą¤‚ą¤—ą¤³ą¤¾ą¤°_ą¤¬ą„ą¤§ą¤µą¤¾ą¤°_ą¤¬ą¤æą¤°ą„‡ą¤øą„ą¤¤ą¤¾ą¤°_ą¤øą„ą¤•ą„ą¤°ą¤¾ą¤°_ą¤¶ą„‡ą¤Øą¤µą¤¾ą¤°".split("_"),weekdaysShort:"ą¤†ą¤Æą¤¤._ą¤øą„‹ą¤®._ą¤®ą¤‚ą¤—ą¤³._ą¤¬ą„ą¤§._ą¤¬ą„ą¤°ą„‡ą¤øą„ą¤¤._ą¤øą„ą¤•ą„ą¤°._ą¤¶ą„‡ą¤Ø.".split("_"),weekdaysMin:"ą¤†_ą¤øą„‹_ą¤®ą¤‚_ą¤¬ą„_ą¤¬ą„ą¤°ą„‡_ą¤øą„_ą¤¶ą„‡".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"A h:mm [ą¤µą¤¾ą¤œą¤¤ą¤¾ą¤‚]",LTS:"A h:mm:ss [ą¤µą¤¾ą¤œą¤¤ą¤¾ą¤‚]",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY A h:mm [ą¤µą¤¾ą¤œą¤¤ą¤¾ą¤‚]",LLLL:"dddd, MMMM Do, YYYY, A h:mm [ą¤µą¤¾ą¤œą¤¤ą¤¾ą¤‚]",llll:"ddd, D MMM YYYY, A h:mm [ą¤µą¤¾ą¤œą¤¤ą¤¾ą¤‚]"},calendar:{sameDay:"[ą¤†ą¤Æą¤œ] LT",nextDay:"[ą¤«ą¤¾ą¤²ą„ą¤Æą¤¾ą¤‚] LT",nextWeek:"[ą¤«ą„ą¤”ą¤²ą„‹] dddd[,] LT",lastDay:"[ą¤•ą¤¾ą¤²] LT",lastWeek:"[ą¤«ą¤¾ą¤Ÿą¤²ą„‹] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%s",past:"%s ą¤†ą¤¦ą„€ą¤‚",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}(ą¤µą„‡ą¤°)/,ordinal:function(e,t){switch(t){case"D":return e+"ą¤µą„‡ą¤°";default:case"M":case"Q":case"DDD":case"d":case"w":case"W":return e}},week:{dow:0,doy:3},meridiemParse:/ą¤°ą¤¾ą¤¤ą„€|ą¤øą¤•ą¤¾ą¤³ą„€ą¤‚|ą¤¦ą¤Øą¤Ŗą¤¾ą¤°ą¤¾ą¤‚|ą¤øą¤¾ą¤‚ą¤œą„‡/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą¤°ą¤¾ą¤¤ą„€")return e<4?e:e+12;else if(t==="ą¤øą¤•ą¤¾ą¤³ą„€ą¤‚")return e;else if(t==="ą¤¦ą¤Øą¤Ŗą¤¾ą¤°ą¤¾ą¤‚")return e>12?e:e+12;else if(t==="ą¤øą¤¾ą¤‚ą¤œą„‡")return e+12},meridiem:function(e,t,n){if(e<4)return"ą¤°ą¤¾ą¤¤ą„€";else if(e<12)return"ą¤øą¤•ą¤¾ą¤³ą„€ą¤‚";else if(e<16)return"ą¤¦ą¤Øą¤Ŗą¤¾ą¤°ą¤¾ą¤‚";else if(e<20)return"ą¤øą¤¾ą¤‚ą¤œą„‡";else return"ą¤°ą¤¾ą¤¤ą„€"}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function t(e,t,n,a){var r={s:["ą¤„ą„‹ą¤”ą¤Æą¤¾ ą¤øą„…ą¤•ą¤‚ą¤”ą¤¾ą¤‚ą¤Øą„€","ą¤„ą„‹ą¤”ą„‡ ą¤øą„…ą¤•ą¤‚ą¤”"],ss:[e+" ą¤øą„…ą¤•ą¤‚ą¤”ą¤¾ą¤‚ą¤Øą„€",e+" ą¤øą„…ą¤•ą¤‚ą¤”"],m:["ą¤ą¤•ą¤¾ ą¤®ą¤æą¤£ą¤Ÿą¤¾ą¤Ø","ą¤ą¤• ą¤®ą¤æą¤Øą„‚ą¤Ÿ"],mm:[e+" ą¤®ą¤æą¤£ą¤Ÿą¤¾ą¤‚ą¤Øą„€",e+" ą¤®ą¤æą¤£ą¤Ÿą¤¾ą¤‚"],h:["ą¤ą¤•ą¤¾ ą¤µą¤°ą¤¾ą¤Ø","ą¤ą¤• ą¤µą¤°"],hh:[e+" ą¤µą¤°ą¤¾ą¤‚ą¤Øą„€",e+" ą¤µą¤°ą¤¾ą¤‚"],d:["ą¤ą¤•ą¤¾ ą¤¦ą¤æą¤øą¤¾ą¤Ø","ą¤ą¤• ą¤¦ą„€ą¤ø"],dd:[e+" ą¤¦ą¤æą¤øą¤¾ą¤‚ą¤Øą„€",e+" ą¤¦ą„€ą¤ø"],M:["ą¤ą¤•ą¤¾ ą¤®ą„ą¤¹ą¤Æą¤Øą„ą¤Æą¤¾ą¤Ø","ą¤ą¤• ą¤®ą„ą¤¹ą¤Æą¤Øą„‹"],MM:[e+" ą¤®ą„ą¤¹ą¤Æą¤Øą„ą¤Æą¤¾ą¤Øą„€",e+" ą¤®ą„ą¤¹ą¤Æą¤Øą„‡"],y:["ą¤ą¤•ą¤¾ ą¤µą¤°ą„ą¤øą¤¾ą¤Ø","ą¤ą¤• ą¤µą¤°ą„ą¤ø"],yy:[e+" ą¤µą¤°ą„ą¤øą¤¾ą¤‚ą¤Øą„€",e+" ą¤µą¤°ą„ą¤øą¤¾ą¤‚"]};return a?r[n][0]:r[n][1]}var n;e.defineLocale("gom-deva",{months:{standalone:"ą¤œą¤¾ą¤Øą„‡ą¤µą¤¾ą¤°ą„€_ą¤«ą„‡ą¤¬ą„ą¤°ą„ą¤µą¤¾ą¤°ą„€_ą¤®ą¤¾ą¤°ą„ą¤š_ą¤ą¤Ŗą„ą¤°ą„€ą¤²_ą¤®ą„‡_ą¤œą„‚ą¤Ø_ą¤œą„ą¤²ą¤Æ_ą¤‘ą¤—ą¤øą„ą¤Ÿ_ą¤øą¤Ŗą„ą¤Ÿą„‡ą¤‚ą¤¬ą¤°_ą¤‘ą¤•ą„ą¤Ÿą„‹ą¤¬ą¤°_ą¤Øą„‹ą¤µą„ą¤¹ą„‡ą¤‚ą¤¬ą¤°_ą¤”ą¤æą¤øą„‡ą¤‚ą¤¬ą¤°".split("_"),format:"ą¤œą¤¾ą¤Øą„‡ą¤µą¤¾ą¤°ą„€ą¤šą„ą¤Æą¤¾_ą¤«ą„‡ą¤¬ą„ą¤°ą„ą¤µą¤¾ą¤°ą„€ą¤šą„ą¤Æą¤¾_ą¤®ą¤¾ą¤°ą„ą¤šą¤¾ą¤šą„ą¤Æą¤¾_ą¤ą¤Ŗą„ą¤°ą„€ą¤²ą¤¾ą¤šą„ą¤Æą¤¾_ą¤®ą„‡ą¤Æą¤¾ą¤šą„ą¤Æą¤¾_ą¤œą„‚ą¤Øą¤¾ą¤šą„ą¤Æą¤¾_ą¤œą„ą¤²ą¤Æą¤¾ą¤šą„ą¤Æą¤¾_ą¤‘ą¤—ą¤øą„ą¤Ÿą¤¾ą¤šą„ą¤Æą¤¾_ą¤øą¤Ŗą„ą¤Ÿą„‡ą¤‚ą¤¬ą¤°ą¤¾ą¤šą„ą¤Æą¤¾_ą¤‘ą¤•ą„ą¤Ÿą„‹ą¤¬ą¤°ą¤¾ą¤šą„ą¤Æą¤¾_ą¤Øą„‹ą¤µą„ą¤¹ą„‡ą¤‚ą¤¬ą¤°ą¤¾ą¤šą„ą¤Æą¤¾_ą¤”ą¤æą¤øą„‡ą¤‚ą¤¬ą¤°ą¤¾ą¤šą„ą¤Æą¤¾".split("_"),isFormat:/MMMM(\s)+D[oD]?/},monthsShort:"ą¤œą¤¾ą¤Øą„‡._ą¤«ą„‡ą¤¬ą„ą¤°ą„._ą¤®ą¤¾ą¤°ą„ą¤š_ą¤ą¤Ŗą„ą¤°ą„€._ą¤®ą„‡_ą¤œą„‚ą¤Ø_ą¤œą„ą¤²._ą¤‘ą¤—._ą¤øą¤Ŗą„ą¤Ÿą„‡ą¤‚._ą¤‘ą¤•ą„ą¤Ÿą„‹._ą¤Øą„‹ą¤µą„ą¤¹ą„‡ą¤‚._ą¤”ą¤æą¤øą„‡ą¤‚.".split("_"),monthsParseExact:true,weekdays:"ą¤†ą¤Æą¤¤ą¤¾ą¤°_ą¤øą„‹ą¤®ą¤¾ą¤°_ą¤®ą¤‚ą¤—ą¤³ą¤¾ą¤°_ą¤¬ą„ą¤§ą¤µą¤¾ą¤°_ą¤¬ą¤æą¤°ą„‡ą¤øą„ą¤¤ą¤¾ą¤°_ą¤øą„ą¤•ą„ą¤°ą¤¾ą¤°_ą¤¶ą„‡ą¤Øą¤µą¤¾ą¤°".split("_"),weekdaysShort:"ą¤†ą¤Æą¤¤._ą¤øą„‹ą¤®._ą¤®ą¤‚ą¤—ą¤³._ą¤¬ą„ą¤§._ą¤¬ą„ą¤°ą„‡ą¤øą„ą¤¤._ą¤øą„ą¤•ą„ą¤°._ą¤¶ą„‡ą¤Ø.".split("_"),weekdaysMin:"ą¤†_ą¤øą„‹_ą¤®ą¤‚_ą¤¬ą„_ą¤¬ą„ą¤°ą„‡_ą¤øą„_ą¤¶ą„‡".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"A h:mm [ą¤µą¤¾ą¤œą¤¤ą¤¾ą¤‚]",LTS:"A h:mm:ss [ą¤µą¤¾ą¤œą¤¤ą¤¾ą¤‚]",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY A h:mm [ą¤µą¤¾ą¤œą¤¤ą¤¾ą¤‚]",LLLL:"dddd, MMMM Do, YYYY, A h:mm [ą¤µą¤¾ą¤œą¤¤ą¤¾ą¤‚]",llll:"ddd, D MMM YYYY, A h:mm [ą¤µą¤¾ą¤œą¤¤ą¤¾ą¤‚]"},calendar:{sameDay:"[ą¤†ą¤Æą¤œ] LT",nextDay:"[ą¤«ą¤¾ą¤²ą„ą¤Æą¤¾ą¤‚] LT",nextWeek:"[ą¤«ą„ą¤”ą¤²ą„‹] dddd[,] LT",lastDay:"[ą¤•ą¤¾ą¤²] LT",lastWeek:"[ą¤«ą¤¾ą¤Ÿą¤²ą„‹] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%s",past:"%s ą¤†ą¤¦ą„€ą¤‚",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}(ą¤µą„‡ą¤°)/,ordinal:function(e,t){switch(t){case"D":return e+"ą¤µą„‡ą¤°";default:case"M":case"Q":case"DDD":case"d":case"w":case"W":return e}},week:{dow:0,doy:3},meridiemParse:/ą¤°ą¤¾ą¤¤ą„€|ą¤øą¤•ą¤¾ą¤³ą„€ą¤‚|ą¤¦ą¤Øą¤Ŗą¤¾ą¤°ą¤¾ą¤‚|ą¤øą¤¾ą¤‚ą¤œą„‡/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą¤°ą¤¾ą¤¤ą„€")return e<4?e:e+12;else if(t==="ą¤øą¤•ą¤¾ą¤³ą„€ą¤‚")return e;else if(t==="ą¤¦ą¤Øą¤Ŗą¤¾ą¤°ą¤¾ą¤‚")return e>12?e:e+12;else if(t==="ą¤øą¤¾ą¤‚ą¤œą„‡")return e+12},meridiem:function(e,t,n){if(e<4)return"ą¤°ą¤¾ą¤¤ą„€";else if(e<12)return"ą¤øą¤•ą¤¾ą¤³ą„€ą¤‚";else if(e<16)return"ą¤¦ą¤Øą¤Ŗą¤¾ą¤°ą¤¾ą¤‚";else if(e<20)return"ą¤øą¤¾ą¤‚ą¤œą„‡";else return"ą¤°ą¤¾ą¤¤ą„€"}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function t(e,t,n,a){var r={s:["thoddea sekondamni","thodde sekond"],ss:[e+" sekondamni",e+" sekond"],m:["eka mintan","ek minut"],mm:[e+" mintamni",e+" mintam"],h:["eka voran","ek vor"],hh:[e+" voramni",e+" voram"],d:["eka disan","ek dis"],dd:[e+" disamni",e+" dis"],M:["eka mhoinean","ek mhoino"],MM:[e+" mhoineamni",e+" mhoine"],y:["eka vorsan","ek voros"],yy:[e+" vorsamni",e+" vorsam"]};return a?r[n][0]:r[n][1]}var n;e.defineLocale("gom-latn",{months:{standalone:"Janer_Febrer_Mars_Abril_Mai_Jun_Julai_Agost_Setembr_Otubr_Novembr_Dezembr".split("_"),format:"Janerachea_Febrerachea_Marsachea_Abrilachea_Maiachea_Junachea_Julaiachea_Agostachea_Setembrachea_Otubrachea_Novembrachea_Dezembrachea".split("_"),isFormat:/MMMM(\s)+D[oD]?/},monthsShort:"Jan._Feb._Mars_Abr._Mai_Jun_Jul._Ago._Set._Otu._Nov._Dez.".split("_"),monthsParseExact:true,weekdays:"Aitar_Somar_Mongllar_Budhvar_Birestar_Sukrar_Son'var".split("_"),weekdaysShort:"Ait._Som._Mon._Bud._Bre._Suk._Son.".split("_"),weekdaysMin:"Ai_Sm_Mo_Bu_Br_Su_Sn".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"A h:mm [vazta]",LTS:"A h:mm:ss [vazta]",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY A h:mm [vazta]",LLLL:"dddd, MMMM Do, YYYY, A h:mm [vazta]",llll:"ddd, D MMM YYYY, A h:mm [vazta]"},calendar:{sameDay:"[Aiz] LT",nextDay:"[Faleam] LT",nextWeek:"[Fuddlo] dddd[,] LT",lastDay:"[Kal] LT",lastWeek:"[Fattlo] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%s",past:"%s adim",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}(er)/,ordinal:function(e,t){switch(t){case"D":return e+"er";default:case"M":case"Q":case"DDD":case"d":case"w":case"W":return e}},week:{dow:0,doy:3},meridiemParse:/rati|sokallim|donparam|sanje/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="rati")return e<4?e:e+12;else if(t==="sokallim")return e;else if(t==="donparam")return e>12?e:e+12;else if(t==="sanje")return e+12},meridiem:function(e,t,n){if(e<4)return"rati";else if(e<12)return"sokallim";else if(e<16)return"donparam";else if(e<20)return"sanje";else return"rati"}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function t(e,t,n,a){var r={s:["thoddea sekondamni","thodde sekond"],ss:[e+" sekondamni",e+" sekond"],m:["eka mintan","ek minut"],mm:[e+" mintamni",e+" mintam"],h:["eka voran","ek vor"],hh:[e+" voramni",e+" voram"],d:["eka disan","ek dis"],dd:[e+" disamni",e+" dis"],M:["eka mhoinean","ek mhoino"],MM:[e+" mhoineamni",e+" mhoine"],y:["eka vorsan","ek voros"],yy:[e+" vorsamni",e+" vorsam"]};return a?r[n][0]:r[n][1]}var n;e.defineLocale("gom-latn",{months:{standalone:"Janer_Febrer_Mars_Abril_Mai_Jun_Julai_Agost_Setembr_Otubr_Novembr_Dezembr".split("_"),format:"Janerachea_Febrerachea_Marsachea_Abrilachea_Maiachea_Junachea_Julaiachea_Agostachea_Setembrachea_Otubrachea_Novembrachea_Dezembrachea".split("_"),isFormat:/MMMM(\s)+D[oD]?/},monthsShort:"Jan._Feb._Mars_Abr._Mai_Jun_Jul._Ago._Set._Otu._Nov._Dez.".split("_"),monthsParseExact:true,weekdays:"Aitar_Somar_Mongllar_Budhvar_Birestar_Sukrar_Son'var".split("_"),weekdaysShort:"Ait._Som._Mon._Bud._Bre._Suk._Son.".split("_"),weekdaysMin:"Ai_Sm_Mo_Bu_Br_Su_Sn".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"A h:mm [vazta]",LTS:"A h:mm:ss [vazta]",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY A h:mm [vazta]",LLLL:"dddd, MMMM Do, YYYY, A h:mm [vazta]",llll:"ddd, D MMM YYYY, A h:mm [vazta]"},calendar:{sameDay:"[Aiz] LT",nextDay:"[Faleam] LT",nextWeek:"[Fuddlo] dddd[,] LT",lastDay:"[Kal] LT",lastWeek:"[Fattlo] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%s",past:"%s adim",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}(er)/,ordinal:function(e,t){switch(t){case"D":return e+"er";default:case"M":case"Q":case"DDD":case"d":case"w":case"W":return e}},week:{dow:0,doy:3},meridiemParse:/rati|sokallim|donparam|sanje/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="rati")return e<4?e:e+12;else if(t==="sokallim")return e;else if(t==="donparam")return e>12?e:e+12;else if(t==="sanje")return e+12},meridiem:function(e,t,n){if(e<4)return"rati";else if(e<12)return"sokallim";else if(e<16)return"donparam";else if(e<20)return"sanje";else return"rati"}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"ą«§",2:"ą«Ø",3:"ą«©",4:"ą«Ŗ",5:"ą««",6:"ą«¬",7:"ą«­",8:"ą«®",9:"ą«Æ",0:"ą«¦"},n={"ą«§":"1","ą«Ø":"2","ą«©":"3","ą«Ŗ":"4","ą««":"5","ą«¬":"6","ą«­":"7","ą«®":"8","ą«Æ":"9","ą«¦":"0"},a;e.defineLocale("gu",{months:"ąŖœąŖ¾ąŖØą«ąŖÆą«ąŖ†ąŖ°ą«€_ąŖ«ą«‡ąŖ¬ą«ąŖ°ą«ąŖ†ąŖ°ą«€_ąŖ®ąŖ¾ąŖ°ą«ąŖš_ąŖąŖŖą«ąŖ°ąŖæąŖ²_ąŖ®ą«‡_ąŖœą«‚ąŖØ_ąŖœą«ąŖ²ąŖ¾ąŖˆ_ąŖ‘ąŖ—ąŖøą«ąŖŸ_ąŖøąŖŖą«ąŖŸą«‡ąŖ®ą«ąŖ¬ąŖ°_ąŖ‘ąŖ•ą«ąŖŸą«ąŖ¬ąŖ°_ąŖØąŖµą«‡ąŖ®ą«ąŖ¬ąŖ°_ąŖ”ąŖæąŖøą«‡ąŖ®ą«ąŖ¬ąŖ°".split("_"),monthsShort:"ąŖœąŖ¾ąŖØą«ąŖÆą«._ąŖ«ą«‡ąŖ¬ą«ąŖ°ą«._ąŖ®ąŖ¾ąŖ°ą«ąŖš_ąŖąŖŖą«ąŖ°ąŖæ._ąŖ®ą«‡_ąŖœą«‚ąŖØ_ąŖœą«ąŖ²ąŖ¾._ąŖ‘ąŖ—._ąŖøąŖŖą«ąŖŸą«‡._ąŖ‘ąŖ•ą«ąŖŸą«._ąŖØąŖµą«‡._ąŖ”ąŖæąŖøą«‡.".split("_"),monthsParseExact:true,weekdays:"ąŖ°ąŖµąŖæąŖµąŖ¾ąŖ°_ąŖøą«‹ąŖ®ąŖµąŖ¾ąŖ°_ąŖ®ąŖ‚ąŖ—ąŖ³ąŖµąŖ¾ąŖ°_ąŖ¬ą«ąŖ§ą«ąŖµąŖ¾ąŖ°_ąŖ—ą«ąŖ°ą«ąŖµąŖ¾ąŖ°_ąŖ¶ą«ąŖ•ą«ąŖ°ąŖµąŖ¾ąŖ°_ąŖ¶ąŖØąŖæąŖµąŖ¾ąŖ°".split("_"),weekdaysShort:"ąŖ°ąŖµąŖæ_ąŖøą«‹ąŖ®_ąŖ®ąŖ‚ąŖ—ąŖ³_ąŖ¬ą«ąŖ§ą«_ąŖ—ą«ąŖ°ą«_ąŖ¶ą«ąŖ•ą«ąŖ°_ąŖ¶ąŖØąŖæ".split("_"),weekdaysMin:"ąŖ°_ąŖøą«‹_ąŖ®ąŖ‚_ąŖ¬ą«_ąŖ—ą«_ąŖ¶ą«_ąŖ¶".split("_"),longDateFormat:{LT:"A h:mm ąŖµąŖ¾ąŖ—ą«ąŖÆą«‡",LTS:"A h:mm:ss ąŖµąŖ¾ąŖ—ą«ąŖÆą«‡",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ąŖµąŖ¾ąŖ—ą«ąŖÆą«‡",LLLL:"dddd, D MMMM YYYY, A h:mm ąŖµąŖ¾ąŖ—ą«ąŖÆą«‡"},calendar:{sameDay:"[ąŖ†ąŖœ] LT",nextDay:"[ąŖ•ąŖ¾ąŖ²ą«‡] LT",nextWeek:"dddd, LT",lastDay:"[ąŖ—ąŖ‡ąŖ•ąŖ¾ąŖ²ą«‡] LT",lastWeek:"[ąŖŖąŖ¾ąŖ›ąŖ²ąŖ¾] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ąŖ®ąŖ¾",past:"%s ąŖŖąŖ¹ą«‡ąŖ²ąŖ¾",s:"ąŖ…ąŖ®ą«ąŖ• ąŖŖąŖ³ą«‹",ss:"%d ąŖøą«‡ąŖ•ąŖ‚ąŖ”",m:"ąŖąŖ• ąŖ®ąŖæąŖØąŖæąŖŸ",mm:"%d ąŖ®ąŖæąŖØąŖæąŖŸ",h:"ąŖąŖ• ąŖ•ąŖ²ąŖ¾ąŖ•",hh:"%d ąŖ•ąŖ²ąŖ¾ąŖ•",d:"ąŖąŖ• ąŖ¦ąŖæąŖµąŖø",dd:"%d ąŖ¦ąŖæąŖµąŖø",M:"ąŖąŖ• ąŖ®ąŖ¹ąŖæąŖØą«‹",MM:"%d ąŖ®ąŖ¹ąŖæąŖØą«‹",y:"ąŖąŖ• ąŖµąŖ°ą«ąŖ·",yy:"%d ąŖµąŖ°ą«ąŖ·"},preparse:function(e){return e.replace(/[ą«§ą«Øą«©ą«Ŗą««ą«¬ą«­ą«®ą«Æą«¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ąŖ°ąŖ¾ąŖ¤|ąŖ¬ąŖŖą«‹ąŖ°|ąŖøąŖµąŖ¾ąŖ°|ąŖøąŖ¾ąŖ‚ąŖœ/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ąŖ°ąŖ¾ąŖ¤")return e<4?e:e+12;else if(t==="ąŖøąŖµąŖ¾ąŖ°")return e;else if(t==="ąŖ¬ąŖŖą«‹ąŖ°")return e>=10?e:e+12;else if(t==="ąŖøąŖ¾ąŖ‚ąŖœ")return e+12},meridiem:function(e,t,n){if(e<4)return"ąŖ°ąŖ¾ąŖ¤";else if(e<10)return"ąŖøąŖµąŖ¾ąŖ°";else if(e<17)return"ąŖ¬ąŖŖą«‹ąŖ°";else if(e<20)return"ąŖøąŖ¾ąŖ‚ąŖœ";else return"ąŖ°ąŖ¾ąŖ¤"},week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"ą«§",2:"ą«Ø",3:"ą«©",4:"ą«Ŗ",5:"ą««",6:"ą«¬",7:"ą«­",8:"ą«®",9:"ą«Æ",0:"ą«¦"},n={"ą«§":"1","ą«Ø":"2","ą«©":"3","ą«Ŗ":"4","ą««":"5","ą«¬":"6","ą«­":"7","ą«®":"8","ą«Æ":"9","ą«¦":"0"},a;e.defineLocale("gu",{months:"ąŖœąŖ¾ąŖØą«ąŖÆą«ąŖ†ąŖ°ą«€_ąŖ«ą«‡ąŖ¬ą«ąŖ°ą«ąŖ†ąŖ°ą«€_ąŖ®ąŖ¾ąŖ°ą«ąŖš_ąŖąŖŖą«ąŖ°ąŖæąŖ²_ąŖ®ą«‡_ąŖœą«‚ąŖØ_ąŖœą«ąŖ²ąŖ¾ąŖˆ_ąŖ‘ąŖ—ąŖøą«ąŖŸ_ąŖøąŖŖą«ąŖŸą«‡ąŖ®ą«ąŖ¬ąŖ°_ąŖ‘ąŖ•ą«ąŖŸą«ąŖ¬ąŖ°_ąŖØąŖµą«‡ąŖ®ą«ąŖ¬ąŖ°_ąŖ”ąŖæąŖøą«‡ąŖ®ą«ąŖ¬ąŖ°".split("_"),monthsShort:"ąŖœąŖ¾ąŖØą«ąŖÆą«._ąŖ«ą«‡ąŖ¬ą«ąŖ°ą«._ąŖ®ąŖ¾ąŖ°ą«ąŖš_ąŖąŖŖą«ąŖ°ąŖæ._ąŖ®ą«‡_ąŖœą«‚ąŖØ_ąŖœą«ąŖ²ąŖ¾._ąŖ‘ąŖ—._ąŖøąŖŖą«ąŖŸą«‡._ąŖ‘ąŖ•ą«ąŖŸą«._ąŖØąŖµą«‡._ąŖ”ąŖæąŖøą«‡.".split("_"),monthsParseExact:true,weekdays:"ąŖ°ąŖµąŖæąŖµąŖ¾ąŖ°_ąŖøą«‹ąŖ®ąŖµąŖ¾ąŖ°_ąŖ®ąŖ‚ąŖ—ąŖ³ąŖµąŖ¾ąŖ°_ąŖ¬ą«ąŖ§ą«ąŖµąŖ¾ąŖ°_ąŖ—ą«ąŖ°ą«ąŖµąŖ¾ąŖ°_ąŖ¶ą«ąŖ•ą«ąŖ°ąŖµąŖ¾ąŖ°_ąŖ¶ąŖØąŖæąŖµąŖ¾ąŖ°".split("_"),weekdaysShort:"ąŖ°ąŖµąŖæ_ąŖøą«‹ąŖ®_ąŖ®ąŖ‚ąŖ—ąŖ³_ąŖ¬ą«ąŖ§ą«_ąŖ—ą«ąŖ°ą«_ąŖ¶ą«ąŖ•ą«ąŖ°_ąŖ¶ąŖØąŖæ".split("_"),weekdaysMin:"ąŖ°_ąŖøą«‹_ąŖ®ąŖ‚_ąŖ¬ą«_ąŖ—ą«_ąŖ¶ą«_ąŖ¶".split("_"),longDateFormat:{LT:"A h:mm ąŖµąŖ¾ąŖ—ą«ąŖÆą«‡",LTS:"A h:mm:ss ąŖµąŖ¾ąŖ—ą«ąŖÆą«‡",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ąŖµąŖ¾ąŖ—ą«ąŖÆą«‡",LLLL:"dddd, D MMMM YYYY, A h:mm ąŖµąŖ¾ąŖ—ą«ąŖÆą«‡"},calendar:{sameDay:"[ąŖ†ąŖœ] LT",nextDay:"[ąŖ•ąŖ¾ąŖ²ą«‡] LT",nextWeek:"dddd, LT",lastDay:"[ąŖ—ąŖ‡ąŖ•ąŖ¾ąŖ²ą«‡] LT",lastWeek:"[ąŖŖąŖ¾ąŖ›ąŖ²ąŖ¾] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ąŖ®ąŖ¾",past:"%s ąŖŖąŖ¹ą«‡ąŖ²ąŖ¾",s:"ąŖ…ąŖ®ą«ąŖ• ąŖŖąŖ³ą«‹",ss:"%d ąŖøą«‡ąŖ•ąŖ‚ąŖ”",m:"ąŖąŖ• ąŖ®ąŖæąŖØąŖæąŖŸ",mm:"%d ąŖ®ąŖæąŖØąŖæąŖŸ",h:"ąŖąŖ• ąŖ•ąŖ²ąŖ¾ąŖ•",hh:"%d ąŖ•ąŖ²ąŖ¾ąŖ•",d:"ąŖąŖ• ąŖ¦ąŖæąŖµąŖø",dd:"%d ąŖ¦ąŖæąŖµąŖø",M:"ąŖąŖ• ąŖ®ąŖ¹ąŖæąŖØą«‹",MM:"%d ąŖ®ąŖ¹ąŖæąŖØą«‹",y:"ąŖąŖ• ąŖµąŖ°ą«ąŖ·",yy:"%d ąŖµąŖ°ą«ąŖ·"},preparse:function(e){return e.replace(/[ą«§ą«Øą«©ą«Ŗą««ą«¬ą«­ą«®ą«Æą«¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ąŖ°ąŖ¾ąŖ¤|ąŖ¬ąŖŖą«‹ąŖ°|ąŖøąŖµąŖ¾ąŖ°|ąŖøąŖ¾ąŖ‚ąŖœ/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ąŖ°ąŖ¾ąŖ¤")return e<4?e:e+12;else if(t==="ąŖøąŖµąŖ¾ąŖ°")return e;else if(t==="ąŖ¬ąŖŖą«‹ąŖ°")return e>=10?e:e+12;else if(t==="ąŖøąŖ¾ąŖ‚ąŖœ")return e+12},meridiem:function(e,t,n){if(e<4)return"ąŖ°ąŖ¾ąŖ¤";else if(e<10)return"ąŖøąŖµąŖ¾ąŖ°";else if(e<17)return"ąŖ¬ąŖŖą«‹ąŖ°";else if(e<20)return"ąŖøąŖ¾ąŖ‚ąŖœ";else return"ąŖ°ąŖ¾ąŖ¤"},week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("he",{months:"ינוא×Ø_פב×Øוא×Ø_מ×Øׄ_אפ×Øיל_מאי_יוני_יולי_אוגוהט_הפטמב×Ø_אוקטוב×Ø_נובמב×Ø_דצמב×Ø".split("_"),monthsShort:"ינו׳_פב×Ø׳_מ×Øׄ_אפ×Ø׳_מאי_יוני_יולי_אוג׳_הפט׳_אוק׳_נוב׳_דצמ׳".split("_"),weekdays:"×Øאשון_שני_שלישי_×Øביעי_חמישי_שישי_שב×Ŗ".split("_"),weekdaysShort:"א׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳".split("_"),weekdaysMin:"א_ב_ג_ד_ה_ו_ש".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [ב]MMMM YYYY",LLL:"D [ב]MMMM YYYY HH:mm",LLLL:"dddd, D [ב]MMMM YYYY HH:mm",l:"D/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY HH:mm",llll:"ddd, D MMM YYYY HH:mm"},calendar:{sameDay:"[היום ב־]LT",nextDay:"[מח×Ø ×‘Ö¾]LT",nextWeek:"dddd [בשעה] LT",lastDay:"[א×Ŗמול ב־]LT",lastWeek:"[ביום] dddd [האח×Øון בשעה] LT",sameElse:"L"},relativeTime:{future:"בעוד %s",past:"לפני %s",s:"מהפ×Ø ×©× ×™×•×Ŗ",ss:"%d שניו×Ŗ",m:"דקה",mm:"%d דקו×Ŗ",h:"שעה",hh:function(e){if(e===2)return"שע×Ŗיים";return e+" שעו×Ŗ"},d:"יום",dd:function(e){if(e===2)return"יומיים";return e+" ימים"},M:"חודש",MM:function(e){if(e===2)return"חודשיים";return e+" חודשים"},y:"שנה",yy:function(e){if(e===2)return"שנ×Ŗיים";else if(e%10===0&&e!==10)return e+" שנה";return e+" שנים"}},meridiemParse:/אחה"צ|לפנה"צ|אח×Øי הצה×Øיים|לפני הצה×Øיים|לפנו×Ŗ בוק×Ø|בבוק×Ø|בע×Øב/i,isPM:function(e){return/^(אחה"צ|אח×Øי הצה×Øיים|בע×Øב)$/.test(e)},meridiem:function(e,t,n){if(e<5)return"לפנו×Ŗ בוק×Ø";else if(e<10)return"בבוק×Ø";else if(e<12)return n?'לפנה"צ':"לפני הצה×Øיים";else if(e<18)return n?'אחה"צ':"אח×Øי הצה×Øיים";else return"בע×Øב"}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("he",{months:"ינוא×Ø_פב×Øוא×Ø_מ×Øׄ_אפ×Øיל_מאי_יוני_יולי_אוגוהט_הפטמב×Ø_אוקטוב×Ø_נובמב×Ø_דצמב×Ø".split("_"),monthsShort:"ינו׳_פב×Ø׳_מ×Øׄ_אפ×Ø׳_מאי_יוני_יולי_אוג׳_הפט׳_אוק׳_נוב׳_דצמ׳".split("_"),weekdays:"×Øאשון_שני_שלישי_×Øביעי_חמישי_שישי_שב×Ŗ".split("_"),weekdaysShort:"א׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳".split("_"),weekdaysMin:"א_ב_ג_ד_ה_ו_ש".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [ב]MMMM YYYY",LLL:"D [ב]MMMM YYYY HH:mm",LLLL:"dddd, D [ב]MMMM YYYY HH:mm",l:"D/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY HH:mm",llll:"ddd, D MMM YYYY HH:mm"},calendar:{sameDay:"[היום ב־]LT",nextDay:"[מח×Ø ×‘Ö¾]LT",nextWeek:"dddd [בשעה] LT",lastDay:"[א×Ŗמול ב־]LT",lastWeek:"[ביום] dddd [האח×Øון בשעה] LT",sameElse:"L"},relativeTime:{future:"בעוד %s",past:"לפני %s",s:"מהפ×Ø ×©× ×™×•×Ŗ",ss:"%d שניו×Ŗ",m:"דקה",mm:"%d דקו×Ŗ",h:"שעה",hh:function(e){if(e===2)return"שע×Ŗיים";return e+" שעו×Ŗ"},d:"יום",dd:function(e){if(e===2)return"יומיים";return e+" ימים"},M:"חודש",MM:function(e){if(e===2)return"חודשיים";return e+" חודשים"},y:"שנה",yy:function(e){if(e===2)return"שנ×Ŗיים";else if(e%10===0&&e!==10)return e+" שנה";return e+" שנים"}},meridiemParse:/אחה"צ|לפנה"צ|אח×Øי הצה×Øיים|לפני הצה×Øיים|לפנו×Ŗ בוק×Ø|בבוק×Ø|בע×Øב/i,isPM:function(e){return/^(אחה"צ|אח×Øי הצה×Øיים|בע×Øב)$/.test(e)},meridiem:function(e,t,n){if(e<5)return"לפנו×Ŗ בוק×Ø";else if(e<10)return"בבוק×Ø";else if(e<12)return n?'לפנה"צ':"לפני הצה×Øיים";else if(e<18)return n?'אחה"צ':"אח×Øי הצה×Øיים";else return"בע×Øב"}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"ą„§",2:"ą„Ø",3:"ą„©",4:"ą„Ŗ",5:"ą„«",6:"ą„¬",7:"ą„­",8:"ą„®",9:"ą„Æ",0:"ą„¦"},n={"ą„§":"1","ą„Ø":"2","ą„©":"3","ą„Ŗ":"4","ą„«":"5","ą„¬":"6","ą„­":"7","ą„®":"8","ą„Æ":"9","ą„¦":"0"},a=[/^ą¤œą¤Ø/i,/^ą¤«ą¤¼ą¤°|ą¤«ą¤°/i,/^ą¤®ą¤¾ą¤°ą„ą¤š/i,/^ą¤…ą¤Ŗą„ą¤°ą„ˆ/i,/^ą¤®ą¤ˆ/i,/^ą¤œą„‚ą¤Ø/i,/^ą¤œą„ą¤²/i,/^ą¤…ą¤—/i,/^ą¤øą¤æą¤¤ą¤‚|ą¤øą¤æą¤¤/i,/^ą¤…ą¤•ą„ą¤Ÿą„‚/i,/^ą¤Øą¤µ|ą¤Øą¤µą¤‚/i,/^ą¤¦ą¤æą¤øą¤‚|ą¤¦ą¤æą¤ø/i],r=[/^ą¤œą¤Ø/i,/^ą¤«ą¤¼ą¤°/i,/^ą¤®ą¤¾ą¤°ą„ą¤š/i,/^ą¤…ą¤Ŗą„ą¤°ą„ˆ/i,/^ą¤®ą¤ˆ/i,/^ą¤œą„‚ą¤Ø/i,/^ą¤œą„ą¤²/i,/^ą¤…ą¤—/i,/^ą¤øą¤æą¤¤/i,/^ą¤…ą¤•ą„ą¤Ÿą„‚/i,/^ą¤Øą¤µ/i,/^ą¤¦ą¤æą¤ø/i],o;e.defineLocale("hi",{months:{format:"ą¤œą¤Øą¤µą¤°ą„€_ą¤«ą¤¼ą¤°ą¤µą¤°ą„€_ą¤®ą¤¾ą¤°ą„ą¤š_ą¤…ą¤Ŗą„ą¤°ą„ˆą¤²_ą¤®ą¤ˆ_ą¤œą„‚ą¤Ø_ą¤œą„ą¤²ą¤¾ą¤ˆ_ą¤…ą¤—ą¤øą„ą¤¤_ą¤øą¤æą¤¤ą¤®ą„ą¤¬ą¤°_ą¤…ą¤•ą„ą¤Ÿą„‚ą¤¬ą¤°_ą¤Øą¤µą¤®ą„ą¤¬ą¤°_ą¤¦ą¤æą¤øą¤®ą„ą¤¬ą¤°".split("_"),standalone:"ą¤œą¤Øą¤µą¤°ą„€_ą¤«ą¤°ą¤µą¤°ą„€_ą¤®ą¤¾ą¤°ą„ą¤š_ą¤…ą¤Ŗą„ą¤°ą„ˆą¤²_ą¤®ą¤ˆ_ą¤œą„‚ą¤Ø_ą¤œą„ą¤²ą¤¾ą¤ˆ_ą¤…ą¤—ą¤øą„ą¤¤_ą¤øą¤æą¤¤ą¤‚ą¤¬ą¤°_ą¤…ą¤•ą„ą¤Ÿą„‚ą¤¬ą¤°_ą¤Øą¤µą¤‚ą¤¬ą¤°_ą¤¦ą¤æą¤øą¤‚ą¤¬ą¤°".split("_")},monthsShort:"ą¤œą¤Ø._ą¤«ą¤¼ą¤°._ą¤®ą¤¾ą¤°ą„ą¤š_ą¤…ą¤Ŗą„ą¤°ą„ˆ._ą¤®ą¤ˆ_ą¤œą„‚ą¤Ø_ą¤œą„ą¤²._ą¤…ą¤—._ą¤øą¤æą¤¤._ą¤…ą¤•ą„ą¤Ÿą„‚._ą¤Øą¤µ._ą¤¦ą¤æą¤ø.".split("_"),weekdays:"ą¤°ą¤µą¤æą¤µą¤¾ą¤°_ą¤øą„‹ą¤®ą¤µą¤¾ą¤°_ą¤®ą¤‚ą¤—ą¤²ą¤µą¤¾ą¤°_ą¤¬ą„ą¤§ą¤µą¤¾ą¤°_ą¤—ą„ą¤°ą„‚ą¤µą¤¾ą¤°_ą¤¶ą„ą¤•ą„ą¤°ą¤µą¤¾ą¤°_ą¤¶ą¤Øą¤æą¤µą¤¾ą¤°".split("_"),weekdaysShort:"ą¤°ą¤µą¤æ_ą¤øą„‹ą¤®_ą¤®ą¤‚ą¤—ą¤²_ą¤¬ą„ą¤§_ą¤—ą„ą¤°ą„‚_ą¤¶ą„ą¤•ą„ą¤°_ą¤¶ą¤Øą¤æ".split("_"),weekdaysMin:"ą¤°_ą¤øą„‹_ą¤®ą¤‚_ą¤¬ą„_ą¤—ą„_ą¤¶ą„_ą¤¶".split("_"),longDateFormat:{LT:"A h:mm ą¤¬ą¤œą„‡",LTS:"A h:mm:ss ą¤¬ą¤œą„‡",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ą¤¬ą¤œą„‡",LLLL:"dddd, D MMMM YYYY, A h:mm ą¤¬ą¤œą„‡"},monthsParse:a,longMonthsParse:a,shortMonthsParse:r,monthsRegex:/^(ą¤œą¤Øą¤µą¤°ą„€|ą¤œą¤Ø\.?|ą¤«ą¤¼ą¤°ą¤µą¤°ą„€|ą¤«ą¤°ą¤µą¤°ą„€|ą¤«ą¤¼ą¤°\.?|ą¤®ą¤¾ą¤°ą„ą¤š?|ą¤…ą¤Ŗą„ą¤°ą„ˆą¤²|ą¤…ą¤Ŗą„ą¤°ą„ˆ\.?|ą¤®ą¤ˆ?|ą¤œą„‚ą¤Ø?|ą¤œą„ą¤²ą¤¾ą¤ˆ|ą¤œą„ą¤²\.?|ą¤…ą¤—ą¤øą„ą¤¤|ą¤…ą¤—\.?|ą¤øą¤æą¤¤ą¤®ą„ą¤¬ą¤°|ą¤øą¤æą¤¤ą¤‚ą¤¬ą¤°|ą¤øą¤æą¤¤\.?|ą¤…ą¤•ą„ą¤Ÿą„‚ą¤¬ą¤°|ą¤…ą¤•ą„ą¤Ÿą„‚\.?|ą¤Øą¤µą¤®ą„ą¤¬ą¤°|ą¤Øą¤µą¤‚ą¤¬ą¤°|ą¤Øą¤µ\.?|ą¤¦ą¤æą¤øą¤®ą„ą¤¬ą¤°|ą¤¦ą¤æą¤øą¤‚ą¤¬ą¤°|ą¤¦ą¤æą¤ø\.?)/i,monthsShortRegex:/^(ą¤œą¤Øą¤µą¤°ą„€|ą¤œą¤Ø\.?|ą¤«ą¤¼ą¤°ą¤µą¤°ą„€|ą¤«ą¤°ą¤µą¤°ą„€|ą¤«ą¤¼ą¤°\.?|ą¤®ą¤¾ą¤°ą„ą¤š?|ą¤…ą¤Ŗą„ą¤°ą„ˆą¤²|ą¤…ą¤Ŗą„ą¤°ą„ˆ\.?|ą¤®ą¤ˆ?|ą¤œą„‚ą¤Ø?|ą¤œą„ą¤²ą¤¾ą¤ˆ|ą¤œą„ą¤²\.?|ą¤…ą¤—ą¤øą„ą¤¤|ą¤…ą¤—\.?|ą¤øą¤æą¤¤ą¤®ą„ą¤¬ą¤°|ą¤øą¤æą¤¤ą¤‚ą¤¬ą¤°|ą¤øą¤æą¤¤\.?|ą¤…ą¤•ą„ą¤Ÿą„‚ą¤¬ą¤°|ą¤…ą¤•ą„ą¤Ÿą„‚\.?|ą¤Øą¤µą¤®ą„ą¤¬ą¤°|ą¤Øą¤µą¤‚ą¤¬ą¤°|ą¤Øą¤µ\.?|ą¤¦ą¤æą¤øą¤®ą„ą¤¬ą¤°|ą¤¦ą¤æą¤øą¤‚ą¤¬ą¤°|ą¤¦ą¤æą¤ø\.?)/i,monthsStrictRegex:/^(ą¤œą¤Øą¤µą¤°ą„€?|ą¤«ą¤¼ą¤°ą¤µą¤°ą„€|ą¤«ą¤°ą¤µą¤°ą„€?|ą¤®ą¤¾ą¤°ą„ą¤š?|ą¤…ą¤Ŗą„ą¤°ą„ˆą¤²?|ą¤®ą¤ˆ?|ą¤œą„‚ą¤Ø?|ą¤œą„ą¤²ą¤¾ą¤ˆ?|ą¤…ą¤—ą¤øą„ą¤¤?|ą¤øą¤æą¤¤ą¤®ą„ą¤¬ą¤°|ą¤øą¤æą¤¤ą¤‚ą¤¬ą¤°|ą¤øą¤æą¤¤?\.?|ą¤…ą¤•ą„ą¤Ÿą„‚ą¤¬ą¤°|ą¤…ą¤•ą„ą¤Ÿą„‚\.?|ą¤Øą¤µą¤®ą„ą¤¬ą¤°|ą¤Øą¤µą¤‚ą¤¬ą¤°?|ą¤¦ą¤æą¤øą¤®ą„ą¤¬ą¤°|ą¤¦ą¤æą¤øą¤‚ą¤¬ą¤°?)/i,monthsShortStrictRegex:/^(ą¤œą¤Ø\.?|ą¤«ą¤¼ą¤°\.?|ą¤®ą¤¾ą¤°ą„ą¤š?|ą¤…ą¤Ŗą„ą¤°ą„ˆ\.?|ą¤®ą¤ˆ?|ą¤œą„‚ą¤Ø?|ą¤œą„ą¤²\.?|ą¤…ą¤—\.?|ą¤øą¤æą¤¤\.?|ą¤…ą¤•ą„ą¤Ÿą„‚\.?|ą¤Øą¤µ\.?|ą¤¦ą¤æą¤ø\.?)/i,calendar:{sameDay:"[ą¤†ą¤œ] LT",nextDay:"[ą¤•ą¤²] LT",nextWeek:"dddd, LT",lastDay:"[ą¤•ą¤²] LT",lastWeek:"[ą¤Ŗą¤æą¤›ą¤²ą„‡] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą¤®ą„‡ą¤‚",past:"%s ą¤Ŗą¤¹ą¤²ą„‡",s:"ą¤•ą„ą¤› ą¤¹ą„€ ą¤•ą„ą¤·ą¤£",ss:"%d ą¤øą„‡ą¤•ą¤‚ą¤”",m:"ą¤ą¤• ą¤®ą¤æą¤Øą¤Ÿ",mm:"%d ą¤®ą¤æą¤Øą¤Ÿ",h:"ą¤ą¤• ą¤˜ą¤‚ą¤Ÿą¤¾",hh:"%d ą¤˜ą¤‚ą¤Ÿą„‡",d:"ą¤ą¤• ą¤¦ą¤æą¤Ø",dd:"%d ą¤¦ą¤æą¤Ø",M:"ą¤ą¤• ą¤®ą¤¹ą„€ą¤Øą„‡",MM:"%d ą¤®ą¤¹ą„€ą¤Øą„‡",y:"ą¤ą¤• ą¤µą¤°ą„ą¤·",yy:"%d ą¤µą¤°ą„ą¤·"},preparse:function(e){return e.replace(/[ą„§ą„Øą„©ą„Ŗą„«ą„¬ą„­ą„®ą„Æą„¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą¤°ą¤¾ą¤¤|ą¤øą„ą¤¬ą¤¹|ą¤¦ą„‹ą¤Ŗą¤¹ą¤°|ą¤¶ą¤¾ą¤®/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą¤°ą¤¾ą¤¤")return e<4?e:e+12;else if(t==="ą¤øą„ą¤¬ą¤¹")return e;else if(t==="ą¤¦ą„‹ą¤Ŗą¤¹ą¤°")return e>=10?e:e+12;else if(t==="ą¤¶ą¤¾ą¤®")return e+12},meridiem:function(e,t,n){if(e<4)return"ą¤°ą¤¾ą¤¤";else if(e<10)return"ą¤øą„ą¤¬ą¤¹";else if(e<17)return"ą¤¦ą„‹ą¤Ŗą¤¹ą¤°";else if(e<20)return"ą¤¶ą¤¾ą¤®";else return"ą¤°ą¤¾ą¤¤"},week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"ą„§",2:"ą„Ø",3:"ą„©",4:"ą„Ŗ",5:"ą„«",6:"ą„¬",7:"ą„­",8:"ą„®",9:"ą„Æ",0:"ą„¦"},n={"ą„§":"1","ą„Ø":"2","ą„©":"3","ą„Ŗ":"4","ą„«":"5","ą„¬":"6","ą„­":"7","ą„®":"8","ą„Æ":"9","ą„¦":"0"},a=[/^ą¤œą¤Ø/i,/^ą¤«ą¤¼ą¤°|ą¤«ą¤°/i,/^ą¤®ą¤¾ą¤°ą„ą¤š/i,/^ą¤…ą¤Ŗą„ą¤°ą„ˆ/i,/^ą¤®ą¤ˆ/i,/^ą¤œą„‚ą¤Ø/i,/^ą¤œą„ą¤²/i,/^ą¤…ą¤—/i,/^ą¤øą¤æą¤¤ą¤‚|ą¤øą¤æą¤¤/i,/^ą¤…ą¤•ą„ą¤Ÿą„‚/i,/^ą¤Øą¤µ|ą¤Øą¤µą¤‚/i,/^ą¤¦ą¤æą¤øą¤‚|ą¤¦ą¤æą¤ø/i],r=[/^ą¤œą¤Ø/i,/^ą¤«ą¤¼ą¤°/i,/^ą¤®ą¤¾ą¤°ą„ą¤š/i,/^ą¤…ą¤Ŗą„ą¤°ą„ˆ/i,/^ą¤®ą¤ˆ/i,/^ą¤œą„‚ą¤Ø/i,/^ą¤œą„ą¤²/i,/^ą¤…ą¤—/i,/^ą¤øą¤æą¤¤/i,/^ą¤…ą¤•ą„ą¤Ÿą„‚/i,/^ą¤Øą¤µ/i,/^ą¤¦ą¤æą¤ø/i],o;e.defineLocale("hi",{months:{format:"ą¤œą¤Øą¤µą¤°ą„€_ą¤«ą¤¼ą¤°ą¤µą¤°ą„€_ą¤®ą¤¾ą¤°ą„ą¤š_ą¤…ą¤Ŗą„ą¤°ą„ˆą¤²_ą¤®ą¤ˆ_ą¤œą„‚ą¤Ø_ą¤œą„ą¤²ą¤¾ą¤ˆ_ą¤…ą¤—ą¤øą„ą¤¤_ą¤øą¤æą¤¤ą¤®ą„ą¤¬ą¤°_ą¤…ą¤•ą„ą¤Ÿą„‚ą¤¬ą¤°_ą¤Øą¤µą¤®ą„ą¤¬ą¤°_ą¤¦ą¤æą¤øą¤®ą„ą¤¬ą¤°".split("_"),standalone:"ą¤œą¤Øą¤µą¤°ą„€_ą¤«ą¤°ą¤µą¤°ą„€_ą¤®ą¤¾ą¤°ą„ą¤š_ą¤…ą¤Ŗą„ą¤°ą„ˆą¤²_ą¤®ą¤ˆ_ą¤œą„‚ą¤Ø_ą¤œą„ą¤²ą¤¾ą¤ˆ_ą¤…ą¤—ą¤øą„ą¤¤_ą¤øą¤æą¤¤ą¤‚ą¤¬ą¤°_ą¤…ą¤•ą„ą¤Ÿą„‚ą¤¬ą¤°_ą¤Øą¤µą¤‚ą¤¬ą¤°_ą¤¦ą¤æą¤øą¤‚ą¤¬ą¤°".split("_")},monthsShort:"ą¤œą¤Ø._ą¤«ą¤¼ą¤°._ą¤®ą¤¾ą¤°ą„ą¤š_ą¤…ą¤Ŗą„ą¤°ą„ˆ._ą¤®ą¤ˆ_ą¤œą„‚ą¤Ø_ą¤œą„ą¤²._ą¤…ą¤—._ą¤øą¤æą¤¤._ą¤…ą¤•ą„ą¤Ÿą„‚._ą¤Øą¤µ._ą¤¦ą¤æą¤ø.".split("_"),weekdays:"ą¤°ą¤µą¤æą¤µą¤¾ą¤°_ą¤øą„‹ą¤®ą¤µą¤¾ą¤°_ą¤®ą¤‚ą¤—ą¤²ą¤µą¤¾ą¤°_ą¤¬ą„ą¤§ą¤µą¤¾ą¤°_ą¤—ą„ą¤°ą„‚ą¤µą¤¾ą¤°_ą¤¶ą„ą¤•ą„ą¤°ą¤µą¤¾ą¤°_ą¤¶ą¤Øą¤æą¤µą¤¾ą¤°".split("_"),weekdaysShort:"ą¤°ą¤µą¤æ_ą¤øą„‹ą¤®_ą¤®ą¤‚ą¤—ą¤²_ą¤¬ą„ą¤§_ą¤—ą„ą¤°ą„‚_ą¤¶ą„ą¤•ą„ą¤°_ą¤¶ą¤Øą¤æ".split("_"),weekdaysMin:"ą¤°_ą¤øą„‹_ą¤®ą¤‚_ą¤¬ą„_ą¤—ą„_ą¤¶ą„_ą¤¶".split("_"),longDateFormat:{LT:"A h:mm ą¤¬ą¤œą„‡",LTS:"A h:mm:ss ą¤¬ą¤œą„‡",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ą¤¬ą¤œą„‡",LLLL:"dddd, D MMMM YYYY, A h:mm ą¤¬ą¤œą„‡"},monthsParse:a,longMonthsParse:a,shortMonthsParse:r,monthsRegex:/^(ą¤œą¤Øą¤µą¤°ą„€|ą¤œą¤Ø\.?|ą¤«ą¤¼ą¤°ą¤µą¤°ą„€|ą¤«ą¤°ą¤µą¤°ą„€|ą¤«ą¤¼ą¤°\.?|ą¤®ą¤¾ą¤°ą„ą¤š?|ą¤…ą¤Ŗą„ą¤°ą„ˆą¤²|ą¤…ą¤Ŗą„ą¤°ą„ˆ\.?|ą¤®ą¤ˆ?|ą¤œą„‚ą¤Ø?|ą¤œą„ą¤²ą¤¾ą¤ˆ|ą¤œą„ą¤²\.?|ą¤…ą¤—ą¤øą„ą¤¤|ą¤…ą¤—\.?|ą¤øą¤æą¤¤ą¤®ą„ą¤¬ą¤°|ą¤øą¤æą¤¤ą¤‚ą¤¬ą¤°|ą¤øą¤æą¤¤\.?|ą¤…ą¤•ą„ą¤Ÿą„‚ą¤¬ą¤°|ą¤…ą¤•ą„ą¤Ÿą„‚\.?|ą¤Øą¤µą¤®ą„ą¤¬ą¤°|ą¤Øą¤µą¤‚ą¤¬ą¤°|ą¤Øą¤µ\.?|ą¤¦ą¤æą¤øą¤®ą„ą¤¬ą¤°|ą¤¦ą¤æą¤øą¤‚ą¤¬ą¤°|ą¤¦ą¤æą¤ø\.?)/i,monthsShortRegex:/^(ą¤œą¤Øą¤µą¤°ą„€|ą¤œą¤Ø\.?|ą¤«ą¤¼ą¤°ą¤µą¤°ą„€|ą¤«ą¤°ą¤µą¤°ą„€|ą¤«ą¤¼ą¤°\.?|ą¤®ą¤¾ą¤°ą„ą¤š?|ą¤…ą¤Ŗą„ą¤°ą„ˆą¤²|ą¤…ą¤Ŗą„ą¤°ą„ˆ\.?|ą¤®ą¤ˆ?|ą¤œą„‚ą¤Ø?|ą¤œą„ą¤²ą¤¾ą¤ˆ|ą¤œą„ą¤²\.?|ą¤…ą¤—ą¤øą„ą¤¤|ą¤…ą¤—\.?|ą¤øą¤æą¤¤ą¤®ą„ą¤¬ą¤°|ą¤øą¤æą¤¤ą¤‚ą¤¬ą¤°|ą¤øą¤æą¤¤\.?|ą¤…ą¤•ą„ą¤Ÿą„‚ą¤¬ą¤°|ą¤…ą¤•ą„ą¤Ÿą„‚\.?|ą¤Øą¤µą¤®ą„ą¤¬ą¤°|ą¤Øą¤µą¤‚ą¤¬ą¤°|ą¤Øą¤µ\.?|ą¤¦ą¤æą¤øą¤®ą„ą¤¬ą¤°|ą¤¦ą¤æą¤øą¤‚ą¤¬ą¤°|ą¤¦ą¤æą¤ø\.?)/i,monthsStrictRegex:/^(ą¤œą¤Øą¤µą¤°ą„€?|ą¤«ą¤¼ą¤°ą¤µą¤°ą„€|ą¤«ą¤°ą¤µą¤°ą„€?|ą¤®ą¤¾ą¤°ą„ą¤š?|ą¤…ą¤Ŗą„ą¤°ą„ˆą¤²?|ą¤®ą¤ˆ?|ą¤œą„‚ą¤Ø?|ą¤œą„ą¤²ą¤¾ą¤ˆ?|ą¤…ą¤—ą¤øą„ą¤¤?|ą¤øą¤æą¤¤ą¤®ą„ą¤¬ą¤°|ą¤øą¤æą¤¤ą¤‚ą¤¬ą¤°|ą¤øą¤æą¤¤?\.?|ą¤…ą¤•ą„ą¤Ÿą„‚ą¤¬ą¤°|ą¤…ą¤•ą„ą¤Ÿą„‚\.?|ą¤Øą¤µą¤®ą„ą¤¬ą¤°|ą¤Øą¤µą¤‚ą¤¬ą¤°?|ą¤¦ą¤æą¤øą¤®ą„ą¤¬ą¤°|ą¤¦ą¤æą¤øą¤‚ą¤¬ą¤°?)/i,monthsShortStrictRegex:/^(ą¤œą¤Ø\.?|ą¤«ą¤¼ą¤°\.?|ą¤®ą¤¾ą¤°ą„ą¤š?|ą¤…ą¤Ŗą„ą¤°ą„ˆ\.?|ą¤®ą¤ˆ?|ą¤œą„‚ą¤Ø?|ą¤œą„ą¤²\.?|ą¤…ą¤—\.?|ą¤øą¤æą¤¤\.?|ą¤…ą¤•ą„ą¤Ÿą„‚\.?|ą¤Øą¤µ\.?|ą¤¦ą¤æą¤ø\.?)/i,calendar:{sameDay:"[ą¤†ą¤œ] LT",nextDay:"[ą¤•ą¤²] LT",nextWeek:"dddd, LT",lastDay:"[ą¤•ą¤²] LT",lastWeek:"[ą¤Ŗą¤æą¤›ą¤²ą„‡] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą¤®ą„‡ą¤‚",past:"%s ą¤Ŗą¤¹ą¤²ą„‡",s:"ą¤•ą„ą¤› ą¤¹ą„€ ą¤•ą„ą¤·ą¤£",ss:"%d ą¤øą„‡ą¤•ą¤‚ą¤”",m:"ą¤ą¤• ą¤®ą¤æą¤Øą¤Ÿ",mm:"%d ą¤®ą¤æą¤Øą¤Ÿ",h:"ą¤ą¤• ą¤˜ą¤‚ą¤Ÿą¤¾",hh:"%d ą¤˜ą¤‚ą¤Ÿą„‡",d:"ą¤ą¤• ą¤¦ą¤æą¤Ø",dd:"%d ą¤¦ą¤æą¤Ø",M:"ą¤ą¤• ą¤®ą¤¹ą„€ą¤Øą„‡",MM:"%d ą¤®ą¤¹ą„€ą¤Øą„‡",y:"ą¤ą¤• ą¤µą¤°ą„ą¤·",yy:"%d ą¤µą¤°ą„ą¤·"},preparse:function(e){return e.replace(/[ą„§ą„Øą„©ą„Ŗą„«ą„¬ą„­ą„®ą„Æą„¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą¤°ą¤¾ą¤¤|ą¤øą„ą¤¬ą¤¹|ą¤¦ą„‹ą¤Ŗą¤¹ą¤°|ą¤¶ą¤¾ą¤®/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą¤°ą¤¾ą¤¤")return e<4?e:e+12;else if(t==="ą¤øą„ą¤¬ą¤¹")return e;else if(t==="ą¤¦ą„‹ą¤Ŗą¤¹ą¤°")return e>=10?e:e+12;else if(t==="ą¤¶ą¤¾ą¤®")return e+12},meridiem:function(e,t,n){if(e<4)return"ą¤°ą¤¾ą¤¤";else if(e<10)return"ą¤øą„ą¤¬ą¤¹";else if(e<17)return"ą¤¦ą„‹ą¤Ŗą¤¹ą¤°";else if(e<20)return"ą¤¶ą¤¾ą¤®";else return"ą¤°ą¤¾ą¤¤"},week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function t(e,t,n){var a=e+" ";switch(n){case"ss":if(e===1)a+="sekunda";else if(e===2||e===3||e===4)a+="sekunde";else a+="sekundi";return a;case"m":return t?"jedna minuta":"jedne minute";case"mm":if(e===1)a+="minuta";else if(e===2||e===3||e===4)a+="minute";else a+="minuta";return a;case"h":return t?"jedan sat":"jednog sata";case"hh":if(e===1)a+="sat";else if(e===2||e===3||e===4)a+="sata";else a+="sati";return a;case"dd":if(e===1)a+="dan";else a+="dana";return a;case"MM":if(e===1)a+="mjesec";else if(e===2||e===3||e===4)a+="mjeseca";else a+="mjeseci";return a;case"yy":if(e===1)a+="godina";else if(e===2||e===3||e===4)a+="godine";else a+="godina";return a}}var n;e.defineLocale("hr",{months:{format:"siječnja_veljače_ožujka_travnja_svibnja_lipnja_srpnja_kolovoza_rujna_listopada_studenoga_prosinca".split("_"),standalone:"siječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac".split("_")},monthsShort:"sij._velj._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.".split("_"),monthsParseExact:true,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"Do MMMM YYYY",LLL:"Do MMMM YYYY H:mm",LLLL:"dddd, Do MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:return"[proÅ”lu] [nedjelju] [u] LT";case 3:return"[proÅ”lu] [srijedu] [u] LT";case 6:return"[proÅ”le] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[proÅ”li] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",ss:t,m:t,mm:t,h:t,hh:t,d:"dan",dd:t,M:"mjesec",MM:t,y:"godinu",yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function t(e,t,n){var a=e+" ";switch(n){case"ss":if(e===1)a+="sekunda";else if(e===2||e===3||e===4)a+="sekunde";else a+="sekundi";return a;case"m":return t?"jedna minuta":"jedne minute";case"mm":if(e===1)a+="minuta";else if(e===2||e===3||e===4)a+="minute";else a+="minuta";return a;case"h":return t?"jedan sat":"jednog sata";case"hh":if(e===1)a+="sat";else if(e===2||e===3||e===4)a+="sata";else a+="sati";return a;case"dd":if(e===1)a+="dan";else a+="dana";return a;case"MM":if(e===1)a+="mjesec";else if(e===2||e===3||e===4)a+="mjeseca";else a+="mjeseci";return a;case"yy":if(e===1)a+="godina";else if(e===2||e===3||e===4)a+="godine";else a+="godina";return a}}var n;e.defineLocale("hr",{months:{format:"siječnja_veljače_ožujka_travnja_svibnja_lipnja_srpnja_kolovoza_rujna_listopada_studenoga_prosinca".split("_"),standalone:"siječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac".split("_")},monthsShort:"sij._velj._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.".split("_"),monthsParseExact:true,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"Do MMMM YYYY",LLL:"Do MMMM YYYY H:mm",LLLL:"dddd, Do MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:return"[proÅ”lu] [nedjelju] [u] LT";case 3:return"[proÅ”lu] [srijedu] [u] LT";case 6:return"[proÅ”le] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[proÅ”li] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",ss:t,m:t,mm:t,h:t,hh:t,d:"dan",dd:t,M:"mjesec",MM:t,y:"godinu",yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t="vasĆ”rnap hĆ©tfőn kedden szerdĆ”n csĆ¼tƶrtƶkƶn pĆ©nteken szombaton".split(" "),n;function a(e,t,n,a){var r=e;switch(n){case"s":return a||t?"nĆ©hĆ”ny mĆ”sodperc":"nĆ©hĆ”ny mĆ”sodperce";case"ss":return r+(a||t)?" mĆ”sodperc":" mĆ”sodperce";case"m":return"egy"+(a||t?" perc":" perce");case"mm":return r+(a||t?" perc":" perce");case"h":return"egy"+(a||t?" Ć³ra":" Ć³rĆ”ja");case"hh":return r+(a||t?" Ć³ra":" Ć³rĆ”ja");case"d":return"egy"+(a||t?" nap":" napja");case"dd":return r+(a||t?" nap":" napja");case"M":return"egy"+(a||t?" hĆ³nap":" hĆ³napja");case"MM":return r+(a||t?" hĆ³nap":" hĆ³napja");case"y":return"egy"+(a||t?" Ć©v":" Ć©ve");case"yy":return r+(a||t?" Ć©v":" Ć©ve")}return""}function r(e){return(e?"":"[mĆŗlt] ")+"["+t[this.day()]+"] LT[-kor]"}e.defineLocale("hu",{months:"januĆ”r_februĆ”r_mĆ”rcius_Ć”prilis_mĆ”jus_jĆŗnius_jĆŗlius_augusztus_szeptember_oktĆ³ber_november_december".split("_"),monthsShort:"jan._feb._mĆ”rc._Ć”pr._mĆ”j._jĆŗn._jĆŗl._aug._szept._okt._nov._dec.".split("_"),monthsParseExact:true,weekdays:"vasĆ”rnap_hĆ©tfő_kedd_szerda_csĆ¼tƶrtƶk_pĆ©ntek_szombat".split("_"),weekdaysShort:"vas_hĆ©t_kedd_sze_csĆ¼t_pĆ©n_szo".split("_"),weekdaysMin:"v_h_k_sze_cs_p_szo".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"YYYY.MM.DD.",LL:"YYYY. MMMM D.",LLL:"YYYY. MMMM D. H:mm",LLLL:"YYYY. MMMM D., dddd H:mm"},meridiemParse:/de|du/i,isPM:function(e){return e.charAt(1).toLowerCase()==="u"},meridiem:function(e,t,n){if(e<12)return n===true?"de":"DE";else return n===true?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return r.call(this,true)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return r.call(this,false)},sameElse:"L"},relativeTime:{future:"%s mĆŗlva",past:"%s",s:a,ss:a,m:a,mm:a,h:a,hh:a,d:a,dd:a,M:a,MM:a,y:a,yy:a},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t="vasĆ”rnap hĆ©tfőn kedden szerdĆ”n csĆ¼tƶrtƶkƶn pĆ©nteken szombaton".split(" "),n;function a(e,t,n,a){var r=e;switch(n){case"s":return a||t?"nĆ©hĆ”ny mĆ”sodperc":"nĆ©hĆ”ny mĆ”sodperce";case"ss":return r+(a||t)?" mĆ”sodperc":" mĆ”sodperce";case"m":return"egy"+(a||t?" perc":" perce");case"mm":return r+(a||t?" perc":" perce");case"h":return"egy"+(a||t?" Ć³ra":" Ć³rĆ”ja");case"hh":return r+(a||t?" Ć³ra":" Ć³rĆ”ja");case"d":return"egy"+(a||t?" nap":" napja");case"dd":return r+(a||t?" nap":" napja");case"M":return"egy"+(a||t?" hĆ³nap":" hĆ³napja");case"MM":return r+(a||t?" hĆ³nap":" hĆ³napja");case"y":return"egy"+(a||t?" Ć©v":" Ć©ve");case"yy":return r+(a||t?" Ć©v":" Ć©ve")}return""}function r(e){return(e?"":"[mĆŗlt] ")+"["+t[this.day()]+"] LT[-kor]"}e.defineLocale("hu",{months:"januĆ”r_februĆ”r_mĆ”rcius_Ć”prilis_mĆ”jus_jĆŗnius_jĆŗlius_augusztus_szeptember_oktĆ³ber_november_december".split("_"),monthsShort:"jan._feb._mĆ”rc._Ć”pr._mĆ”j._jĆŗn._jĆŗl._aug._szept._okt._nov._dec.".split("_"),monthsParseExact:true,weekdays:"vasĆ”rnap_hĆ©tfő_kedd_szerda_csĆ¼tƶrtƶk_pĆ©ntek_szombat".split("_"),weekdaysShort:"vas_hĆ©t_kedd_sze_csĆ¼t_pĆ©n_szo".split("_"),weekdaysMin:"v_h_k_sze_cs_p_szo".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"YYYY.MM.DD.",LL:"YYYY. MMMM D.",LLL:"YYYY. MMMM D. H:mm",LLLL:"YYYY. MMMM D., dddd H:mm"},meridiemParse:/de|du/i,isPM:function(e){return e.charAt(1).toLowerCase()==="u"},meridiem:function(e,t,n){if(e<12)return n===true?"de":"DE";else return n===true?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return r.call(this,true)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return r.call(this,false)},sameElse:"L"},relativeTime:{future:"%s mĆŗlva",past:"%s",s:a,ss:a,m:a,mm:a,h:a,hh:a,d:a,dd:a,M:a,MM:a,y:a,yy:a},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("hy-am",{months:{format:"Õ°ÕøÖ‚Õ¶Õ¾Õ”Ö€Õ«_ÖƒÕ„ÕæÖ€Õ¾Õ”Ö€Õ«_Õ“Õ”Ö€ÕæÕ«_Õ”Õŗրիլի_Õ“Õ”ÕµÕ«Õ½Õ«_Õ°Õøւնիսի_Õ°Õøւլիսի_օգÕøÕ½ÕæÕøÕ½Õ«_Õ½Õ„ÕŗÕæÕ„Õ“Õ¢Õ„Ö€Õ«_Õ°ÕøÕÆÕæÕ„Õ“Õ¢Õ„Ö€Õ«_Õ¶ÕøÕµÕ„Õ“Õ¢Õ„Ö€Õ«_Õ¤Õ„ÕÆÕæÕ„Õ“Õ¢Õ„Ö€Õ«".split("_"),standalone:"Õ°ÕøÖ‚Õ¶Õ¾Õ”Ö€_ÖƒÕ„ÕæÖ€Õ¾Õ”Ö€_Õ“Õ”Ö€Õæ_Õ”Õŗրիլ_Õ“Õ”ÕµÕ«Õ½_Õ°Õøւնիս_Õ°Õøւլիս_օգÕøÕ½ÕæÕøÕ½_Õ½Õ„ÕŗÕæÕ„Õ“Õ¢Õ„Ö€_Õ°ÕøÕÆÕæÕ„Õ“Õ¢Õ„Ö€_Õ¶ÕøÕµÕ„Õ“Õ¢Õ„Ö€_Õ¤Õ„ÕÆÕæÕ„Õ“Õ¢Õ„Ö€".split("_")},monthsShort:"Õ°Õ¶Õ¾_փÕæր_Õ“Ö€Õæ_Õ”Õŗր_Õ“ÕµÕ½_Õ°Õ¶Õ½_Õ°Õ¬Õ½_օգս_Õ½ÕŗÕæ_Õ°ÕÆÕæ_Õ¶Õ“Õ¢_Õ¤ÕÆÕæ".split("_"),weekdays:"ÕÆÕ«Ö€Õ”ÕÆÕ«_Õ„Ö€ÕÆÕøÖ‚Õ·Õ”Õ¢Õ©Õ«_Õ„Ö€Õ„Ö„Õ·Õ”Õ¢Õ©Õ«_Õ¹ÕøÖ€Õ„Ö„Õ·Õ”Õ¢Õ©Õ«_Õ°Õ«Õ¶Õ£Õ·Õ”Õ¢Õ©Õ«_ÕøÖ‚Ö€Õ¢Õ”Õ©_Õ·Õ”Õ¢Õ”Õ©".split("_"),weekdaysShort:"ÕÆրÕÆ_Õ„Ö€ÕÆ_Õ„Ö€Ö„_չրք_Õ°Õ¶Õ£_Õøւրբ_Õ·Õ¢Õ©".split("_"),weekdaysMin:"ÕÆրÕÆ_Õ„Ö€ÕÆ_Õ„Ö€Ö„_չրք_Õ°Õ¶Õ£_Õøւրբ_Õ·Õ¢Õ©".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY Õ©.",LLL:"D MMMM YYYY Õ©., HH:mm",LLLL:"dddd, D MMMM YYYY Õ©., HH:mm"},calendar:{sameDay:"[Õ”ÕµÕ½Ö…Ö€] LT",nextDay:"[Õ¾Õ”Õ²ÕØ] LT",lastDay:"[Õ„Ö€Õ„ÕÆ] LT",nextWeek:function(){return"dddd [օրÕØ ÕŖÕ”Õ“ÕØ] LT"},lastWeek:function(){return"[ՔնցՔծ] dddd [օրÕØ ÕŖÕ”Õ“ÕØ] LT"},sameElse:"L"},relativeTime:{future:"%s Õ°Õ„ÕæÕø",past:"%s Õ”Õ¼Õ”Õ»",s:"Õ“Õ« Ö„Õ”Õ¶Õ« Õ¾Õ”ÕµÖ€ÕÆÕµÕ”Õ¶",ss:"%d Õ¾Õ”ÕµÖ€ÕÆÕµÕ”Õ¶",m:"րÕøÕŗÕ„",mm:"%d րÕøÕŗÕ„",h:"ÕŖÕ”Õ“",hh:"%d ÕŖÕ”Õ“",d:"օր",dd:"%d օր",M:"Õ”Õ“Õ«Õ½",MM:"%d Õ”Õ“Õ«Õ½",y:"ÕæÕ”Ö€Õ«",yy:"%d ÕæÕ”Ö€Õ«"},meridiemParse:/Õ£Õ«Õ·Õ„Ö€Õ¾Õ”|Õ”Õ¼Õ”Õ¾ÕøÕæÕ¾Õ”|ցՄրՄÕÆÕ¾Õ”|Õ„Ö€Õ„ÕÆÕøÕµÕ”Õ¶/,isPM:function(e){return/^(ցՄրՄÕÆÕ¾Õ”|Õ„Ö€Õ„ÕÆÕøÕµÕ”Õ¶)$/.test(e)},meridiem:function(e){if(e<4)return"Õ£Õ«Õ·Õ„Ö€Õ¾Õ”";else if(e<12)return"Õ”Õ¼Õ”Õ¾ÕøÕæÕ¾Õ”";else if(e<17)return"ցՄրՄÕÆÕ¾Õ”";else return"Õ„Ö€Õ„ÕÆÕøÕµÕ”Õ¶"},dayOfMonthOrdinalParse:/\d{1,2}|\d{1,2}-(Õ«Õ¶|րդ)/,ordinal:function(e,t){switch(t){case"DDD":case"w":case"W":case"DDDo":if(e===1)return e+"-Õ«Õ¶";return e+"-րդ";default:return e}},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("hy-am",{months:{format:"Õ°ÕøÖ‚Õ¶Õ¾Õ”Ö€Õ«_ÖƒÕ„ÕæÖ€Õ¾Õ”Ö€Õ«_Õ“Õ”Ö€ÕæÕ«_Õ”Õŗրիլի_Õ“Õ”ÕµÕ«Õ½Õ«_Õ°Õøւնիսի_Õ°Õøւլիսի_օգÕøÕ½ÕæÕøÕ½Õ«_Õ½Õ„ÕŗÕæÕ„Õ“Õ¢Õ„Ö€Õ«_Õ°ÕøÕÆÕæÕ„Õ“Õ¢Õ„Ö€Õ«_Õ¶ÕøÕµÕ„Õ“Õ¢Õ„Ö€Õ«_Õ¤Õ„ÕÆÕæÕ„Õ“Õ¢Õ„Ö€Õ«".split("_"),standalone:"Õ°ÕøÖ‚Õ¶Õ¾Õ”Ö€_ÖƒÕ„ÕæÖ€Õ¾Õ”Ö€_Õ“Õ”Ö€Õæ_Õ”Õŗրիլ_Õ“Õ”ÕµÕ«Õ½_Õ°Õøւնիս_Õ°Õøւլիս_օգÕøÕ½ÕæÕøÕ½_Õ½Õ„ÕŗÕæÕ„Õ“Õ¢Õ„Ö€_Õ°ÕøÕÆÕæÕ„Õ“Õ¢Õ„Ö€_Õ¶ÕøÕµÕ„Õ“Õ¢Õ„Ö€_Õ¤Õ„ÕÆÕæÕ„Õ“Õ¢Õ„Ö€".split("_")},monthsShort:"Õ°Õ¶Õ¾_փÕæր_Õ“Ö€Õæ_Õ”Õŗր_Õ“ÕµÕ½_Õ°Õ¶Õ½_Õ°Õ¬Õ½_օգս_Õ½ÕŗÕæ_Õ°ÕÆÕæ_Õ¶Õ“Õ¢_Õ¤ÕÆÕæ".split("_"),weekdays:"ÕÆÕ«Ö€Õ”ÕÆÕ«_Õ„Ö€ÕÆÕøÖ‚Õ·Õ”Õ¢Õ©Õ«_Õ„Ö€Õ„Ö„Õ·Õ”Õ¢Õ©Õ«_Õ¹ÕøÖ€Õ„Ö„Õ·Õ”Õ¢Õ©Õ«_Õ°Õ«Õ¶Õ£Õ·Õ”Õ¢Õ©Õ«_ÕøÖ‚Ö€Õ¢Õ”Õ©_Õ·Õ”Õ¢Õ”Õ©".split("_"),weekdaysShort:"ÕÆրÕÆ_Õ„Ö€ÕÆ_Õ„Ö€Ö„_չրք_Õ°Õ¶Õ£_Õøւրբ_Õ·Õ¢Õ©".split("_"),weekdaysMin:"ÕÆրÕÆ_Õ„Ö€ÕÆ_Õ„Ö€Ö„_չրք_Õ°Õ¶Õ£_Õøւրբ_Õ·Õ¢Õ©".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY Õ©.",LLL:"D MMMM YYYY Õ©., HH:mm",LLLL:"dddd, D MMMM YYYY Õ©., HH:mm"},calendar:{sameDay:"[Õ”ÕµÕ½Ö…Ö€] LT",nextDay:"[Õ¾Õ”Õ²ÕØ] LT",lastDay:"[Õ„Ö€Õ„ÕÆ] LT",nextWeek:function(){return"dddd [օրÕØ ÕŖÕ”Õ“ÕØ] LT"},lastWeek:function(){return"[ՔնցՔծ] dddd [օրÕØ ÕŖÕ”Õ“ÕØ] LT"},sameElse:"L"},relativeTime:{future:"%s Õ°Õ„ÕæÕø",past:"%s Õ”Õ¼Õ”Õ»",s:"Õ“Õ« Ö„Õ”Õ¶Õ« Õ¾Õ”ÕµÖ€ÕÆÕµÕ”Õ¶",ss:"%d Õ¾Õ”ÕµÖ€ÕÆÕµÕ”Õ¶",m:"րÕøÕŗÕ„",mm:"%d րÕøÕŗÕ„",h:"ÕŖÕ”Õ“",hh:"%d ÕŖÕ”Õ“",d:"օր",dd:"%d օր",M:"Õ”Õ“Õ«Õ½",MM:"%d Õ”Õ“Õ«Õ½",y:"ÕæÕ”Ö€Õ«",yy:"%d ÕæÕ”Ö€Õ«"},meridiemParse:/Õ£Õ«Õ·Õ„Ö€Õ¾Õ”|Õ”Õ¼Õ”Õ¾ÕøÕæÕ¾Õ”|ցՄրՄÕÆÕ¾Õ”|Õ„Ö€Õ„ÕÆÕøÕµÕ”Õ¶/,isPM:function(e){return/^(ցՄրՄÕÆÕ¾Õ”|Õ„Ö€Õ„ÕÆÕøÕµÕ”Õ¶)$/.test(e)},meridiem:function(e){if(e<4)return"Õ£Õ«Õ·Õ„Ö€Õ¾Õ”";else if(e<12)return"Õ”Õ¼Õ”Õ¾ÕøÕæÕ¾Õ”";else if(e<17)return"ցՄրՄÕÆÕ¾Õ”";else return"Õ„Ö€Õ„ÕÆÕøÕµÕ”Õ¶"},dayOfMonthOrdinalParse:/\d{1,2}|\d{1,2}-(Õ«Õ¶|րդ)/,ordinal:function(e,t){switch(t){case"DDD":case"w":case"W":case"DDDo":if(e===1)return e+"-Õ«Õ¶";return e+"-րդ";default:return e}},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("id",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Agt_Sep_Okt_Nov_Des".split("_"),weekdays:"Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"),weekdaysShort:"Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|siang|sore|malam/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="pagi")return e;else if(t==="siang")return e>=11?e:e+12;else if(t==="sore"||t==="malam")return e+12},meridiem:function(e,t,n){if(e<11)return"pagi";else if(e<15)return"siang";else if(e<19)return"sore";else return"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Besok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kemarin pukul] LT",lastWeek:"dddd [lalu pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lalu",s:"beberapa detik",ss:"%d detik",m:"semenit",mm:"%d menit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("id",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Agt_Sep_Okt_Nov_Des".split("_"),weekdays:"Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"),weekdaysShort:"Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|siang|sore|malam/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="pagi")return e;else if(t==="siang")return e>=11?e:e+12;else if(t==="sore"||t==="malam")return e+12},meridiem:function(e,t,n){if(e<11)return"pagi";else if(e<15)return"siang";else if(e<19)return"sore";else return"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Besok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kemarin pukul] LT",lastWeek:"dddd [lalu pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lalu",s:"beberapa detik",ss:"%d detik",m:"semenit",mm:"%d menit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function o(e){if(e%100===11)return true;else if(e%10===1)return false;return true}function t(e,t,n,a){var r=e+" ";switch(n){case"s":return t||a?"nokkrar sekĆŗndur":"nokkrum sekĆŗndum";case"ss":if(o(e))return r+(t||a?"sekĆŗndur":"sekĆŗndum");return r+"sekĆŗnda";case"m":return t?"mĆ­nĆŗta":"mĆ­nĆŗtu";case"mm":if(o(e))return r+(t||a?"mĆ­nĆŗtur":"mĆ­nĆŗtum");else if(t)return r+"mĆ­nĆŗta";return r+"mĆ­nĆŗtu";case"hh":if(o(e))return r+(t||a?"klukkustundir":"klukkustundum");return r+"klukkustund";case"d":if(t)return"dagur";return a?"dag":"degi";case"dd":if(o(e)){if(t)return r+"dagar";return r+(a?"daga":"dƶgum")}else if(t)return r+"dagur";return r+(a?"dag":"degi");case"M":if(t)return"mĆ”nuĆ°ur";return a?"mĆ”nuĆ°":"mĆ”nuĆ°i";case"MM":if(o(e)){if(t)return r+"mĆ”nuĆ°ir";return r+(a?"mĆ”nuĆ°i":"mĆ”nuĆ°um")}else if(t)return r+"mĆ”nuĆ°ur";return r+(a?"mĆ”nuĆ°":"mĆ”nuĆ°i");case"y":return t||a?"Ć”r":"Ć”ri";case"yy":if(o(e))return r+(t||a?"Ć”r":"Ć”rum");return r+(t||a?"Ć”r":"Ć”ri")}}var n;e.defineLocale("is",{months:"janĆŗar_febrĆŗar_mars_aprĆ­l_maĆ­_jĆŗnĆ­_jĆŗlĆ­_Ć”gĆŗst_september_oktĆ³ber_nĆ³vember_desember".split("_"),monthsShort:"jan_feb_mar_apr_maĆ­_jĆŗn_jĆŗl_Ć”gĆŗ_sep_okt_nĆ³v_des".split("_"),weekdays:"sunnudagur_mĆ”nudagur_Ć¾riĆ°judagur_miĆ°vikudagur_fimmtudagur_fƶstudagur_laugardagur".split("_"),weekdaysShort:"sun_mĆ”n_Ć¾ri_miĆ°_fim_fƶs_lau".split("_"),weekdaysMin:"Su_MĆ”_ƞr_Mi_Fi_Fƶ_La".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] H:mm",LLLL:"dddd, D. MMMM YYYY [kl.] H:mm"},calendar:{sameDay:"[Ć­ dag kl.] LT",nextDay:"[Ć” morgun kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[Ć­ gƦr kl.] LT",lastWeek:"[sĆ­Ć°asta] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"eftir %s",past:"fyrir %s sĆ­Ć°an",s:t,ss:t,m:t,mm:t,h:"klukkustund",hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function o(e){if(e%100===11)return true;else if(e%10===1)return false;return true}function t(e,t,n,a){var r=e+" ";switch(n){case"s":return t||a?"nokkrar sekĆŗndur":"nokkrum sekĆŗndum";case"ss":if(o(e))return r+(t||a?"sekĆŗndur":"sekĆŗndum");return r+"sekĆŗnda";case"m":return t?"mĆ­nĆŗta":"mĆ­nĆŗtu";case"mm":if(o(e))return r+(t||a?"mĆ­nĆŗtur":"mĆ­nĆŗtum");else if(t)return r+"mĆ­nĆŗta";return r+"mĆ­nĆŗtu";case"hh":if(o(e))return r+(t||a?"klukkustundir":"klukkustundum");return r+"klukkustund";case"d":if(t)return"dagur";return a?"dag":"degi";case"dd":if(o(e)){if(t)return r+"dagar";return r+(a?"daga":"dƶgum")}else if(t)return r+"dagur";return r+(a?"dag":"degi");case"M":if(t)return"mĆ”nuĆ°ur";return a?"mĆ”nuĆ°":"mĆ”nuĆ°i";case"MM":if(o(e)){if(t)return r+"mĆ”nuĆ°ir";return r+(a?"mĆ”nuĆ°i":"mĆ”nuĆ°um")}else if(t)return r+"mĆ”nuĆ°ur";return r+(a?"mĆ”nuĆ°":"mĆ”nuĆ°i");case"y":return t||a?"Ć”r":"Ć”ri";case"yy":if(o(e))return r+(t||a?"Ć”r":"Ć”rum");return r+(t||a?"Ć”r":"Ć”ri")}}var n;e.defineLocale("is",{months:"janĆŗar_febrĆŗar_mars_aprĆ­l_maĆ­_jĆŗnĆ­_jĆŗlĆ­_Ć”gĆŗst_september_oktĆ³ber_nĆ³vember_desember".split("_"),monthsShort:"jan_feb_mar_apr_maĆ­_jĆŗn_jĆŗl_Ć”gĆŗ_sep_okt_nĆ³v_des".split("_"),weekdays:"sunnudagur_mĆ”nudagur_Ć¾riĆ°judagur_miĆ°vikudagur_fimmtudagur_fƶstudagur_laugardagur".split("_"),weekdaysShort:"sun_mĆ”n_Ć¾ri_miĆ°_fim_fƶs_lau".split("_"),weekdaysMin:"Su_MĆ”_ƞr_Mi_Fi_Fƶ_La".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] H:mm",LLLL:"dddd, D. MMMM YYYY [kl.] H:mm"},calendar:{sameDay:"[Ć­ dag kl.] LT",nextDay:"[Ć” morgun kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[Ć­ gƦr kl.] LT",lastWeek:"[sĆ­Ć°asta] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"eftir %s",past:"fyrir %s sĆ­Ć°an",s:t,ss:t,m:t,mm:t,h:"klukkustund",hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("it",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"domenica_lunedƬ_martedƬ_mercoledƬ_giovedƬ_venerdƬ_sabato".split("_"),weekdaysShort:"dom_lun_mar_mer_gio_ven_sab".split("_"),weekdaysMin:"do_lu_ma_me_gi_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:function(){return"[Oggi a"+(this.hours()>1?"lle ":this.hours()===0?" ":"ll'")+"]LT"},nextDay:function(){return"[Domani a"+(this.hours()>1?"lle ":this.hours()===0?" ":"ll'")+"]LT"},nextWeek:function(){return"dddd [a"+(this.hours()>1?"lle ":this.hours()===0?" ":"ll'")+"]LT"},lastDay:function(){return"[Ieri a"+(this.hours()>1?"lle ":this.hours()===0?" ":"ll'")+"]LT"},lastWeek:function(){switch(this.day()){case 0:return"[La scorsa] dddd [a"+(this.hours()>1?"lle ":this.hours()===0?" ":"ll'")+"]LT";default:return"[Lo scorso] dddd [a"+(this.hours()>1?"lle ":this.hours()===0?" ":"ll'")+"]LT"}},sameElse:"L"},relativeTime:{future:"tra %s",past:"%s fa",s:"alcuni secondi",ss:"%d secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",w:"una settimana",ww:"%d settimane",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("it",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"domenica_lunedƬ_martedƬ_mercoledƬ_giovedƬ_venerdƬ_sabato".split("_"),weekdaysShort:"dom_lun_mar_mer_gio_ven_sab".split("_"),weekdaysMin:"do_lu_ma_me_gi_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:function(){return"[Oggi a"+(this.hours()>1?"lle ":this.hours()===0?" ":"ll'")+"]LT"},nextDay:function(){return"[Domani a"+(this.hours()>1?"lle ":this.hours()===0?" ":"ll'")+"]LT"},nextWeek:function(){return"dddd [a"+(this.hours()>1?"lle ":this.hours()===0?" ":"ll'")+"]LT"},lastDay:function(){return"[Ieri a"+(this.hours()>1?"lle ":this.hours()===0?" ":"ll'")+"]LT"},lastWeek:function(){switch(this.day()){case 0:return"[La scorsa] dddd [a"+(this.hours()>1?"lle ":this.hours()===0?" ":"ll'")+"]LT";default:return"[Lo scorso] dddd [a"+(this.hours()>1?"lle ":this.hours()===0?" ":"ll'")+"]LT"}},sameElse:"L"},relativeTime:{future:"tra %s",past:"%s fa",s:"alcuni secondi",ss:"%d secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",w:"una settimana",ww:"%d settimane",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("it-ch",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"domenica_lunedƬ_martedƬ_mercoledƬ_giovedƬ_venerdƬ_sabato".split("_"),weekdaysShort:"dom_lun_mar_mer_gio_ven_sab".split("_"),weekdaysMin:"do_lu_ma_me_gi_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Oggi alle] LT",nextDay:"[Domani alle] LT",nextWeek:"dddd [alle] LT",lastDay:"[Ieri alle] LT",lastWeek:function(){switch(this.day()){case 0:return"[la scorsa] dddd [alle] LT";default:return"[lo scorso] dddd [alle] LT"}},sameElse:"L"},relativeTime:{future:function(e){return(/^[0-9].+$/.test(e)?"tra":"in")+" "+e},past:"%s fa",s:"alcuni secondi",ss:"%d secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("it-ch",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"domenica_lunedƬ_martedƬ_mercoledƬ_giovedƬ_venerdƬ_sabato".split("_"),weekdaysShort:"dom_lun_mar_mer_gio_ven_sab".split("_"),weekdaysMin:"do_lu_ma_me_gi_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Oggi alle] LT",nextDay:"[Domani alle] LT",nextWeek:"dddd [alle] LT",lastDay:"[Ieri alle] LT",lastWeek:function(){switch(this.day()){case 0:return"[la scorsa] dddd [alle] LT";default:return"[lo scorso] dddd [alle] LT"}},sameElse:"L"},relativeTime:{future:function(e){return(/^[0-9].+$/.test(e)?"tra":"in")+" "+e},past:"%s fa",s:"alcuni secondi",ss:"%d secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("ja",{eras:[{since:"2019-05-01",offset:1,name:"令和",narrow:"ć‹æ",abbr:"R"},{since:"1989-01-08",until:"2019-04-30",offset:1,name:"å¹³ęˆ",narrow:"ć»",abbr:"H"},{since:"1926-12-25",until:"1989-01-07",offset:1,name:"ę˜­å’Œ",narrow:"ć¼",abbr:"S"},{since:"1912-07-30",until:"1926-12-24",offset:1,name:"å¤§ę­£",narrow:"ć½",abbr:"T"},{since:"1873-01-01",until:"1912-07-29",offset:6,name:"ę˜Žę²»",narrow:"ć¾",abbr:"M"},{since:"0001-01-01",until:"1873-12-31",offset:1,name:"č„æꚦ",narrow:"AD",abbr:"AD"},{since:"0000-12-31",until:-Infinity,offset:1,name:"ē“€å…ƒå‰",narrow:"BC",abbr:"BC"}],eraYearOrdinalRegex:/(元|\d+)幓/,eraYearOrdinalParse:function(e,t){return t[1]==="元"?1:parseInt(t[1]||e,10)},months:"1꜈_2꜈_3꜈_4꜈_5꜈_6꜈_7꜈_8꜈_9꜈_10꜈_11꜈_12꜈".split("_"),monthsShort:"1꜈_2꜈_3꜈_4꜈_5꜈_6꜈_7꜈_8꜈_9꜈_10꜈_11꜈_12꜈".split("_"),weekdays:"ę—„ę›œę—„_ęœˆę›œę—„_ē«ę›œę—„_ę°“ę›œę—„_ęœØę›œę—„_é‡‘ę›œę—„_åœŸę›œę—„".split("_"),weekdaysShort:"ę—„_꜈_ē«_ę°“_ęœØ_金_土".split("_"),weekdaysMin:"ę—„_꜈_ē«_ę°“_ęœØ_金_土".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY幓M꜈Dę—„",LLL:"YYYY幓M꜈Dę—„ HH:mm",LLLL:"YYYY幓M꜈Dę—„ dddd HH:mm",l:"YYYY/MM/DD",ll:"YYYY幓M꜈Dę—„",lll:"YYYY幓M꜈Dę—„ HH:mm",llll:"YYYY幓M꜈Dę—„(ddd) HH:mm"},meridiemParse:/午前|午後/i,isPM:function(e){return e==="午後"},meridiem:function(e,t,n){if(e<12)return"午前";else return"午後"},calendar:{sameDay:"[今ꗄ] LT",nextDay:"[ę˜Žę—„] LT",nextWeek:function(e){if(e.week()!==this.week())return"[ę„é€±]dddd LT";else return"dddd LT"},lastDay:"[ę˜Øę—„] LT",lastWeek:function(e){if(this.week()!==e.week())return"[先週]dddd LT";else return"dddd LT"},sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}ę—„/,ordinal:function(e,t){switch(t){case"y":return e===1?"元幓":e+"幓";case"d":case"D":case"DDD":return e+"ę—„";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"ꕰē§’",ss:"%dē§’",m:"1分",mm:"%d分",h:"1Ꙃ間",hh:"%dꙂ間",d:"1ę—„",dd:"%dę—„",M:"1ćƒ¶ęœˆ",MM:"%dćƒ¶ęœˆ",y:"1幓",yy:"%d幓"}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("ja",{eras:[{since:"2019-05-01",offset:1,name:"令和",narrow:"ć‹æ",abbr:"R"},{since:"1989-01-08",until:"2019-04-30",offset:1,name:"å¹³ęˆ",narrow:"ć»",abbr:"H"},{since:"1926-12-25",until:"1989-01-07",offset:1,name:"ę˜­å’Œ",narrow:"ć¼",abbr:"S"},{since:"1912-07-30",until:"1926-12-24",offset:1,name:"å¤§ę­£",narrow:"ć½",abbr:"T"},{since:"1873-01-01",until:"1912-07-29",offset:6,name:"ę˜Žę²»",narrow:"ć¾",abbr:"M"},{since:"0001-01-01",until:"1873-12-31",offset:1,name:"č„æꚦ",narrow:"AD",abbr:"AD"},{since:"0000-12-31",until:-Infinity,offset:1,name:"ē“€å…ƒå‰",narrow:"BC",abbr:"BC"}],eraYearOrdinalRegex:/(元|\d+)幓/,eraYearOrdinalParse:function(e,t){return t[1]==="元"?1:parseInt(t[1]||e,10)},months:"1꜈_2꜈_3꜈_4꜈_5꜈_6꜈_7꜈_8꜈_9꜈_10꜈_11꜈_12꜈".split("_"),monthsShort:"1꜈_2꜈_3꜈_4꜈_5꜈_6꜈_7꜈_8꜈_9꜈_10꜈_11꜈_12꜈".split("_"),weekdays:"ę—„ę›œę—„_ęœˆę›œę—„_ē«ę›œę—„_ę°“ę›œę—„_ęœØę›œę—„_é‡‘ę›œę—„_åœŸę›œę—„".split("_"),weekdaysShort:"ę—„_꜈_ē«_ę°“_ęœØ_金_土".split("_"),weekdaysMin:"ę—„_꜈_ē«_ę°“_ęœØ_金_土".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY幓M꜈Dę—„",LLL:"YYYY幓M꜈Dę—„ HH:mm",LLLL:"YYYY幓M꜈Dę—„ dddd HH:mm",l:"YYYY/MM/DD",ll:"YYYY幓M꜈Dę—„",lll:"YYYY幓M꜈Dę—„ HH:mm",llll:"YYYY幓M꜈Dę—„(ddd) HH:mm"},meridiemParse:/午前|午後/i,isPM:function(e){return e==="午後"},meridiem:function(e,t,n){if(e<12)return"午前";else return"午後"},calendar:{sameDay:"[今ꗄ] LT",nextDay:"[ę˜Žę—„] LT",nextWeek:function(e){if(e.week()!==this.week())return"[ę„é€±]dddd LT";else return"dddd LT"},lastDay:"[ę˜Øę—„] LT",lastWeek:function(e){if(this.week()!==e.week())return"[先週]dddd LT";else return"dddd LT"},sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}ę—„/,ordinal:function(e,t){switch(t){case"y":return e===1?"元幓":e+"幓";case"d":case"D":case"DDD":return e+"ę—„";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"ꕰē§’",ss:"%dē§’",m:"1分",mm:"%d分",h:"1Ꙃ間",hh:"%dꙂ間",d:"1ę—„",dd:"%dę—„",M:"1ćƒ¶ęœˆ",MM:"%dćƒ¶ęœˆ",y:"1幓",yy:"%d幓"}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("jv",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_Nopember_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nop_Des".split("_"),weekdays:"Minggu_Senen_Seloso_Rebu_Kemis_Jemuwah_Septu".split("_"),weekdaysShort:"Min_Sen_Sel_Reb_Kem_Jem_Sep".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sp".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/enjing|siyang|sonten|ndalu/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="enjing")return e;else if(t==="siyang")return e>=11?e:e+12;else if(t==="sonten"||t==="ndalu")return e+12},meridiem:function(e,t,n){if(e<11)return"enjing";else if(e<15)return"siyang";else if(e<19)return"sonten";else return"ndalu"},calendar:{sameDay:"[Dinten puniko pukul] LT",nextDay:"[Mbenjang pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kala wingi pukul] LT",lastWeek:"dddd [kepengker pukul] LT",sameElse:"L"},relativeTime:{future:"wonten ing %s",past:"%s ingkang kepengker",s:"sawetawis detik",ss:"%d detik",m:"setunggal menit",mm:"%d menit",h:"setunggal jam",hh:"%d jam",d:"sedinten",dd:"%d dinten",M:"sewulan",MM:"%d wulan",y:"setaun",yy:"%d taun"},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("jv",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_Nopember_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nop_Des".split("_"),weekdays:"Minggu_Senen_Seloso_Rebu_Kemis_Jemuwah_Septu".split("_"),weekdaysShort:"Min_Sen_Sel_Reb_Kem_Jem_Sep".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sp".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/enjing|siyang|sonten|ndalu/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="enjing")return e;else if(t==="siyang")return e>=11?e:e+12;else if(t==="sonten"||t==="ndalu")return e+12},meridiem:function(e,t,n){if(e<11)return"enjing";else if(e<15)return"siyang";else if(e<19)return"sonten";else return"ndalu"},calendar:{sameDay:"[Dinten puniko pukul] LT",nextDay:"[Mbenjang pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kala wingi pukul] LT",lastWeek:"dddd [kepengker pukul] LT",sameElse:"L"},relativeTime:{future:"wonten ing %s",past:"%s ingkang kepengker",s:"sawetawis detik",ss:"%d detik",m:"setunggal menit",mm:"%d menit",h:"setunggal jam",hh:"%d jam",d:"sedinten",dd:"%d dinten",M:"sewulan",MM:"%d wulan",y:"setaun",yy:"%d taun"},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("ka",{months:"įƒ˜įƒįƒœįƒ•įƒįƒ įƒ˜_įƒ—įƒ”įƒ‘įƒ”įƒ įƒ•įƒįƒšįƒ˜_įƒ›įƒįƒ įƒ¢įƒ˜_įƒįƒžįƒ įƒ˜įƒšįƒ˜_įƒ›įƒįƒ˜įƒ”įƒ˜_įƒ˜įƒ•įƒœįƒ˜įƒ”įƒ˜_įƒ˜įƒ•įƒšįƒ˜įƒ”įƒ˜_įƒįƒ’įƒ•įƒ˜įƒ”įƒ¢įƒ_įƒ”įƒ”įƒ„įƒ¢įƒ”įƒ›įƒ‘įƒ”įƒ įƒ˜_įƒįƒ„įƒ¢įƒįƒ›įƒ‘įƒ”įƒ įƒ˜_įƒœįƒįƒ”įƒ›įƒ‘įƒ”įƒ įƒ˜_įƒ“įƒ”įƒ™įƒ”įƒ›įƒ‘įƒ”įƒ įƒ˜".split("_"),monthsShort:"įƒ˜įƒįƒœ_įƒ—įƒ”įƒ‘_įƒ›įƒįƒ _įƒįƒžįƒ _įƒ›įƒįƒ˜_įƒ˜įƒ•įƒœ_įƒ˜įƒ•įƒš_įƒįƒ’įƒ•_įƒ”įƒ”įƒ„_įƒįƒ„įƒ¢_įƒœįƒįƒ”_įƒ“įƒ”įƒ™".split("_"),weekdays:{standalone:"įƒ™įƒ•įƒ˜įƒ įƒ_įƒįƒ įƒØįƒįƒ‘įƒįƒ—įƒ˜_įƒ”įƒįƒ›įƒØįƒįƒ‘įƒįƒ—įƒ˜_įƒįƒ—įƒ®įƒØįƒįƒ‘įƒįƒ—įƒ˜_įƒ®įƒ£įƒ—įƒØįƒįƒ‘įƒįƒ—įƒ˜_įƒžįƒįƒ įƒįƒ”įƒ™įƒ”įƒ•įƒ˜_įƒØįƒįƒ‘įƒįƒ—įƒ˜".split("_"),format:"įƒ™įƒ•įƒ˜įƒ įƒįƒ”_įƒįƒ įƒØįƒįƒ‘įƒįƒ—įƒ”_įƒ”įƒįƒ›įƒØįƒįƒ‘įƒįƒ—įƒ”_įƒįƒ—įƒ®įƒØįƒįƒ‘įƒįƒ—įƒ”_įƒ®įƒ£įƒ—įƒØįƒįƒ‘įƒįƒ—įƒ”_įƒžįƒįƒ įƒįƒ”įƒ™įƒ”įƒ•įƒ”_įƒØįƒįƒ‘įƒįƒ—įƒ”".split("_"),isFormat:/(įƒ¬įƒ˜įƒœįƒ|įƒØįƒ”įƒ›įƒ“įƒ”įƒ’)/},weekdaysShort:"įƒ™įƒ•įƒ˜_įƒįƒ įƒØ_įƒ”įƒįƒ›_įƒįƒ—įƒ®_įƒ®įƒ£įƒ—_įƒžįƒįƒ _įƒØįƒįƒ‘".split("_"),weekdaysMin:"įƒ™įƒ•_įƒįƒ _įƒ”įƒ_įƒįƒ—_įƒ®įƒ£_įƒžįƒ_įƒØįƒ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[įƒ“įƒ¦įƒ”įƒ”] LT[-įƒ–įƒ”]",nextDay:"[įƒ®įƒ•įƒįƒš] LT[-įƒ–įƒ”]",lastDay:"[įƒ’įƒ£įƒØįƒ˜įƒœ] LT[-įƒ–įƒ”]",nextWeek:"[įƒØįƒ”įƒ›įƒ“įƒ”įƒ’] dddd LT[-įƒ–įƒ”]",lastWeek:"[įƒ¬įƒ˜įƒœįƒ] dddd LT-įƒ–įƒ”",sameElse:"L"},relativeTime:{future:function(e){return e.replace(/(įƒ¬įƒįƒ›|įƒ¬įƒ£įƒ—|įƒ”įƒįƒįƒ—|įƒ¬įƒ”įƒš|įƒ“įƒ¦|įƒ—įƒ•)(įƒ˜|įƒ”)/,function(e,t,n){return n==="įƒ˜"?t+"įƒØįƒ˜":t+n+"įƒØįƒ˜"})},past:function(e){if(/(įƒ¬įƒįƒ›įƒ˜|įƒ¬įƒ£įƒ—įƒ˜|įƒ”įƒįƒįƒ—įƒ˜|įƒ“įƒ¦įƒ”|įƒ—įƒ•įƒ”)/.test(e))return e.replace(/(įƒ˜|įƒ”)$/,"įƒ˜įƒ” įƒ¬įƒ˜įƒœ");if(/įƒ¬įƒ”įƒšįƒ˜/.test(e))return e.replace(/įƒ¬įƒ”įƒšįƒ˜$/,"įƒ¬įƒšįƒ˜įƒ” įƒ¬įƒ˜įƒœ");return e},s:"įƒ įƒįƒ›įƒ“įƒ”įƒœįƒ˜įƒ›įƒ” įƒ¬įƒįƒ›įƒ˜",ss:"%d įƒ¬įƒįƒ›įƒ˜",m:"įƒ¬įƒ£įƒ—įƒ˜",mm:"%d įƒ¬įƒ£įƒ—įƒ˜",h:"įƒ”įƒįƒįƒ—įƒ˜",hh:"%d įƒ”įƒįƒįƒ—įƒ˜",d:"įƒ“įƒ¦įƒ”",dd:"%d įƒ“įƒ¦įƒ”",M:"įƒ—įƒ•įƒ”",MM:"%d įƒ—įƒ•įƒ”",y:"įƒ¬įƒ”įƒšįƒ˜",yy:"%d įƒ¬įƒ”įƒšįƒ˜"},dayOfMonthOrdinalParse:/0|1-įƒšįƒ˜|įƒ›įƒ”-\d{1,2}|\d{1,2}-įƒ”/,ordinal:function(e){if(e===0)return e;if(e===1)return e+"-įƒšįƒ˜";if(e<20||e<=100&&e%20===0||e%100===0)return"įƒ›įƒ”-"+e;return e+"-įƒ”"},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("ka",{months:"įƒ˜įƒįƒœįƒ•įƒįƒ įƒ˜_įƒ—įƒ”įƒ‘įƒ”įƒ įƒ•įƒįƒšįƒ˜_įƒ›įƒįƒ įƒ¢įƒ˜_įƒįƒžįƒ įƒ˜įƒšįƒ˜_įƒ›įƒįƒ˜įƒ”įƒ˜_įƒ˜įƒ•įƒœįƒ˜įƒ”įƒ˜_įƒ˜įƒ•įƒšįƒ˜įƒ”įƒ˜_įƒįƒ’įƒ•įƒ˜įƒ”įƒ¢įƒ_įƒ”įƒ”įƒ„įƒ¢įƒ”įƒ›įƒ‘įƒ”įƒ įƒ˜_įƒįƒ„įƒ¢įƒįƒ›įƒ‘įƒ”įƒ įƒ˜_įƒœįƒįƒ”įƒ›įƒ‘įƒ”įƒ įƒ˜_įƒ“įƒ”įƒ™įƒ”įƒ›įƒ‘įƒ”įƒ įƒ˜".split("_"),monthsShort:"įƒ˜įƒįƒœ_įƒ—įƒ”įƒ‘_įƒ›įƒįƒ _įƒįƒžįƒ _įƒ›įƒįƒ˜_įƒ˜įƒ•įƒœ_įƒ˜įƒ•įƒš_įƒįƒ’įƒ•_įƒ”įƒ”įƒ„_įƒįƒ„įƒ¢_įƒœįƒįƒ”_įƒ“įƒ”įƒ™".split("_"),weekdays:{standalone:"įƒ™įƒ•įƒ˜įƒ įƒ_įƒįƒ įƒØįƒįƒ‘įƒįƒ—įƒ˜_įƒ”įƒįƒ›įƒØįƒįƒ‘įƒįƒ—įƒ˜_įƒįƒ—įƒ®įƒØįƒįƒ‘įƒįƒ—įƒ˜_įƒ®įƒ£įƒ—įƒØįƒįƒ‘įƒįƒ—įƒ˜_įƒžįƒįƒ įƒįƒ”įƒ™įƒ”įƒ•įƒ˜_įƒØįƒįƒ‘įƒįƒ—įƒ˜".split("_"),format:"įƒ™įƒ•įƒ˜įƒ įƒįƒ”_įƒįƒ įƒØįƒįƒ‘įƒįƒ—įƒ”_įƒ”įƒįƒ›įƒØįƒįƒ‘įƒįƒ—įƒ”_įƒįƒ—įƒ®įƒØįƒįƒ‘įƒįƒ—įƒ”_įƒ®įƒ£įƒ—įƒØįƒįƒ‘įƒįƒ—įƒ”_įƒžįƒįƒ įƒįƒ”įƒ™įƒ”įƒ•įƒ”_įƒØįƒįƒ‘įƒįƒ—įƒ”".split("_"),isFormat:/(įƒ¬įƒ˜įƒœįƒ|įƒØįƒ”įƒ›įƒ“įƒ”įƒ’)/},weekdaysShort:"įƒ™įƒ•įƒ˜_įƒįƒ įƒØ_įƒ”įƒįƒ›_įƒįƒ—įƒ®_įƒ®įƒ£įƒ—_įƒžįƒįƒ _įƒØįƒįƒ‘".split("_"),weekdaysMin:"įƒ™įƒ•_įƒįƒ _įƒ”įƒ_įƒįƒ—_įƒ®įƒ£_įƒžįƒ_įƒØįƒ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[įƒ“įƒ¦įƒ”įƒ”] LT[-įƒ–įƒ”]",nextDay:"[įƒ®įƒ•įƒįƒš] LT[-įƒ–įƒ”]",lastDay:"[įƒ’įƒ£įƒØįƒ˜įƒœ] LT[-įƒ–įƒ”]",nextWeek:"[įƒØįƒ”įƒ›įƒ“įƒ”įƒ’] dddd LT[-įƒ–įƒ”]",lastWeek:"[įƒ¬įƒ˜įƒœįƒ] dddd LT-įƒ–įƒ”",sameElse:"L"},relativeTime:{future:function(e){return e.replace(/(įƒ¬įƒįƒ›|įƒ¬įƒ£įƒ—|įƒ”įƒįƒįƒ—|įƒ¬įƒ”įƒš|įƒ“įƒ¦|įƒ—įƒ•)(įƒ˜|įƒ”)/,function(e,t,n){return n==="įƒ˜"?t+"įƒØįƒ˜":t+n+"įƒØįƒ˜"})},past:function(e){if(/(įƒ¬įƒįƒ›įƒ˜|įƒ¬įƒ£įƒ—įƒ˜|įƒ”įƒįƒįƒ—įƒ˜|įƒ“įƒ¦įƒ”|įƒ—įƒ•įƒ”)/.test(e))return e.replace(/(įƒ˜|įƒ”)$/,"įƒ˜įƒ” įƒ¬įƒ˜įƒœ");if(/įƒ¬įƒ”įƒšįƒ˜/.test(e))return e.replace(/įƒ¬įƒ”įƒšįƒ˜$/,"įƒ¬įƒšįƒ˜įƒ” įƒ¬įƒ˜įƒœ");return e},s:"įƒ įƒįƒ›įƒ“įƒ”įƒœįƒ˜įƒ›įƒ” įƒ¬įƒįƒ›įƒ˜",ss:"%d įƒ¬įƒįƒ›įƒ˜",m:"įƒ¬įƒ£įƒ—įƒ˜",mm:"%d įƒ¬įƒ£įƒ—įƒ˜",h:"įƒ”įƒįƒįƒ—įƒ˜",hh:"%d įƒ”įƒįƒįƒ—įƒ˜",d:"įƒ“įƒ¦įƒ”",dd:"%d įƒ“įƒ¦įƒ”",M:"įƒ—įƒ•įƒ”",MM:"%d įƒ—įƒ•įƒ”",y:"įƒ¬įƒ”įƒšįƒ˜",yy:"%d įƒ¬įƒ”įƒšįƒ˜"},dayOfMonthOrdinalParse:/0|1-įƒšįƒ˜|įƒ›įƒ”-\d{1,2}|\d{1,2}-įƒ”/,ordinal:function(e){if(e===0)return e;if(e===1)return e+"-įƒšįƒ˜";if(e<20||e<=100&&e%20===0||e%100===0)return"įƒ›įƒ”-"+e;return e+"-įƒ”"},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var a={0:"-ші",1:"-ші",2:"-ші",3:"-ші",4:"-ші",5:"-ші",6:"-шы",7:"-ші",8:"-ші",9:"-шы",10:"-шы",20:"-шы",30:"-шы",40:"-шы",50:"-ші",60:"-шы",70:"-ші",80:"-ші",90:"-шы",100:"-ші"},t;e.defineLocale("kk",{months:"Ņ›Š°Ņ£Ń‚Š°Ń€_Š°Ņ›ŠæŠ°Š½_Š½Š°ŃƒŃ€Ń‹Š·_сәуір_Š¼Š°Š¼Ń‹Ń€_Š¼Š°ŃƒŃŃ‹Š¼_шіŠ»Š“Šµ_тŠ°Š¼Ń‹Š·_Ņ›Ń‹Ń€ŠŗŅÆŠ¹ŠµŠŗ_Ņ›Š°Š·Š°Š½_Ņ›Š°Ń€Š°ŃˆŠ°_Š¶ŠµŠ»Ń‚Š¾Ņ›ŃŠ°Š½".split("_"),monthsShort:"Ņ›Š°Ņ£_Š°Ņ›Šæ_Š½Š°Ńƒ_сәу_Š¼Š°Š¼_Š¼Š°Ńƒ_шіŠ»_тŠ°Š¼_Ņ›Ń‹Ń€_Ņ›Š°Š·_Ņ›Š°Ń€_Š¶ŠµŠ»".split("_"),weekdays:"Š¶ŠµŠŗсŠµŠ½Š±Ń–_Š“ŅÆŠ¹ŃŠµŠ½Š±Ń–_сŠµŠ¹ŃŠµŠ½Š±Ń–_сәрсŠµŠ½Š±Ń–_Š±ŠµŠ¹ŃŠµŠ½Š±Ń–_Š¶Ņ±Š¼Š°_сŠµŠ½Š±Ń–".split("_"),weekdaysShort:"Š¶ŠµŠŗ_Š“ŅÆŠ¹_сŠµŠ¹_сәр_Š±ŠµŠ¹_Š¶Ņ±Š¼_сŠµŠ½".split("_"),weekdaysMin:"Š¶Šŗ_Š“Š¹_сŠ¹_ср_Š±Š¹_Š¶Š¼_сŠ½".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Š‘ŅÆŠ³Ń–Š½ сŠ°Ņ“Š°Ń‚] LT",nextDay:"[Š•Ń€Ń‚ŠµŅ£ сŠ°Ņ“Š°Ń‚] LT",nextWeek:"dddd [сŠ°Ņ“Š°Ń‚] LT",lastDay:"[ŠšŠµŃˆŠµ сŠ°Ņ“Š°Ń‚] LT",lastWeek:"[ÓØтŠŗŠµŠ½ Š°ŠæтŠ°Š½Ń‹Ņ£] dddd [сŠ°Ņ“Š°Ń‚] LT",sameElse:"L"},relativeTime:{future:"%s ішіŠ½Š“Šµ",past:"%s Š±Ņ±Ń€Ń‹Š½",s:"Š±Ń–Ń€Š½ŠµŃˆŠµ сŠµŠŗуŠ½Š“",ss:"%d сŠµŠŗуŠ½Š“",m:"Š±Ń–Ń€ Š¼ŠøŠ½ŃƒŃ‚",mm:"%d Š¼ŠøŠ½ŃƒŃ‚",h:"Š±Ń–Ń€ сŠ°Ņ“Š°Ń‚",hh:"%d сŠ°Ņ“Š°Ń‚",d:"Š±Ń–Ń€ ŠŗŅÆŠ½",dd:"%d ŠŗŅÆŠ½",M:"Š±Ń–Ń€ Š°Š¹",MM:"%d Š°Š¹",y:"Š±Ń–Ń€ Š¶Ń‹Š»",yy:"%d Š¶Ń‹Š»"},dayOfMonthOrdinalParse:/\d{1,2}-(ші|шы)/,ordinal:function(e){var t=e%10,n=e>=100?100:null;return e+(a[e]||a[t]||a[n])},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var a={0:"-ші",1:"-ші",2:"-ші",3:"-ші",4:"-ші",5:"-ші",6:"-шы",7:"-ші",8:"-ші",9:"-шы",10:"-шы",20:"-шы",30:"-шы",40:"-шы",50:"-ші",60:"-шы",70:"-ші",80:"-ші",90:"-шы",100:"-ші"},t;e.defineLocale("kk",{months:"Ņ›Š°Ņ£Ń‚Š°Ń€_Š°Ņ›ŠæŠ°Š½_Š½Š°ŃƒŃ€Ń‹Š·_сәуір_Š¼Š°Š¼Ń‹Ń€_Š¼Š°ŃƒŃŃ‹Š¼_шіŠ»Š“Šµ_тŠ°Š¼Ń‹Š·_Ņ›Ń‹Ń€ŠŗŅÆŠ¹ŠµŠŗ_Ņ›Š°Š·Š°Š½_Ņ›Š°Ń€Š°ŃˆŠ°_Š¶ŠµŠ»Ń‚Š¾Ņ›ŃŠ°Š½".split("_"),monthsShort:"Ņ›Š°Ņ£_Š°Ņ›Šæ_Š½Š°Ńƒ_сәу_Š¼Š°Š¼_Š¼Š°Ńƒ_шіŠ»_тŠ°Š¼_Ņ›Ń‹Ń€_Ņ›Š°Š·_Ņ›Š°Ń€_Š¶ŠµŠ»".split("_"),weekdays:"Š¶ŠµŠŗсŠµŠ½Š±Ń–_Š“ŅÆŠ¹ŃŠµŠ½Š±Ń–_сŠµŠ¹ŃŠµŠ½Š±Ń–_сәрсŠµŠ½Š±Ń–_Š±ŠµŠ¹ŃŠµŠ½Š±Ń–_Š¶Ņ±Š¼Š°_сŠµŠ½Š±Ń–".split("_"),weekdaysShort:"Š¶ŠµŠŗ_Š“ŅÆŠ¹_сŠµŠ¹_сәр_Š±ŠµŠ¹_Š¶Ņ±Š¼_сŠµŠ½".split("_"),weekdaysMin:"Š¶Šŗ_Š“Š¹_сŠ¹_ср_Š±Š¹_Š¶Š¼_сŠ½".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Š‘ŅÆŠ³Ń–Š½ сŠ°Ņ“Š°Ń‚] LT",nextDay:"[Š•Ń€Ń‚ŠµŅ£ сŠ°Ņ“Š°Ń‚] LT",nextWeek:"dddd [сŠ°Ņ“Š°Ń‚] LT",lastDay:"[ŠšŠµŃˆŠµ сŠ°Ņ“Š°Ń‚] LT",lastWeek:"[ÓØтŠŗŠµŠ½ Š°ŠæтŠ°Š½Ń‹Ņ£] dddd [сŠ°Ņ“Š°Ń‚] LT",sameElse:"L"},relativeTime:{future:"%s ішіŠ½Š“Šµ",past:"%s Š±Ņ±Ń€Ń‹Š½",s:"Š±Ń–Ń€Š½ŠµŃˆŠµ сŠµŠŗуŠ½Š“",ss:"%d сŠµŠŗуŠ½Š“",m:"Š±Ń–Ń€ Š¼ŠøŠ½ŃƒŃ‚",mm:"%d Š¼ŠøŠ½ŃƒŃ‚",h:"Š±Ń–Ń€ сŠ°Ņ“Š°Ń‚",hh:"%d сŠ°Ņ“Š°Ń‚",d:"Š±Ń–Ń€ ŠŗŅÆŠ½",dd:"%d ŠŗŅÆŠ½",M:"Š±Ń–Ń€ Š°Š¹",MM:"%d Š°Š¹",y:"Š±Ń–Ń€ Š¶Ń‹Š»",yy:"%d Š¶Ń‹Š»"},dayOfMonthOrdinalParse:/\d{1,2}-(ші|шы)/,ordinal:function(e){var t=e%10,n=e>=100?100:null;return e+(a[e]||a[t]||a[n])},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"įŸ”",2:"įŸ¢",3:"įŸ£",4:"įŸ¤",5:"įŸ„",6:"įŸ¦",7:"įŸ§",8:"įŸØ",9:"įŸ©",0:"įŸ "},n={"įŸ”":"1","įŸ¢":"2","įŸ£":"3","įŸ¤":"4","įŸ„":"5","įŸ¦":"6","įŸ§":"7","įŸØ":"8","įŸ©":"9","įŸ ":"0"},a;e.defineLocale("km",{months:"įž˜įž€įžšįž¶_įž€įž»įž˜įŸ’įž—įŸˆ_įž˜įžøįž“įž¶_įž˜įŸįžŸįž¶_įž§įžŸįž—įž¶_įž˜įž·įžįž»įž“įž¶_įž€įž€įŸ’įž€įžŠįž¶_įžŸįžøįž įž¶_įž€įž‰įŸ’įž‰įž¶_įžįž»įž›įž¶_įžœįž·įž…įŸ’įž†įž·įž€įž¶_įž’įŸ’įž“įž¼".split("_"),monthsShort:"įž˜įž€įžšįž¶_įž€įž»įž˜įŸ’įž—įŸˆ_įž˜įžøįž“įž¶_įž˜įŸįžŸįž¶_įž§įžŸįž—įž¶_įž˜įž·įžįž»įž“įž¶_įž€įž€įŸ’įž€įžŠįž¶_įžŸįžøįž įž¶_įž€įž‰įŸ’įž‰įž¶_įžįž»įž›įž¶_įžœįž·įž…įŸ’įž†įž·įž€įž¶_įž’įŸ’įž“įž¼".split("_"),weekdays:"įž¢įž¶įž‘įž·įžįŸ’įž™_įž…įŸįž“įŸ’įž‘_įž¢įž„įŸ’įž‚įž¶įžš_įž–įž»įž’_įž–įŸ’įžšįž įžŸįŸ’įž”įžįž·įŸ_įžŸįž»įž€įŸ’įžš_įžŸįŸ…įžšįŸ".split("_"),weekdaysShort:"įž¢įž¶_įž…_įž¢_įž–_įž–įŸ’įžš_įžŸįž»_įžŸ".split("_"),weekdaysMin:"įž¢įž¶_įž…_įž¢_įž–_įž–įŸ’įžš_įžŸįž»_įžŸ".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/įž–įŸ’įžšįž¹įž€|įž›įŸ’įž„įž¶įž…/,isPM:function(e){return e==="įž›įŸ’įž„įž¶įž…"},meridiem:function(e,t,n){if(e<12)return"įž–įŸ’įžšįž¹įž€";else return"įž›įŸ’įž„įž¶įž…"},calendar:{sameDay:"[įžįŸ’įž„įŸƒįž“įŸįŸ‡ įž˜įŸ‰įŸ„įž„] LT",nextDay:"[įžŸįŸ’įž¢įŸ‚įž€ įž˜įŸ‰įŸ„įž„] LT",nextWeek:"dddd [įž˜įŸ‰įŸ„įž„] LT",lastDay:"[įž˜įŸ’įžŸįž·įž›įž˜įž·įž‰ įž˜įŸ‰įŸ„įž„] LT",lastWeek:"dddd [įžŸįž”įŸ’įžįž¶įž įŸįž˜įž»įž“] [įž˜įŸ‰įŸ„įž„] LT",sameElse:"L"},relativeTime:{future:"%sįž‘įŸ€įž",past:"%sįž˜įž»įž“",s:"įž”įŸ‰įž»įž“įŸ’įž˜įž¶įž“įžœįž·įž“įž¶įž‘įžø",ss:"%d įžœįž·įž“įž¶įž‘įžø",m:"įž˜įž½įž™įž“įž¶įž‘įžø",mm:"%d įž“įž¶įž‘įžø",h:"įž˜įž½įž™įž˜įŸ‰įŸ„įž„",hh:"%d įž˜įŸ‰įŸ„įž„",d:"įž˜įž½įž™įžįŸ’įž„įŸƒ",dd:"%d įžįŸ’įž„įŸƒ",M:"įž˜įž½įž™įžįŸ‚",MM:"%d įžįŸ‚",y:"įž˜įž½įž™įž†įŸ’įž“įž¶įŸ†",yy:"%d įž†įŸ’įž“įž¶įŸ†"},dayOfMonthOrdinalParse:/įž‘įžø\d{1,2}/,ordinal:"įž‘įžø%d",preparse:function(e){return e.replace(/[įŸ”įŸ¢įŸ£įŸ¤įŸ„įŸ¦įŸ§įŸØįŸ©įŸ ]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"įŸ”",2:"įŸ¢",3:"įŸ£",4:"įŸ¤",5:"įŸ„",6:"įŸ¦",7:"įŸ§",8:"įŸØ",9:"įŸ©",0:"įŸ "},n={"įŸ”":"1","įŸ¢":"2","įŸ£":"3","įŸ¤":"4","įŸ„":"5","įŸ¦":"6","įŸ§":"7","įŸØ":"8","įŸ©":"9","įŸ ":"0"},a;e.defineLocale("km",{months:"įž˜įž€įžšįž¶_įž€įž»įž˜įŸ’įž—įŸˆ_įž˜įžøįž“įž¶_įž˜įŸįžŸįž¶_įž§įžŸįž—įž¶_įž˜įž·įžįž»įž“įž¶_įž€įž€įŸ’įž€įžŠįž¶_įžŸįžøįž įž¶_įž€įž‰įŸ’įž‰įž¶_įžįž»įž›įž¶_įžœįž·įž…įŸ’įž†įž·įž€įž¶_įž’įŸ’įž“įž¼".split("_"),monthsShort:"įž˜įž€įžšįž¶_įž€įž»įž˜įŸ’įž—įŸˆ_įž˜įžøįž“įž¶_įž˜įŸįžŸįž¶_įž§įžŸįž—įž¶_įž˜įž·įžįž»įž“įž¶_įž€įž€įŸ’įž€įžŠįž¶_įžŸįžøįž įž¶_įž€įž‰įŸ’įž‰įž¶_įžįž»įž›įž¶_įžœįž·įž…įŸ’įž†įž·įž€įž¶_įž’įŸ’įž“įž¼".split("_"),weekdays:"įž¢įž¶įž‘įž·įžįŸ’įž™_įž…įŸįž“įŸ’įž‘_įž¢įž„įŸ’įž‚įž¶įžš_įž–įž»įž’_įž–įŸ’įžšįž įžŸįŸ’įž”įžįž·įŸ_įžŸįž»įž€įŸ’įžš_įžŸįŸ…įžšįŸ".split("_"),weekdaysShort:"įž¢įž¶_įž…_įž¢_įž–_įž–įŸ’įžš_įžŸįž»_įžŸ".split("_"),weekdaysMin:"įž¢įž¶_įž…_įž¢_įž–_įž–įŸ’įžš_įžŸįž»_įžŸ".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/įž–įŸ’įžšįž¹įž€|įž›įŸ’įž„įž¶įž…/,isPM:function(e){return e==="įž›įŸ’įž„įž¶įž…"},meridiem:function(e,t,n){if(e<12)return"įž–įŸ’įžšįž¹įž€";else return"įž›įŸ’įž„įž¶įž…"},calendar:{sameDay:"[įžįŸ’įž„įŸƒįž“įŸįŸ‡ įž˜įŸ‰įŸ„įž„] LT",nextDay:"[įžŸįŸ’įž¢įŸ‚įž€ įž˜įŸ‰įŸ„įž„] LT",nextWeek:"dddd [įž˜įŸ‰įŸ„įž„] LT",lastDay:"[įž˜įŸ’įžŸįž·įž›įž˜įž·įž‰ įž˜įŸ‰įŸ„įž„] LT",lastWeek:"dddd [įžŸįž”įŸ’įžįž¶įž įŸįž˜įž»įž“] [įž˜įŸ‰įŸ„įž„] LT",sameElse:"L"},relativeTime:{future:"%sįž‘įŸ€įž",past:"%sįž˜įž»įž“",s:"įž”įŸ‰įž»įž“įŸ’įž˜įž¶įž“įžœįž·įž“įž¶įž‘įžø",ss:"%d įžœįž·įž“įž¶įž‘įžø",m:"įž˜įž½įž™įž“įž¶įž‘įžø",mm:"%d įž“įž¶įž‘įžø",h:"įž˜įž½įž™įž˜įŸ‰įŸ„įž„",hh:"%d įž˜įŸ‰įŸ„įž„",d:"įž˜įž½įž™įžįŸ’įž„įŸƒ",dd:"%d įžįŸ’įž„įŸƒ",M:"įž˜įž½įž™įžįŸ‚",MM:"%d įžįŸ‚",y:"įž˜įž½įž™įž†įŸ’įž“įž¶įŸ†",yy:"%d įž†įŸ’įž“įž¶įŸ†"},dayOfMonthOrdinalParse:/įž‘įžø\d{1,2}/,ordinal:"įž‘įžø%d",preparse:function(e){return e.replace(/[įŸ”įŸ¢įŸ£įŸ¤įŸ„įŸ¦įŸ§įŸØįŸ©įŸ ]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"ą³§",2:"ą³Ø",3:"ą³©",4:"ą³Ŗ",5:"ą³«",6:"ą³¬",7:"ą³­",8:"ą³®",9:"ą³Æ",0:"ą³¦"},n={"ą³§":"1","ą³Ø":"2","ą³©":"3","ą³Ŗ":"4","ą³«":"5","ą³¬":"6","ą³­":"7","ą³®":"8","ą³Æ":"9","ą³¦":"0"},a;e.defineLocale("kn",{months:"ą²œą²Øą²µą²°ą²æ_ą²«ą³†ą²¬ą³ą²°ą²µą²°ą²æ_ą²®ą²¾ą²°ą³ą²šą³_ą²ą²Ŗą³ą²°ą²æą²²ą³_ą²®ą³†ą³•_ą²œą³‚ą²Øą³_ą²œą³ą²²ą³†ą³–_ą²†ą²—ą²øą³ą²Ÿą³_ą²øą³†ą²Ŗą³ą²Ÿą³†ą²‚ą²¬ą²°ą³_ą²…ą²•ą³ą²Ÿą³†ą³‚ą³•ą²¬ą²°ą³_ą²Øą²µą³†ą²‚ą²¬ą²°ą³_ą²”ą²æą²øą³†ą²‚ą²¬ą²°ą³".split("_"),monthsShort:"ą²œą²Ø_ą²«ą³†ą²¬ą³ą²°_ą²®ą²¾ą²°ą³ą²šą³_ą²ą²Ŗą³ą²°ą²æą²²ą³_ą²®ą³†ą³•_ą²œą³‚ą²Øą³_ą²œą³ą²²ą³†ą³–_ą²†ą²—ą²øą³ą²Ÿą³_ą²øą³†ą²Ŗą³ą²Ÿą³†ą²‚_ą²…ą²•ą³ą²Ÿą³†ą³‚ą³•_ą²Øą²µą³†ą²‚_ą²”ą²æą²øą³†ą²‚".split("_"),monthsParseExact:true,weekdays:"ą²­ą²¾ą²Øą³ą²µą²¾ą²°_ą²øą³†ą³‚ą³•ą²®ą²µą²¾ą²°_ą²®ą²‚ą²—ą²³ą²µą²¾ą²°_ą²¬ą³ą²§ą²µą²¾ą²°_ą²—ą³ą²°ą³ą²µą²¾ą²°_ą²¶ą³ą²•ą³ą²°ą²µą²¾ą²°_ą²¶ą²Øą²æą²µą²¾ą²°".split("_"),weekdaysShort:"ą²­ą²¾ą²Øą³_ą²øą³†ą³‚ą³•ą²®_ą²®ą²‚ą²—ą²³_ą²¬ą³ą²§_ą²—ą³ą²°ą³_ą²¶ą³ą²•ą³ą²°_ą²¶ą²Øą²æ".split("_"),weekdaysMin:"ą²­ą²¾_ą²øą³†ą³‚ą³•_ą²®ą²‚_ą²¬ą³_ą²—ą³_ą²¶ą³_ą²¶".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[ą²‡ą²‚ą²¦ą³] LT",nextDay:"[ą²Øą²¾ą²³ą³†] LT",nextWeek:"dddd, LT",lastDay:"[ą²Øą²æą²Øą³ą²Øą³†] LT",lastWeek:"[ą²•ą³†ą³‚ą²Øą³†ą²Æ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą²Øą²‚ą²¤ą²°",past:"%s ą²¹ą²æą²‚ą²¦ą³†",s:"ą²•ą³†ą²²ą²µą³ ą²•ą³ą²·ą²£ą²—ą²³ą³",ss:"%d ą²øą³†ą²•ą³†ą²‚ą²”ą³ą²—ą²³ą³",m:"ą²’ą²‚ą²¦ą³ ą²Øą²æą²®ą²æą²·",mm:"%d ą²Øą²æą²®ą²æą²·",h:"ą²’ą²‚ą²¦ą³ ą²—ą²‚ą²Ÿą³†",hh:"%d ą²—ą²‚ą²Ÿą³†",d:"ą²’ą²‚ą²¦ą³ ą²¦ą²æą²Ø",dd:"%d ą²¦ą²æą²Ø",M:"ą²’ą²‚ą²¦ą³ ą²¤ą²æą²‚ą²—ą²³ą³",MM:"%d ą²¤ą²æą²‚ą²—ą²³ą³",y:"ą²’ą²‚ą²¦ą³ ą²µą²°ą³ą²·",yy:"%d ą²µą²°ą³ą²·"},preparse:function(e){return e.replace(/[ą³§ą³Øą³©ą³Ŗą³«ą³¬ą³­ą³®ą³Æą³¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą²°ą²¾ą²¤ą³ą²°ą²æ|ą²¬ą³†ą²³ą²æą²—ą³ą²—ą³†|ą²®ą²§ą³ą²Æą²¾ą²¹ą³ą²Ø|ą²øą²‚ą²œą³†/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą²°ą²¾ą²¤ą³ą²°ą²æ")return e<4?e:e+12;else if(t==="ą²¬ą³†ą²³ą²æą²—ą³ą²—ą³†")return e;else if(t==="ą²®ą²§ą³ą²Æą²¾ą²¹ą³ą²Ø")return e>=10?e:e+12;else if(t==="ą²øą²‚ą²œą³†")return e+12},meridiem:function(e,t,n){if(e<4)return"ą²°ą²¾ą²¤ą³ą²°ą²æ";else if(e<10)return"ą²¬ą³†ą²³ą²æą²—ą³ą²—ą³†";else if(e<17)return"ą²®ą²§ą³ą²Æą²¾ą²¹ą³ą²Ø";else if(e<20)return"ą²øą²‚ą²œą³†";else return"ą²°ą²¾ą²¤ą³ą²°ą²æ"},dayOfMonthOrdinalParse:/\d{1,2}(ą²Øą³†ą³•)/,ordinal:function(e){return e+"ą²Øą³†ą³•"},week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"ą³§",2:"ą³Ø",3:"ą³©",4:"ą³Ŗ",5:"ą³«",6:"ą³¬",7:"ą³­",8:"ą³®",9:"ą³Æ",0:"ą³¦"},n={"ą³§":"1","ą³Ø":"2","ą³©":"3","ą³Ŗ":"4","ą³«":"5","ą³¬":"6","ą³­":"7","ą³®":"8","ą³Æ":"9","ą³¦":"0"},a;e.defineLocale("kn",{months:"ą²œą²Øą²µą²°ą²æ_ą²«ą³†ą²¬ą³ą²°ą²µą²°ą²æ_ą²®ą²¾ą²°ą³ą²šą³_ą²ą²Ŗą³ą²°ą²æą²²ą³_ą²®ą³†ą³•_ą²œą³‚ą²Øą³_ą²œą³ą²²ą³†ą³–_ą²†ą²—ą²øą³ą²Ÿą³_ą²øą³†ą²Ŗą³ą²Ÿą³†ą²‚ą²¬ą²°ą³_ą²…ą²•ą³ą²Ÿą³†ą³‚ą³•ą²¬ą²°ą³_ą²Øą²µą³†ą²‚ą²¬ą²°ą³_ą²”ą²æą²øą³†ą²‚ą²¬ą²°ą³".split("_"),monthsShort:"ą²œą²Ø_ą²«ą³†ą²¬ą³ą²°_ą²®ą²¾ą²°ą³ą²šą³_ą²ą²Ŗą³ą²°ą²æą²²ą³_ą²®ą³†ą³•_ą²œą³‚ą²Øą³_ą²œą³ą²²ą³†ą³–_ą²†ą²—ą²øą³ą²Ÿą³_ą²øą³†ą²Ŗą³ą²Ÿą³†ą²‚_ą²…ą²•ą³ą²Ÿą³†ą³‚ą³•_ą²Øą²µą³†ą²‚_ą²”ą²æą²øą³†ą²‚".split("_"),monthsParseExact:true,weekdays:"ą²­ą²¾ą²Øą³ą²µą²¾ą²°_ą²øą³†ą³‚ą³•ą²®ą²µą²¾ą²°_ą²®ą²‚ą²—ą²³ą²µą²¾ą²°_ą²¬ą³ą²§ą²µą²¾ą²°_ą²—ą³ą²°ą³ą²µą²¾ą²°_ą²¶ą³ą²•ą³ą²°ą²µą²¾ą²°_ą²¶ą²Øą²æą²µą²¾ą²°".split("_"),weekdaysShort:"ą²­ą²¾ą²Øą³_ą²øą³†ą³‚ą³•ą²®_ą²®ą²‚ą²—ą²³_ą²¬ą³ą²§_ą²—ą³ą²°ą³_ą²¶ą³ą²•ą³ą²°_ą²¶ą²Øą²æ".split("_"),weekdaysMin:"ą²­ą²¾_ą²øą³†ą³‚ą³•_ą²®ą²‚_ą²¬ą³_ą²—ą³_ą²¶ą³_ą²¶".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[ą²‡ą²‚ą²¦ą³] LT",nextDay:"[ą²Øą²¾ą²³ą³†] LT",nextWeek:"dddd, LT",lastDay:"[ą²Øą²æą²Øą³ą²Øą³†] LT",lastWeek:"[ą²•ą³†ą³‚ą²Øą³†ą²Æ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą²Øą²‚ą²¤ą²°",past:"%s ą²¹ą²æą²‚ą²¦ą³†",s:"ą²•ą³†ą²²ą²µą³ ą²•ą³ą²·ą²£ą²—ą²³ą³",ss:"%d ą²øą³†ą²•ą³†ą²‚ą²”ą³ą²—ą²³ą³",m:"ą²’ą²‚ą²¦ą³ ą²Øą²æą²®ą²æą²·",mm:"%d ą²Øą²æą²®ą²æą²·",h:"ą²’ą²‚ą²¦ą³ ą²—ą²‚ą²Ÿą³†",hh:"%d ą²—ą²‚ą²Ÿą³†",d:"ą²’ą²‚ą²¦ą³ ą²¦ą²æą²Ø",dd:"%d ą²¦ą²æą²Ø",M:"ą²’ą²‚ą²¦ą³ ą²¤ą²æą²‚ą²—ą²³ą³",MM:"%d ą²¤ą²æą²‚ą²—ą²³ą³",y:"ą²’ą²‚ą²¦ą³ ą²µą²°ą³ą²·",yy:"%d ą²µą²°ą³ą²·"},preparse:function(e){return e.replace(/[ą³§ą³Øą³©ą³Ŗą³«ą³¬ą³­ą³®ą³Æą³¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą²°ą²¾ą²¤ą³ą²°ą²æ|ą²¬ą³†ą²³ą²æą²—ą³ą²—ą³†|ą²®ą²§ą³ą²Æą²¾ą²¹ą³ą²Ø|ą²øą²‚ą²œą³†/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą²°ą²¾ą²¤ą³ą²°ą²æ")return e<4?e:e+12;else if(t==="ą²¬ą³†ą²³ą²æą²—ą³ą²—ą³†")return e;else if(t==="ą²®ą²§ą³ą²Æą²¾ą²¹ą³ą²Ø")return e>=10?e:e+12;else if(t==="ą²øą²‚ą²œą³†")return e+12},meridiem:function(e,t,n){if(e<4)return"ą²°ą²¾ą²¤ą³ą²°ą²æ";else if(e<10)return"ą²¬ą³†ą²³ą²æą²—ą³ą²—ą³†";else if(e<17)return"ą²®ą²§ą³ą²Æą²¾ą²¹ą³ą²Ø";else if(e<20)return"ą²øą²‚ą²œą³†";else return"ą²°ą²¾ą²¤ą³ą²°ą²æ"},dayOfMonthOrdinalParse:/\d{1,2}(ą²Øą³†ą³•)/,ordinal:function(e){return e+"ą²Øą³†ą³•"},week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("ko",{months:"1ģ›”_2ģ›”_3ģ›”_4ģ›”_5ģ›”_6ģ›”_7ģ›”_8ģ›”_9ģ›”_10ģ›”_11ģ›”_12ģ›”".split("_"),monthsShort:"1ģ›”_2ģ›”_3ģ›”_4ģ›”_5ģ›”_6ģ›”_7ģ›”_8ģ›”_9ģ›”_10ģ›”_11ģ›”_12ģ›”".split("_"),weekdays:"ģ¼ģš”ģ¼_ģ›”ģš”ģ¼_ķ™”ģš”ģ¼_ģˆ˜ģš”ģ¼_ėŖ©ģš”ģ¼_źøˆģš”ģ¼_ķ† ģš”ģ¼".split("_"),weekdaysShort:"ģ¼_ģ›”_ķ™”_ģˆ˜_ėŖ©_źøˆ_ķ† ".split("_"),weekdaysMin:"ģ¼_ģ›”_ķ™”_ģˆ˜_ėŖ©_źøˆ_ķ† ".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"YYYY.MM.DD.",LL:"YYYYė…„ MMMM Dģ¼",LLL:"YYYYė…„ MMMM Dģ¼ A h:mm",LLLL:"YYYYė…„ MMMM Dģ¼ dddd A h:mm",l:"YYYY.MM.DD.",ll:"YYYYė…„ MMMM Dģ¼",lll:"YYYYė…„ MMMM Dģ¼ A h:mm",llll:"YYYYė…„ MMMM Dģ¼ dddd A h:mm"},calendar:{sameDay:"ģ˜¤ėŠ˜ LT",nextDay:"ė‚“ģ¼ LT",nextWeek:"dddd LT",lastDay:"ģ–“ģ œ LT",lastWeek:"ģ§€ė‚œģ£¼ dddd LT",sameElse:"L"},relativeTime:{future:"%s ķ›„",past:"%s ģ „",s:"ėŖ‡ ģ“ˆ",ss:"%dģ“ˆ",m:"1ė¶„",mm:"%dė¶„",h:"ķ•œ ģ‹œź°„",hh:"%dģ‹œź°„",d:"ķ•˜ė£Ø",dd:"%dģ¼",M:"ķ•œ ė‹¬",MM:"%dė‹¬",y:"ģ¼ ė…„",yy:"%dė…„"},dayOfMonthOrdinalParse:/\d{1,2}(ģ¼|ģ›”|ģ£¼)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"ģ¼";case"M":return e+"ģ›”";case"w":case"W":return e+"ģ£¼";default:return e}},meridiemParse:/ģ˜¤ģ „|ģ˜¤ķ›„/,isPM:function(e){return e==="ģ˜¤ķ›„"},meridiem:function(e,t,n){return e<12?"ģ˜¤ģ „":"ģ˜¤ķ›„"}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("ko",{months:"1ģ›”_2ģ›”_3ģ›”_4ģ›”_5ģ›”_6ģ›”_7ģ›”_8ģ›”_9ģ›”_10ģ›”_11ģ›”_12ģ›”".split("_"),monthsShort:"1ģ›”_2ģ›”_3ģ›”_4ģ›”_5ģ›”_6ģ›”_7ģ›”_8ģ›”_9ģ›”_10ģ›”_11ģ›”_12ģ›”".split("_"),weekdays:"ģ¼ģš”ģ¼_ģ›”ģš”ģ¼_ķ™”ģš”ģ¼_ģˆ˜ģš”ģ¼_ėŖ©ģš”ģ¼_źøˆģš”ģ¼_ķ† ģš”ģ¼".split("_"),weekdaysShort:"ģ¼_ģ›”_ķ™”_ģˆ˜_ėŖ©_źøˆ_ķ† ".split("_"),weekdaysMin:"ģ¼_ģ›”_ķ™”_ģˆ˜_ėŖ©_źøˆ_ķ† ".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"YYYY.MM.DD.",LL:"YYYYė…„ MMMM Dģ¼",LLL:"YYYYė…„ MMMM Dģ¼ A h:mm",LLLL:"YYYYė…„ MMMM Dģ¼ dddd A h:mm",l:"YYYY.MM.DD.",ll:"YYYYė…„ MMMM Dģ¼",lll:"YYYYė…„ MMMM Dģ¼ A h:mm",llll:"YYYYė…„ MMMM Dģ¼ dddd A h:mm"},calendar:{sameDay:"ģ˜¤ėŠ˜ LT",nextDay:"ė‚“ģ¼ LT",nextWeek:"dddd LT",lastDay:"ģ–“ģ œ LT",lastWeek:"ģ§€ė‚œģ£¼ dddd LT",sameElse:"L"},relativeTime:{future:"%s ķ›„",past:"%s ģ „",s:"ėŖ‡ ģ“ˆ",ss:"%dģ“ˆ",m:"1ė¶„",mm:"%dė¶„",h:"ķ•œ ģ‹œź°„",hh:"%dģ‹œź°„",d:"ķ•˜ė£Ø",dd:"%dģ¼",M:"ķ•œ ė‹¬",MM:"%dė‹¬",y:"ģ¼ ė…„",yy:"%dė…„"},dayOfMonthOrdinalParse:/\d{1,2}(ģ¼|ģ›”|ģ£¼)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"ģ¼";case"M":return e+"ģ›”";case"w":case"W":return e+"ģ£¼";default:return e}},meridiemParse:/ģ˜¤ģ „|ģ˜¤ķ›„/,isPM:function(e){return e==="ģ˜¤ķ›„"},meridiem:function(e,t,n){return e<12?"ģ˜¤ģ „":"ģ˜¤ķ›„"}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"Ł”",2:"Ł¢",3:"Ł£",4:"Ł¤",5:"Ł„",6:"Ł¦",7:"Ł§",8:"ŁØ",9:"Ł©",0:"Ł "},n={"Ł”":"1","Ł¢":"2","Ł£":"3","Ł¤":"4","Ł„":"5","Ł¦":"6","Ł§":"7","ŁØ":"8","Ł©":"9","Ł ":"0"},a=["Ś©Ų§Ł†ŁˆŁ†ŪŒ ŲÆŁˆŁˆŪ•Ł…","Ų“ŁˆŲØŲ§ŲŖ","Ų¦Ų§Ų²Ų§Ų±","Ł†ŪŒŲ³Ų§Ł†","Ų¦Ų§ŪŒŲ§Ų±","Ų­ŁˆŲ²Ū•ŪŒŲ±Ų§Ł†","ŲŖŪ•Ł…Ł…ŁˆŲ²","Ų¦Ų§ŲØ","Ų¦Ū•ŪŒŁ„ŁˆŁˆŁ„","ŲŖŲ“Ų±ŪŒŁ†ŪŒ ŪŒŪ•ŁƒŪ•Ł…","ŲŖŲ“Ų±ŪŒŁ†ŪŒ ŲÆŁˆŁˆŪ•Ł…","ŁƒŲ§Ł†ŁˆŁ†ŪŒ ŪŒŪ•Ś©Ū•Ł…"],r;e.defineLocale("ku",{months:a,monthsShort:a,weekdays:"ŪŒŁ‡ā€ŒŁƒŲ“Ł‡ā€ŒŁ…Ł…Ł‡ā€Œ_ŲÆŁˆŁˆŲ“Ł‡ā€ŒŁ…Ł…Ł‡ā€Œ_Ų³ŪŽŲ“Ł‡ā€ŒŁ…Ł…Ł‡ā€Œ_Ś†ŁˆŲ§Ų±Ų“Ł‡ā€ŒŁ…Ł…Ł‡ā€Œ_Ł¾ŪŽŁ†Ų¬Ų“Ł‡ā€ŒŁ…Ł…Ł‡ā€Œ_Ł‡Ł‡ā€ŒŪŒŁ†ŪŒ_Ų“Ł‡ā€ŒŁ…Ł…Ł‡ā€Œ".split("_"),weekdaysShort:"ŪŒŁ‡ā€ŒŁƒŲ“Ł‡ā€ŒŁ…_ŲÆŁˆŁˆŲ“Ł‡ā€ŒŁ…_Ų³ŪŽŲ“Ł‡ā€ŒŁ…_Ś†ŁˆŲ§Ų±Ų“Ł‡ā€ŒŁ…_Ł¾ŪŽŁ†Ų¬Ų“Ł‡ā€ŒŁ…_Ł‡Ł‡ā€ŒŪŒŁ†ŪŒ_Ų“Ł‡ā€ŒŁ…Ł…Ł‡ā€Œ".split("_"),weekdaysMin:"ŪŒ_ŲÆ_Ų³_Ś†_Ł¾_Ł‡_Ų“".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/Ų¦ŪŽŁˆŲ§Ų±Ł‡ā€Œ|ŲØŁ‡ā€ŒŪŒŲ§Ł†ŪŒ/,isPM:function(e){return/Ų¦ŪŽŁˆŲ§Ų±Ł‡ā€Œ/.test(e)},meridiem:function(e,t,n){if(e<12)return"ŲØŁ‡ā€ŒŪŒŲ§Ł†ŪŒ";else return"Ų¦ŪŽŁˆŲ§Ų±Ł‡ā€Œ"},calendar:{sameDay:"[Ų¦Ł‡ā€ŒŁ…Ų±Ū† ŁƒŲ§ŲŖŚ˜Ł…ŪŽŲ±] LT",nextDay:"[ŲØŁ‡ā€ŒŪŒŲ§Ł†ŪŒ ŁƒŲ§ŲŖŚ˜Ł…ŪŽŲ±] LT",nextWeek:"dddd [ŁƒŲ§ŲŖŚ˜Ł…ŪŽŲ±] LT",lastDay:"[ŲÆŁˆŪŽŁ†ŪŽ ŁƒŲ§ŲŖŚ˜Ł…ŪŽŲ±] LT",lastWeek:"dddd [ŁƒŲ§ŲŖŚ˜Ł…ŪŽŲ±] LT",sameElse:"L"},relativeTime:{future:"Ł„Ł‡ā€Œ %s",past:"%s",s:"Ś†Ł‡ā€ŒŁ†ŲÆ Ś†Ų±ŁƒŁ‡ā€ŒŪŒŁ‡ā€ŒŁƒ",ss:"Ś†Ų±ŁƒŁ‡ā€Œ %d",m:"ŪŒŁ‡ā€ŒŁƒ Ų®ŁˆŁ„Ł‡ā€ŒŁƒ",mm:"%d Ų®ŁˆŁ„Ł‡ā€ŒŁƒ",h:"ŪŒŁ‡ā€ŒŁƒ ŁƒŲ§ŲŖŚ˜Ł…ŪŽŲ±",hh:"%d ŁƒŲ§ŲŖŚ˜Ł…ŪŽŲ±",d:"ŪŒŁ‡ā€ŒŁƒ Ś•Ū†Ś˜",dd:"%d Ś•Ū†Ś˜",M:"ŪŒŁ‡ā€ŒŁƒ Ł…Ų§Ł†ŚÆ",MM:"%d Ł…Ų§Ł†ŚÆ",y:"ŪŒŁ‡ā€ŒŁƒ Ų³Ų§Śµ",yy:"%d Ų³Ų§Śµ"},preparse:function(e){return e.replace(/[Ł”Ł¢Ł£Ł¤Ł„Ł¦Ł§ŁØŁ©Ł ]/g,function(e){return n[e]}).replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"ŲŒ")},week:{dow:6,doy:12}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"Ł”",2:"Ł¢",3:"Ł£",4:"Ł¤",5:"Ł„",6:"Ł¦",7:"Ł§",8:"ŁØ",9:"Ł©",0:"Ł "},n={"Ł”":"1","Ł¢":"2","Ł£":"3","Ł¤":"4","Ł„":"5","Ł¦":"6","Ł§":"7","ŁØ":"8","Ł©":"9","Ł ":"0"},a=["Ś©Ų§Ł†ŁˆŁ†ŪŒ ŲÆŁˆŁˆŪ•Ł…","Ų“ŁˆŲØŲ§ŲŖ","Ų¦Ų§Ų²Ų§Ų±","Ł†ŪŒŲ³Ų§Ł†","Ų¦Ų§ŪŒŲ§Ų±","Ų­ŁˆŲ²Ū•ŪŒŲ±Ų§Ł†","ŲŖŪ•Ł…Ł…ŁˆŲ²","Ų¦Ų§ŲØ","Ų¦Ū•ŪŒŁ„ŁˆŁˆŁ„","ŲŖŲ“Ų±ŪŒŁ†ŪŒ ŪŒŪ•ŁƒŪ•Ł…","ŲŖŲ“Ų±ŪŒŁ†ŪŒ ŲÆŁˆŁˆŪ•Ł…","ŁƒŲ§Ł†ŁˆŁ†ŪŒ ŪŒŪ•Ś©Ū•Ł…"],r;e.defineLocale("ku",{months:a,monthsShort:a,weekdays:"ŪŒŁ‡ā€ŒŁƒŲ“Ł‡ā€ŒŁ…Ł…Ł‡ā€Œ_ŲÆŁˆŁˆŲ“Ł‡ā€ŒŁ…Ł…Ł‡ā€Œ_Ų³ŪŽŲ“Ł‡ā€ŒŁ…Ł…Ł‡ā€Œ_Ś†ŁˆŲ§Ų±Ų“Ł‡ā€ŒŁ…Ł…Ł‡ā€Œ_Ł¾ŪŽŁ†Ų¬Ų“Ł‡ā€ŒŁ…Ł…Ł‡ā€Œ_Ł‡Ł‡ā€ŒŪŒŁ†ŪŒ_Ų“Ł‡ā€ŒŁ…Ł…Ł‡ā€Œ".split("_"),weekdaysShort:"ŪŒŁ‡ā€ŒŁƒŲ“Ł‡ā€ŒŁ…_ŲÆŁˆŁˆŲ“Ł‡ā€ŒŁ…_Ų³ŪŽŲ“Ł‡ā€ŒŁ…_Ś†ŁˆŲ§Ų±Ų“Ł‡ā€ŒŁ…_Ł¾ŪŽŁ†Ų¬Ų“Ł‡ā€ŒŁ…_Ł‡Ł‡ā€ŒŪŒŁ†ŪŒ_Ų“Ł‡ā€ŒŁ…Ł…Ł‡ā€Œ".split("_"),weekdaysMin:"ŪŒ_ŲÆ_Ų³_Ś†_Ł¾_Ł‡_Ų“".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/Ų¦ŪŽŁˆŲ§Ų±Ł‡ā€Œ|ŲØŁ‡ā€ŒŪŒŲ§Ł†ŪŒ/,isPM:function(e){return/Ų¦ŪŽŁˆŲ§Ų±Ł‡ā€Œ/.test(e)},meridiem:function(e,t,n){if(e<12)return"ŲØŁ‡ā€ŒŪŒŲ§Ł†ŪŒ";else return"Ų¦ŪŽŁˆŲ§Ų±Ł‡ā€Œ"},calendar:{sameDay:"[Ų¦Ł‡ā€ŒŁ…Ų±Ū† ŁƒŲ§ŲŖŚ˜Ł…ŪŽŲ±] LT",nextDay:"[ŲØŁ‡ā€ŒŪŒŲ§Ł†ŪŒ ŁƒŲ§ŲŖŚ˜Ł…ŪŽŲ±] LT",nextWeek:"dddd [ŁƒŲ§ŲŖŚ˜Ł…ŪŽŲ±] LT",lastDay:"[ŲÆŁˆŪŽŁ†ŪŽ ŁƒŲ§ŲŖŚ˜Ł…ŪŽŲ±] LT",lastWeek:"dddd [ŁƒŲ§ŲŖŚ˜Ł…ŪŽŲ±] LT",sameElse:"L"},relativeTime:{future:"Ł„Ł‡ā€Œ %s",past:"%s",s:"Ś†Ł‡ā€ŒŁ†ŲÆ Ś†Ų±ŁƒŁ‡ā€ŒŪŒŁ‡ā€ŒŁƒ",ss:"Ś†Ų±ŁƒŁ‡ā€Œ %d",m:"ŪŒŁ‡ā€ŒŁƒ Ų®ŁˆŁ„Ł‡ā€ŒŁƒ",mm:"%d Ų®ŁˆŁ„Ł‡ā€ŒŁƒ",h:"ŪŒŁ‡ā€ŒŁƒ ŁƒŲ§ŲŖŚ˜Ł…ŪŽŲ±",hh:"%d ŁƒŲ§ŲŖŚ˜Ł…ŪŽŲ±",d:"ŪŒŁ‡ā€ŒŁƒ Ś•Ū†Ś˜",dd:"%d Ś•Ū†Ś˜",M:"ŪŒŁ‡ā€ŒŁƒ Ł…Ų§Ł†ŚÆ",MM:"%d Ł…Ų§Ł†ŚÆ",y:"ŪŒŁ‡ā€ŒŁƒ Ų³Ų§Śµ",yy:"%d Ų³Ų§Śµ"},preparse:function(e){return e.replace(/[Ł”Ł¢Ł£Ł¤Ł„Ł¦Ł§ŁØŁ©Ł ]/g,function(e){return n[e]}).replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"ŲŒ")},week:{dow:6,doy:12}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var a={0:"-чŅÆ",1:"-чŠø",2:"-чŠø",3:"-чŅÆ",4:"-чŅÆ",5:"-чŠø",6:"-чы",7:"-чŠø",8:"-чŠø",9:"-чу",10:"-чу",20:"-чы",30:"-чу",40:"-чы",50:"-чŅÆ",60:"-чы",70:"-чŠø",80:"-чŠø",90:"-чу",100:"-чŅÆ"},t;e.defineLocale("ky",{months:"яŠ½Š²Š°Ń€ŃŒ_фŠµŠ²Ń€Š°Š»ŃŒ_Š¼Š°Ń€Ń‚_Š°ŠæрŠµŠ»ŃŒ_Š¼Š°Š¹_ŠøюŠ½ŃŒ_ŠøюŠ»ŃŒ_Š°Š²Š³ŃƒŃŃ‚_сŠµŠ½Ń‚яŠ±Ń€ŃŒ_Š¾ŠŗтяŠ±Ń€ŃŒ_Š½Š¾ŃŠ±Ń€ŃŒ_Š“ŠµŠŗŠ°Š±Ń€ŃŒ".split("_"),monthsShort:"яŠ½Š²_фŠµŠ²_Š¼Š°Ń€Ń‚_Š°Šæр_Š¼Š°Š¹_ŠøюŠ½ŃŒ_ŠøюŠ»ŃŒ_Š°Š²Š³_сŠµŠ½_Š¾Šŗт_Š½Š¾Ń_Š“ŠµŠŗ".split("_"),weekdays:"Š–ŠµŠŗшŠµŠ¼Š±Šø_Š”ŅÆŠ¹ŃˆÓ©Š¼Š±ŅÆ_ŠØŠµŠ¹ŃˆŠµŠ¼Š±Šø_ŠØŠ°Ń€ŃˆŠµŠ¼Š±Šø_Š‘ŠµŠ¹ŃˆŠµŠ¼Š±Šø_Š–ŃƒŠ¼Š°_Š˜ŃˆŠµŠ¼Š±Šø".split("_"),weekdaysShort:"Š–ŠµŠŗ_Š”ŅÆŠ¹_ŠØŠµŠ¹_ŠØŠ°Ń€_Š‘ŠµŠ¹_Š–ŃƒŠ¼_Š˜ŃˆŠµ".split("_"),weekdaysMin:"Š–Šŗ_Š”Š¹_ŠØŠ¹_ŠØр_Š‘Š¹_Š–Š¼_Š˜Ńˆ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Š‘ŅÆŠ³ŅÆŠ½ сŠ°Š°Ń‚] LT",nextDay:"[Š­Ń€Ń‚ŠµŅ£ сŠ°Š°Ń‚] LT",nextWeek:"dddd [сŠ°Š°Ń‚] LT",lastDay:"[ŠšŠµŃ‡ŃŃ сŠ°Š°Ń‚] LT",lastWeek:"[ÓØтŠŗÓ©Š½ Š°ŠæтŠ°Š½Ń‹Š½] dddd [ŠŗŅÆŠ½ŅÆ] [сŠ°Š°Ń‚] LT",sameElse:"L"},relativeTime:{future:"%s ŠøчŠøŠ½Š“Šµ",past:"%s Š¼ŃƒŃ€ŃƒŠ½",s:"Š±ŠøрŠ½ŠµŃ‡Šµ сŠµŠŗуŠ½Š“",ss:"%d сŠµŠŗуŠ½Š“",m:"Š±Šøр Š¼ŅÆŠ½Ó©Ń‚",mm:"%d Š¼ŅÆŠ½Ó©Ń‚",h:"Š±Šøр сŠ°Š°Ń‚",hh:"%d сŠ°Š°Ń‚",d:"Š±Šøр ŠŗŅÆŠ½",dd:"%d ŠŗŅÆŠ½",M:"Š±Šøр Š°Š¹",MM:"%d Š°Š¹",y:"Š±Šøр Š¶Ń‹Š»",yy:"%d Š¶Ń‹Š»"},dayOfMonthOrdinalParse:/\d{1,2}-(чŠø|чы|чŅÆ|чу)/,ordinal:function(e){var t=e%10,n=e>=100?100:null;return e+(a[e]||a[t]||a[n])},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var a={0:"-чŅÆ",1:"-чŠø",2:"-чŠø",3:"-чŅÆ",4:"-чŅÆ",5:"-чŠø",6:"-чы",7:"-чŠø",8:"-чŠø",9:"-чу",10:"-чу",20:"-чы",30:"-чу",40:"-чы",50:"-чŅÆ",60:"-чы",70:"-чŠø",80:"-чŠø",90:"-чу",100:"-чŅÆ"},t;e.defineLocale("ky",{months:"яŠ½Š²Š°Ń€ŃŒ_фŠµŠ²Ń€Š°Š»ŃŒ_Š¼Š°Ń€Ń‚_Š°ŠæрŠµŠ»ŃŒ_Š¼Š°Š¹_ŠøюŠ½ŃŒ_ŠøюŠ»ŃŒ_Š°Š²Š³ŃƒŃŃ‚_сŠµŠ½Ń‚яŠ±Ń€ŃŒ_Š¾ŠŗтяŠ±Ń€ŃŒ_Š½Š¾ŃŠ±Ń€ŃŒ_Š“ŠµŠŗŠ°Š±Ń€ŃŒ".split("_"),monthsShort:"яŠ½Š²_фŠµŠ²_Š¼Š°Ń€Ń‚_Š°Šæр_Š¼Š°Š¹_ŠøюŠ½ŃŒ_ŠøюŠ»ŃŒ_Š°Š²Š³_сŠµŠ½_Š¾Šŗт_Š½Š¾Ń_Š“ŠµŠŗ".split("_"),weekdays:"Š–ŠµŠŗшŠµŠ¼Š±Šø_Š”ŅÆŠ¹ŃˆÓ©Š¼Š±ŅÆ_ŠØŠµŠ¹ŃˆŠµŠ¼Š±Šø_ŠØŠ°Ń€ŃˆŠµŠ¼Š±Šø_Š‘ŠµŠ¹ŃˆŠµŠ¼Š±Šø_Š–ŃƒŠ¼Š°_Š˜ŃˆŠµŠ¼Š±Šø".split("_"),weekdaysShort:"Š–ŠµŠŗ_Š”ŅÆŠ¹_ŠØŠµŠ¹_ŠØŠ°Ń€_Š‘ŠµŠ¹_Š–ŃƒŠ¼_Š˜ŃˆŠµ".split("_"),weekdaysMin:"Š–Šŗ_Š”Š¹_ŠØŠ¹_ŠØр_Š‘Š¹_Š–Š¼_Š˜Ńˆ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Š‘ŅÆŠ³ŅÆŠ½ сŠ°Š°Ń‚] LT",nextDay:"[Š­Ń€Ń‚ŠµŅ£ сŠ°Š°Ń‚] LT",nextWeek:"dddd [сŠ°Š°Ń‚] LT",lastDay:"[ŠšŠµŃ‡ŃŃ сŠ°Š°Ń‚] LT",lastWeek:"[ÓØтŠŗÓ©Š½ Š°ŠæтŠ°Š½Ń‹Š½] dddd [ŠŗŅÆŠ½ŅÆ] [сŠ°Š°Ń‚] LT",sameElse:"L"},relativeTime:{future:"%s ŠøчŠøŠ½Š“Šµ",past:"%s Š¼ŃƒŃ€ŃƒŠ½",s:"Š±ŠøрŠ½ŠµŃ‡Šµ сŠµŠŗуŠ½Š“",ss:"%d сŠµŠŗуŠ½Š“",m:"Š±Šøр Š¼ŅÆŠ½Ó©Ń‚",mm:"%d Š¼ŅÆŠ½Ó©Ń‚",h:"Š±Šøр сŠ°Š°Ń‚",hh:"%d сŠ°Š°Ń‚",d:"Š±Šøр ŠŗŅÆŠ½",dd:"%d ŠŗŅÆŠ½",M:"Š±Šøр Š°Š¹",MM:"%d Š°Š¹",y:"Š±Šøр Š¶Ń‹Š»",yy:"%d Š¶Ń‹Š»"},dayOfMonthOrdinalParse:/\d{1,2}-(чŠø|чы|чŅÆ|чу)/,ordinal:function(e){var t=e%10,n=e>=100?100:null;return e+(a[e]||a[t]||a[n])},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function t(e,t,n,a){var r={m:["eng Minutt","enger Minutt"],h:["eng Stonn","enger Stonn"],d:["een Dag","engem Dag"],M:["ee Mount","engem Mount"],y:["ee Joer","engem Joer"]};return t?r[n][0]:r[n][1]}function n(e){var t=e.substr(0,e.indexOf(" "));if(r(t))return"a "+e;return"an "+e}function a(e){var t=e.substr(0,e.indexOf(" "));if(r(t))return"viru "+e;return"virun "+e}function r(e){e=parseInt(e,10);if(isNaN(e))return false;if(e<0)return true;else if(e<10){if(4<=e&&e<=7)return true;return false}else if(e<100){var t=e%10,n=e/10;if(t===0)return r(n);return r(t)}else if(e<1e4){while(e>=10)e=e/10;return r(e)}else{e=e/1e3;return r(e)}}var o;e.defineLocale("lb",{months:"Januar_Februar_MƤerz_AbrĆ«ll_Mee_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Abr._Mee_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),monthsParseExact:true,weekdays:"Sonndeg_MĆ©indeg_DĆ«nschdeg_MĆ«ttwoch_Donneschdeg_Freideg_Samschdeg".split("_"),weekdaysShort:"So._MĆ©._DĆ«._MĆ«._Do._Fr._Sa.".split("_"),weekdaysMin:"So_MĆ©_DĆ«_MĆ«_Do_Fr_Sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm [Auer]",LTS:"H:mm:ss [Auer]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm [Auer]",LLLL:"dddd, D. MMMM YYYY H:mm [Auer]"},calendar:{sameDay:"[Haut um] LT",sameElse:"L",nextDay:"[Muer um] LT",nextWeek:"dddd [um] LT",lastDay:"[GĆ«schter um] LT",lastWeek:function(){switch(this.day()){case 2:case 4:return"[Leschten] dddd [um] LT";default:return"[Leschte] dddd [um] LT"}}},relativeTime:{future:n,past:a,s:"e puer Sekonnen",ss:"%d Sekonnen",m:t,mm:"%d Minutten",h:t,hh:"%d Stonnen",d:t,dd:"%d Deeg",M:t,MM:"%d MĆ©int",y:t,yy:"%d Joer"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function t(e,t,n,a){var r={m:["eng Minutt","enger Minutt"],h:["eng Stonn","enger Stonn"],d:["een Dag","engem Dag"],M:["ee Mount","engem Mount"],y:["ee Joer","engem Joer"]};return t?r[n][0]:r[n][1]}function n(e){var t=e.substr(0,e.indexOf(" "));if(r(t))return"a "+e;return"an "+e}function a(e){var t=e.substr(0,e.indexOf(" "));if(r(t))return"viru "+e;return"virun "+e}function r(e){e=parseInt(e,10);if(isNaN(e))return false;if(e<0)return true;else if(e<10){if(4<=e&&e<=7)return true;return false}else if(e<100){var t=e%10,n=e/10;if(t===0)return r(n);return r(t)}else if(e<1e4){while(e>=10)e=e/10;return r(e)}else{e=e/1e3;return r(e)}}var o;e.defineLocale("lb",{months:"Januar_Februar_MƤerz_AbrĆ«ll_Mee_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Abr._Mee_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),monthsParseExact:true,weekdays:"Sonndeg_MĆ©indeg_DĆ«nschdeg_MĆ«ttwoch_Donneschdeg_Freideg_Samschdeg".split("_"),weekdaysShort:"So._MĆ©._DĆ«._MĆ«._Do._Fr._Sa.".split("_"),weekdaysMin:"So_MĆ©_DĆ«_MĆ«_Do_Fr_Sa".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm [Auer]",LTS:"H:mm:ss [Auer]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm [Auer]",LLLL:"dddd, D. MMMM YYYY H:mm [Auer]"},calendar:{sameDay:"[Haut um] LT",sameElse:"L",nextDay:"[Muer um] LT",nextWeek:"dddd [um] LT",lastDay:"[GĆ«schter um] LT",lastWeek:function(){switch(this.day()){case 2:case 4:return"[Leschten] dddd [um] LT";default:return"[Leschte] dddd [um] LT"}}},relativeTime:{future:n,past:a,s:"e puer Sekonnen",ss:"%d Sekonnen",m:t,mm:"%d Minutten",h:t,hh:"%d Stonnen",d:t,dd:"%d Deeg",M:t,MM:"%d MĆ©int",y:t,yy:"%d Joer"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("lo",{months:"ąŗ”ąŗ±ąŗ‡ąŗąŗ­ąŗ™_ąŗąŗøąŗ”ąŗžąŗ²_ąŗ”ąŗµąŗ™ąŗ²_ą»€ąŗ”ąŗŖąŗ²_ąŗžąŗ¶ąŗ”ąŗŖąŗ°ąŗžąŗ²_ąŗ”ąŗ“ąŗ–ąŗøąŗ™ąŗ²_ąŗą»ąŗ„ąŗ°ąŗąŗ»ąŗ”_ąŗŖąŗ“ąŗ‡ąŗ«ąŗ²_ąŗąŗ±ąŗ™ąŗąŗ²_ąŗ•ąŗøąŗ„ąŗ²_ąŗžąŗ°ąŗˆąŗ“ąŗ_ąŗ—ąŗ±ąŗ™ąŗ§ąŗ²".split("_"),monthsShort:"ąŗ”ąŗ±ąŗ‡ąŗąŗ­ąŗ™_ąŗąŗøąŗ”ąŗžąŗ²_ąŗ”ąŗµąŗ™ąŗ²_ą»€ąŗ”ąŗŖąŗ²_ąŗžąŗ¶ąŗ”ąŗŖąŗ°ąŗžąŗ²_ąŗ”ąŗ“ąŗ–ąŗøąŗ™ąŗ²_ąŗą»ąŗ„ąŗ°ąŗąŗ»ąŗ”_ąŗŖąŗ“ąŗ‡ąŗ«ąŗ²_ąŗąŗ±ąŗ™ąŗąŗ²_ąŗ•ąŗøąŗ„ąŗ²_ąŗžąŗ°ąŗˆąŗ“ąŗ_ąŗ—ąŗ±ąŗ™ąŗ§ąŗ²".split("_"),weekdays:"ąŗ­ąŗ²ąŗ—ąŗ“ąŗ”_ąŗˆąŗ±ąŗ™_ąŗ­ąŗ±ąŗ‡ąŗ„ąŗ²ąŗ™_ąŗžąŗøąŗ”_ąŗžąŗ°ąŗ«ąŗ±ąŗ”_ąŗŖąŗøąŗ_ą»€ąŗŖąŗ»ąŗ²".split("_"),weekdaysShort:"ąŗ—ąŗ“ąŗ”_ąŗˆąŗ±ąŗ™_ąŗ­ąŗ±ąŗ‡ąŗ„ąŗ²ąŗ™_ąŗžąŗøąŗ”_ąŗžąŗ°ąŗ«ąŗ±ąŗ”_ąŗŖąŗøąŗ_ą»€ąŗŖąŗ»ąŗ²".split("_"),weekdaysMin:"ąŗ—_ąŗˆ_ąŗ­ąŗ„_ąŗž_ąŗžąŗ«_ąŗŖąŗ_ąŗŖ".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"ąŗ§ąŗ±ąŗ™dddd D MMMM YYYY HH:mm"},meridiemParse:/ąŗ•ąŗ­ąŗ™ą»€ąŗŠąŗ»ą»‰ąŗ²|ąŗ•ąŗ­ąŗ™ą»ąŗ„ąŗ‡/,isPM:function(e){return e==="ąŗ•ąŗ­ąŗ™ą»ąŗ„ąŗ‡"},meridiem:function(e,t,n){if(e<12)return"ąŗ•ąŗ­ąŗ™ą»€ąŗŠąŗ»ą»‰ąŗ²";else return"ąŗ•ąŗ­ąŗ™ą»ąŗ„ąŗ‡"},calendar:{sameDay:"[ąŗ”ąŗ·ą»‰ąŗ™ąŗµą»‰ą»€ąŗ§ąŗ„ąŗ²] LT",nextDay:"[ąŗ”ąŗ·ą»‰ąŗ­ąŗ·ą»ˆąŗ™ą»€ąŗ§ąŗ„ąŗ²] LT",nextWeek:"[ąŗ§ąŗ±ąŗ™]dddd[ą»œą»‰ąŗ²ą»€ąŗ§ąŗ„ąŗ²] LT",lastDay:"[ąŗ”ąŗ·ą»‰ąŗ§ąŗ²ąŗ™ąŗ™ąŗµą»‰ą»€ąŗ§ąŗ„ąŗ²] LT",lastWeek:"[ąŗ§ąŗ±ąŗ™]dddd[ą»ąŗ„ą»‰ąŗ§ąŗ™ąŗµą»‰ą»€ąŗ§ąŗ„ąŗ²] LT",sameElse:"L"},relativeTime:{future:"ąŗ­ąŗµąŗ %s",past:"%sąŗœą»ˆąŗ²ąŗ™ąŗ”ąŗ²",s:"ąŗšą»ą»ˆą»€ąŗ—ąŗ»ą»ˆąŗ²ą»ƒąŗ”ąŗ§ąŗ“ąŗ™ąŗ²ąŗ—ąŗµ",ss:"%d ąŗ§ąŗ“ąŗ™ąŗ²ąŗ—ąŗµ",m:"1 ąŗ™ąŗ²ąŗ—ąŗµ",mm:"%d ąŗ™ąŗ²ąŗ—ąŗµ",h:"1 ąŗŠąŗ»ą»ˆąŗ§ą»‚ąŗ”ąŗ‡",hh:"%d ąŗŠąŗ»ą»ˆąŗ§ą»‚ąŗ”ąŗ‡",d:"1 ąŗ”ąŗ·ą»‰",dd:"%d ąŗ”ąŗ·ą»‰",M:"1 ą»€ąŗ”ąŗ·ąŗ­ąŗ™",MM:"%d ą»€ąŗ”ąŗ·ąŗ­ąŗ™",y:"1 ąŗ›ąŗµ",yy:"%d ąŗ›ąŗµ"},dayOfMonthOrdinalParse:/(ąŗ—ąŗµą»ˆ)\d{1,2}/,ordinal:function(e){return"ąŗ—ąŗµą»ˆ"+e}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("lo",{months:"ąŗ”ąŗ±ąŗ‡ąŗąŗ­ąŗ™_ąŗąŗøąŗ”ąŗžąŗ²_ąŗ”ąŗµąŗ™ąŗ²_ą»€ąŗ”ąŗŖąŗ²_ąŗžąŗ¶ąŗ”ąŗŖąŗ°ąŗžąŗ²_ąŗ”ąŗ“ąŗ–ąŗøąŗ™ąŗ²_ąŗą»ąŗ„ąŗ°ąŗąŗ»ąŗ”_ąŗŖąŗ“ąŗ‡ąŗ«ąŗ²_ąŗąŗ±ąŗ™ąŗąŗ²_ąŗ•ąŗøąŗ„ąŗ²_ąŗžąŗ°ąŗˆąŗ“ąŗ_ąŗ—ąŗ±ąŗ™ąŗ§ąŗ²".split("_"),monthsShort:"ąŗ”ąŗ±ąŗ‡ąŗąŗ­ąŗ™_ąŗąŗøąŗ”ąŗžąŗ²_ąŗ”ąŗµąŗ™ąŗ²_ą»€ąŗ”ąŗŖąŗ²_ąŗžąŗ¶ąŗ”ąŗŖąŗ°ąŗžąŗ²_ąŗ”ąŗ“ąŗ–ąŗøąŗ™ąŗ²_ąŗą»ąŗ„ąŗ°ąŗąŗ»ąŗ”_ąŗŖąŗ“ąŗ‡ąŗ«ąŗ²_ąŗąŗ±ąŗ™ąŗąŗ²_ąŗ•ąŗøąŗ„ąŗ²_ąŗžąŗ°ąŗˆąŗ“ąŗ_ąŗ—ąŗ±ąŗ™ąŗ§ąŗ²".split("_"),weekdays:"ąŗ­ąŗ²ąŗ—ąŗ“ąŗ”_ąŗˆąŗ±ąŗ™_ąŗ­ąŗ±ąŗ‡ąŗ„ąŗ²ąŗ™_ąŗžąŗøąŗ”_ąŗžąŗ°ąŗ«ąŗ±ąŗ”_ąŗŖąŗøąŗ_ą»€ąŗŖąŗ»ąŗ²".split("_"),weekdaysShort:"ąŗ—ąŗ“ąŗ”_ąŗˆąŗ±ąŗ™_ąŗ­ąŗ±ąŗ‡ąŗ„ąŗ²ąŗ™_ąŗžąŗøąŗ”_ąŗžąŗ°ąŗ«ąŗ±ąŗ”_ąŗŖąŗøąŗ_ą»€ąŗŖąŗ»ąŗ²".split("_"),weekdaysMin:"ąŗ—_ąŗˆ_ąŗ­ąŗ„_ąŗž_ąŗžąŗ«_ąŗŖąŗ_ąŗŖ".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"ąŗ§ąŗ±ąŗ™dddd D MMMM YYYY HH:mm"},meridiemParse:/ąŗ•ąŗ­ąŗ™ą»€ąŗŠąŗ»ą»‰ąŗ²|ąŗ•ąŗ­ąŗ™ą»ąŗ„ąŗ‡/,isPM:function(e){return e==="ąŗ•ąŗ­ąŗ™ą»ąŗ„ąŗ‡"},meridiem:function(e,t,n){if(e<12)return"ąŗ•ąŗ­ąŗ™ą»€ąŗŠąŗ»ą»‰ąŗ²";else return"ąŗ•ąŗ­ąŗ™ą»ąŗ„ąŗ‡"},calendar:{sameDay:"[ąŗ”ąŗ·ą»‰ąŗ™ąŗµą»‰ą»€ąŗ§ąŗ„ąŗ²] LT",nextDay:"[ąŗ”ąŗ·ą»‰ąŗ­ąŗ·ą»ˆąŗ™ą»€ąŗ§ąŗ„ąŗ²] LT",nextWeek:"[ąŗ§ąŗ±ąŗ™]dddd[ą»œą»‰ąŗ²ą»€ąŗ§ąŗ„ąŗ²] LT",lastDay:"[ąŗ”ąŗ·ą»‰ąŗ§ąŗ²ąŗ™ąŗ™ąŗµą»‰ą»€ąŗ§ąŗ„ąŗ²] LT",lastWeek:"[ąŗ§ąŗ±ąŗ™]dddd[ą»ąŗ„ą»‰ąŗ§ąŗ™ąŗµą»‰ą»€ąŗ§ąŗ„ąŗ²] LT",sameElse:"L"},relativeTime:{future:"ąŗ­ąŗµąŗ %s",past:"%sąŗœą»ˆąŗ²ąŗ™ąŗ”ąŗ²",s:"ąŗšą»ą»ˆą»€ąŗ—ąŗ»ą»ˆąŗ²ą»ƒąŗ”ąŗ§ąŗ“ąŗ™ąŗ²ąŗ—ąŗµ",ss:"%d ąŗ§ąŗ“ąŗ™ąŗ²ąŗ—ąŗµ",m:"1 ąŗ™ąŗ²ąŗ—ąŗµ",mm:"%d ąŗ™ąŗ²ąŗ—ąŗµ",h:"1 ąŗŠąŗ»ą»ˆąŗ§ą»‚ąŗ”ąŗ‡",hh:"%d ąŗŠąŗ»ą»ˆąŗ§ą»‚ąŗ”ąŗ‡",d:"1 ąŗ”ąŗ·ą»‰",dd:"%d ąŗ”ąŗ·ą»‰",M:"1 ą»€ąŗ”ąŗ·ąŗ­ąŗ™",MM:"%d ą»€ąŗ”ąŗ·ąŗ­ąŗ™",y:"1 ąŗ›ąŗµ",yy:"%d ąŗ›ąŗµ"},dayOfMonthOrdinalParse:/(ąŗ—ąŗµą»ˆ)\d{1,2}/,ordinal:function(e){return"ąŗ—ąŗµą»ˆ"+e}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={ss:"sekundė_sekundžių_sekundes",m:"minutė_minutės_minutę",mm:"minutės_minučių_minutes",h:"valanda_valandos_valandą",hh:"valandos_valandų_valandas",d:"diena_dienos_dieną",dd:"dienos_dienų_dienas",M:"mėnuo_mėnesio_mėnesÄÆ",MM:"mėnesiai_mėnesių_mėnesius",y:"metai_metų_metus",yy:"metai_metų_metus"},n;function a(e,t,n,a){if(t)return"kelios sekundės";else return a?"kelių sekundžių":"kelias sekundes"}function o(e,t,n,a){return t?l(n)[0]:a?l(n)[1]:l(n)[2]}function i(e){return e%10===0||e>10&&e<20}function l(e){return t[e].split("_")}function r(e,t,n,a){var r=e+" ";if(e===1)return r+o(e,t,n[0],a);else if(t)return r+(i(e)?l(n)[1]:l(n)[0]);else if(a)return r+l(n)[1];else return r+(i(e)?l(n)[1]:l(n)[2])}e.defineLocale("lt",{months:{format:"sausio_vasario_kovo_balandžio_gegužės_birželio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio".split("_"),standalone:"sausis_vasaris_kovas_balandis_gegužė_birželis_liepa_rugpjÅ«tis_rugsėjis_spalis_lapkritis_gruodis".split("_"),isFormat:/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|MMMM?(\[[^\[\]]*\]|\s)+D[oD]?/},monthsShort:"sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd".split("_"),weekdays:{format:"sekmadienÄÆ_pirmadienÄÆ_antradienÄÆ_trečiadienÄÆ_ketvirtadienÄÆ_penktadienÄÆ_Å”eÅ”tadienÄÆ".split("_"),standalone:"sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_Å”eÅ”tadienis".split("_"),isFormat:/dddd HH:mm/},weekdaysShort:"Sek_Pir_Ant_Tre_Ket_Pen_Å eÅ”".split("_"),weekdaysMin:"S_P_A_T_K_Pn_Å ".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY [m.] MMMM D [d.]",LLL:"YYYY [m.] MMMM D [d.], HH:mm [val.]",LLLL:"YYYY [m.] MMMM D [d.], dddd, HH:mm [val.]",l:"YYYY-MM-DD",ll:"YYYY [m.] MMMM D [d.]",lll:"YYYY [m.] MMMM D [d.], HH:mm [val.]",llll:"YYYY [m.] MMMM D [d.], ddd, HH:mm [val.]"},calendar:{sameDay:"[Å iandien] LT",nextDay:"[Rytoj] LT",nextWeek:"dddd LT",lastDay:"[Vakar] LT",lastWeek:"[PraėjusÄÆ] dddd LT",sameElse:"L"},relativeTime:{future:"po %s",past:"prieÅ” %s",s:a,ss:r,m:o,mm:r,h:o,hh:r,d:o,dd:r,M:o,MM:r,y:o,yy:r},dayOfMonthOrdinalParse:/\d{1,2}-oji/,ordinal:function(e){return e+"-oji"},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={ss:"sekundė_sekundžių_sekundes",m:"minutė_minutės_minutę",mm:"minutės_minučių_minutes",h:"valanda_valandos_valandą",hh:"valandos_valandų_valandas",d:"diena_dienos_dieną",dd:"dienos_dienų_dienas",M:"mėnuo_mėnesio_mėnesÄÆ",MM:"mėnesiai_mėnesių_mėnesius",y:"metai_metų_metus",yy:"metai_metų_metus"},n;function a(e,t,n,a){if(t)return"kelios sekundės";else return a?"kelių sekundžių":"kelias sekundes"}function o(e,t,n,a){return t?l(n)[0]:a?l(n)[1]:l(n)[2]}function i(e){return e%10===0||e>10&&e<20}function l(e){return t[e].split("_")}function r(e,t,n,a){var r=e+" ";if(e===1)return r+o(e,t,n[0],a);else if(t)return r+(i(e)?l(n)[1]:l(n)[0]);else if(a)return r+l(n)[1];else return r+(i(e)?l(n)[1]:l(n)[2])}e.defineLocale("lt",{months:{format:"sausio_vasario_kovo_balandžio_gegužės_birželio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio".split("_"),standalone:"sausis_vasaris_kovas_balandis_gegužė_birželis_liepa_rugpjÅ«tis_rugsėjis_spalis_lapkritis_gruodis".split("_"),isFormat:/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|MMMM?(\[[^\[\]]*\]|\s)+D[oD]?/},monthsShort:"sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd".split("_"),weekdays:{format:"sekmadienÄÆ_pirmadienÄÆ_antradienÄÆ_trečiadienÄÆ_ketvirtadienÄÆ_penktadienÄÆ_Å”eÅ”tadienÄÆ".split("_"),standalone:"sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_Å”eÅ”tadienis".split("_"),isFormat:/dddd HH:mm/},weekdaysShort:"Sek_Pir_Ant_Tre_Ket_Pen_Å eÅ”".split("_"),weekdaysMin:"S_P_A_T_K_Pn_Å ".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY [m.] MMMM D [d.]",LLL:"YYYY [m.] MMMM D [d.], HH:mm [val.]",LLLL:"YYYY [m.] MMMM D [d.], dddd, HH:mm [val.]",l:"YYYY-MM-DD",ll:"YYYY [m.] MMMM D [d.]",lll:"YYYY [m.] MMMM D [d.], HH:mm [val.]",llll:"YYYY [m.] MMMM D [d.], ddd, HH:mm [val.]"},calendar:{sameDay:"[Å iandien] LT",nextDay:"[Rytoj] LT",nextWeek:"dddd LT",lastDay:"[Vakar] LT",lastWeek:"[PraėjusÄÆ] dddd LT",sameElse:"L"},relativeTime:{future:"po %s",past:"prieÅ” %s",s:a,ss:r,m:o,mm:r,h:o,hh:r,d:o,dd:r,M:o,MM:r,y:o,yy:r},dayOfMonthOrdinalParse:/\d{1,2}-oji/,ordinal:function(e){return e+"-oji"},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var a={ss:"sekundes_sekundēm_sekunde_sekundes".split("_"),m:"minÅ«tes_minÅ«tēm_minÅ«te_minÅ«tes".split("_"),mm:"minÅ«tes_minÅ«tēm_minÅ«te_minÅ«tes".split("_"),h:"stundas_stundām_stunda_stundas".split("_"),hh:"stundas_stundām_stunda_stundas".split("_"),d:"dienas_dienām_diena_dienas".split("_"),dd:"dienas_dienām_diena_dienas".split("_"),M:"mēneÅ”a_mēneÅ”iem_mēnesis_mēneÅ”i".split("_"),MM:"mēneÅ”a_mēneÅ”iem_mēnesis_mēneÅ”i".split("_"),y:"gada_gadiem_gads_gadi".split("_"),yy:"gada_gadiem_gads_gadi".split("_")},t;function r(e,t,n){if(n)return t%10===1&&t%100!==11?e[2]:e[3];else return t%10===1&&t%100!==11?e[0]:e[1]}function n(e,t,n){return e+" "+r(a[n],e,t)}function o(e,t,n){return r(a[n],e,t)}function i(e,t){return t?"dažas sekundes":"dažām sekundēm"}e.defineLocale("lv",{months:"janvāris_februāris_marts_aprÄ«lis_maijs_jÅ«nijs_jÅ«lijs_augusts_septembris_oktobris_novembris_decembris".split("_"),monthsShort:"jan_feb_mar_apr_mai_jÅ«n_jÅ«l_aug_sep_okt_nov_dec".split("_"),weekdays:"svētdiena_pirmdiena_otrdiena_treÅ”diena_ceturtdiena_piektdiena_sestdiena".split("_"),weekdaysShort:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysMin:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY.",LL:"YYYY. [gada] D. MMMM",LLL:"YYYY. [gada] D. MMMM, HH:mm",LLLL:"YYYY. [gada] D. MMMM, dddd, HH:mm"},calendar:{sameDay:"[Å odien pulksten] LT",nextDay:"[RÄ«t pulksten] LT",nextWeek:"dddd [pulksten] LT",lastDay:"[Vakar pulksten] LT",lastWeek:"[PagājuŔā] dddd [pulksten] LT",sameElse:"L"},relativeTime:{future:"pēc %s",past:"pirms %s",s:i,ss:n,m:o,mm:n,h:o,hh:n,d:o,dd:n,M:o,MM:n,y:o,yy:n},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var a={ss:"sekundes_sekundēm_sekunde_sekundes".split("_"),m:"minÅ«tes_minÅ«tēm_minÅ«te_minÅ«tes".split("_"),mm:"minÅ«tes_minÅ«tēm_minÅ«te_minÅ«tes".split("_"),h:"stundas_stundām_stunda_stundas".split("_"),hh:"stundas_stundām_stunda_stundas".split("_"),d:"dienas_dienām_diena_dienas".split("_"),dd:"dienas_dienām_diena_dienas".split("_"),M:"mēneÅ”a_mēneÅ”iem_mēnesis_mēneÅ”i".split("_"),MM:"mēneÅ”a_mēneÅ”iem_mēnesis_mēneÅ”i".split("_"),y:"gada_gadiem_gads_gadi".split("_"),yy:"gada_gadiem_gads_gadi".split("_")},t;function r(e,t,n){if(n)return t%10===1&&t%100!==11?e[2]:e[3];else return t%10===1&&t%100!==11?e[0]:e[1]}function n(e,t,n){return e+" "+r(a[n],e,t)}function o(e,t,n){return r(a[n],e,t)}function i(e,t){return t?"dažas sekundes":"dažām sekundēm"}e.defineLocale("lv",{months:"janvāris_februāris_marts_aprÄ«lis_maijs_jÅ«nijs_jÅ«lijs_augusts_septembris_oktobris_novembris_decembris".split("_"),monthsShort:"jan_feb_mar_apr_mai_jÅ«n_jÅ«l_aug_sep_okt_nov_dec".split("_"),weekdays:"svētdiena_pirmdiena_otrdiena_treÅ”diena_ceturtdiena_piektdiena_sestdiena".split("_"),weekdaysShort:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysMin:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY.",LL:"YYYY. [gada] D. MMMM",LLL:"YYYY. [gada] D. MMMM, HH:mm",LLLL:"YYYY. [gada] D. MMMM, dddd, HH:mm"},calendar:{sameDay:"[Å odien pulksten] LT",nextDay:"[RÄ«t pulksten] LT",nextWeek:"dddd [pulksten] LT",lastDay:"[Vakar pulksten] LT",lastWeek:"[PagājuŔā] dddd [pulksten] LT",sameElse:"L"},relativeTime:{future:"pēc %s",past:"pirms %s",s:i,ss:n,m:o,mm:n,h:o,hh:n,d:o,dd:n,M:o,MM:n,y:o,yy:n},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var r={words:{ss:["sekund","sekunda","sekundi"],m:["jedan minut","jednog minuta"],mm:["minut","minuta","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mjesec","mjeseca","mjeseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(e,t){return e===1?t[0]:e>=2&&e<=4?t[1]:t[2]},translate:function(e,t,n){var a=r.words[n];if(n.length===1)return t?a[0]:a[1];else return e+" "+r.correctGrammaticalCase(e,a)}},t;e.defineLocale("me",{months:"januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.".split("_"),monthsParseExact:true,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sjutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){var e=["[proÅ”le] [nedjelje] [u] LT","[proÅ”log] [ponedjeljka] [u] LT","[proÅ”log] [utorka] [u] LT","[proÅ”le] [srijede] [u] LT","[proÅ”log] [četvrtka] [u] LT","[proÅ”log] [petka] [u] LT","[proÅ”le] [subote] [u] LT"];return e[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"nekoliko sekundi",ss:r.translate,m:r.translate,mm:r.translate,h:r.translate,hh:r.translate,d:"dan",dd:r.translate,M:"mjesec",MM:r.translate,y:"godinu",yy:r.translate},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var r={words:{ss:["sekund","sekunda","sekundi"],m:["jedan minut","jednog minuta"],mm:["minut","minuta","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mjesec","mjeseca","mjeseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(e,t){return e===1?t[0]:e>=2&&e<=4?t[1]:t[2]},translate:function(e,t,n){var a=r.words[n];if(n.length===1)return t?a[0]:a[1];else return e+" "+r.correctGrammaticalCase(e,a)}},t;e.defineLocale("me",{months:"januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.".split("_"),monthsParseExact:true,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sjutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){var e=["[proÅ”le] [nedjelje] [u] LT","[proÅ”log] [ponedjeljka] [u] LT","[proÅ”log] [utorka] [u] LT","[proÅ”le] [srijede] [u] LT","[proÅ”log] [četvrtka] [u] LT","[proÅ”log] [petka] [u] LT","[proÅ”le] [subote] [u] LT"];return e[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"nekoliko sekundi",ss:r.translate,m:r.translate,mm:r.translate,h:r.translate,hh:r.translate,d:"dan",dd:r.translate,M:"mjesec",MM:r.translate,y:"godinu",yy:r.translate},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("mi",{months:"Kohi-tāte_Hui-tanguru_PoutÅ«-te-rangi_Paenga-whāwhā_Haratua_Pipiri_Hōngoingoi_Here-turi-kōkā_Mahuru_Whiringa-ā-nuku_Whiringa-ā-rangi_Hakihea".split("_"),monthsShort:"Kohi_Hui_Pou_Pae_Hara_Pipi_Hōngoi_Here_Mahu_Whi-nu_Whi-ra_Haki".split("_"),monthsRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsStrictRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsShortRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsShortStrictRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,2}/i,weekdays:"Rātapu_Mane_TÅ«rei_Wenerei_Tāite_Paraire_Hātarei".split("_"),weekdaysShort:"Ta_Ma_TÅ«_We_Tāi_Pa_Hā".split("_"),weekdaysMin:"Ta_Ma_TÅ«_We_Tāi_Pa_Hā".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [i] HH:mm",LLLL:"dddd, D MMMM YYYY [i] HH:mm"},calendar:{sameDay:"[i teie mahana, i] LT",nextDay:"[apopo i] LT",nextWeek:"dddd [i] LT",lastDay:"[inanahi i] LT",lastWeek:"dddd [whakamutunga i] LT",sameElse:"L"},relativeTime:{future:"i roto i %s",past:"%s i mua",s:"te hēkona ruarua",ss:"%d hēkona",m:"he meneti",mm:"%d meneti",h:"te haora",hh:"%d haora",d:"he ra",dd:"%d ra",M:"he marama",MM:"%d marama",y:"he tau",yy:"%d tau"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("mi",{months:"Kohi-tāte_Hui-tanguru_PoutÅ«-te-rangi_Paenga-whāwhā_Haratua_Pipiri_Hōngoingoi_Here-turi-kōkā_Mahuru_Whiringa-ā-nuku_Whiringa-ā-rangi_Hakihea".split("_"),monthsShort:"Kohi_Hui_Pou_Pae_Hara_Pipi_Hōngoi_Here_Mahu_Whi-nu_Whi-ra_Haki".split("_"),monthsRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsStrictRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsShortRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsShortStrictRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,2}/i,weekdays:"Rātapu_Mane_TÅ«rei_Wenerei_Tāite_Paraire_Hātarei".split("_"),weekdaysShort:"Ta_Ma_TÅ«_We_Tāi_Pa_Hā".split("_"),weekdaysMin:"Ta_Ma_TÅ«_We_Tāi_Pa_Hā".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [i] HH:mm",LLLL:"dddd, D MMMM YYYY [i] HH:mm"},calendar:{sameDay:"[i teie mahana, i] LT",nextDay:"[apopo i] LT",nextWeek:"dddd [i] LT",lastDay:"[inanahi i] LT",lastWeek:"dddd [whakamutunga i] LT",sameElse:"L"},relativeTime:{future:"i roto i %s",past:"%s i mua",s:"te hēkona ruarua",ss:"%d hēkona",m:"he meneti",mm:"%d meneti",h:"te haora",hh:"%d haora",d:"he ra",dd:"%d ra",M:"he marama",MM:"%d marama",y:"he tau",yy:"%d tau"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("mk",{months:"јŠ°Š½ŃƒŠ°Ń€Šø_фŠµŠ²Ń€ŃƒŠ°Ń€Šø_Š¼Š°Ń€Ń‚_Š°ŠæрŠøŠ»_Š¼Š°Ń˜_јуŠ½Šø_јуŠ»Šø_Š°Š²Š³ŃƒŃŃ‚_сŠµŠæтŠµŠ¼Š²Ń€Šø_Š¾ŠŗтŠ¾Š¼Š²Ń€Šø_Š½Š¾ŠµŠ¼Š²Ń€Šø_Š“ŠµŠŗŠµŠ¼Š²Ń€Šø".split("_"),monthsShort:"јŠ°Š½_фŠµŠ²_Š¼Š°Ń€_Š°Šæр_Š¼Š°Ń˜_јуŠ½_јуŠ»_Š°Š²Š³_сŠµŠæ_Š¾Šŗт_Š½Š¾Šµ_Š“ŠµŠŗ".split("_"),weekdays:"Š½ŠµŠ“ŠµŠ»Š°_ŠæŠ¾Š½ŠµŠ“ŠµŠ»Š½ŠøŠŗ_Š²Ń‚Š¾Ń€Š½ŠøŠŗ_срŠµŠ“Š°_чŠµŃ‚Š²Ń€Ń‚Š¾Šŗ_ŠæŠµŃ‚Š¾Šŗ_сŠ°Š±Š¾Ń‚Š°".split("_"),weekdaysShort:"Š½ŠµŠ“_ŠæŠ¾Š½_Š²Ń‚Š¾_срŠµ_чŠµŃ‚_ŠæŠµŃ‚_сŠ°Š±".split("_"),weekdaysMin:"Š½e_Šæo_Š²Ń‚_ср_чŠµ_ŠæŠµ_сa".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[Š”ŠµŠ½ŠµŃ Š²Š¾] LT",nextDay:"[Š£Ń‚Ń€Šµ Š²Š¾] LT",nextWeek:"[Š’Š¾] dddd [Š²Š¾] LT",lastDay:"[Š’чŠµŃ€Š° Š²Š¾] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[Š˜Š·Š¼ŠøŠ½Š°Ń‚Š°Ń‚Š°] dddd [Š²Š¾] LT";case 1:case 2:case 4:case 5:return"[Š˜Š·Š¼ŠøŠ½Š°Ń‚ŠøŠ¾Ń‚] dddd [Š²Š¾] LT"}},sameElse:"L"},relativeTime:{future:"Š·Š° %s",past:"ŠæрŠµŠ“ %s",s:"Š½ŠµŠŗŠ¾Š»Šŗу сŠµŠŗуŠ½Š“Šø",ss:"%d сŠµŠŗуŠ½Š“Šø",m:"ŠµŠ“Š½Š° Š¼ŠøŠ½ŃƒŃ‚Š°",mm:"%d Š¼ŠøŠ½ŃƒŃ‚Šø",h:"ŠµŠ“ŠµŠ½ чŠ°Ń",hh:"%d чŠ°ŃŠ°",d:"ŠµŠ“ŠµŠ½ Š“ŠµŠ½",dd:"%d Š“ŠµŠ½Š°",M:"ŠµŠ“ŠµŠ½ Š¼ŠµŃŠµŃ†",MM:"%d Š¼ŠµŃŠµŃ†Šø",y:"ŠµŠ“Š½Š° Š³Š¾Š“ŠøŠ½Š°",yy:"%d Š³Š¾Š“ŠøŠ½Šø"},dayOfMonthOrdinalParse:/\d{1,2}-(ŠµŠ²|ŠµŠ½|тŠø|Š²Šø|рŠø|Š¼Šø)/,ordinal:function(e){var t=e%10,n=e%100;if(e===0)return e+"-ŠµŠ²";else if(n===0)return e+"-ŠµŠ½";else if(n>10&&n<20)return e+"-тŠø";else if(t===1)return e+"-Š²Šø";else if(t===2)return e+"-рŠø";else if(t===7||t===8)return e+"-Š¼Šø";else return e+"-тŠø"},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("mk",{months:"јŠ°Š½ŃƒŠ°Ń€Šø_фŠµŠ²Ń€ŃƒŠ°Ń€Šø_Š¼Š°Ń€Ń‚_Š°ŠæрŠøŠ»_Š¼Š°Ń˜_јуŠ½Šø_јуŠ»Šø_Š°Š²Š³ŃƒŃŃ‚_сŠµŠæтŠµŠ¼Š²Ń€Šø_Š¾ŠŗтŠ¾Š¼Š²Ń€Šø_Š½Š¾ŠµŠ¼Š²Ń€Šø_Š“ŠµŠŗŠµŠ¼Š²Ń€Šø".split("_"),monthsShort:"јŠ°Š½_фŠµŠ²_Š¼Š°Ń€_Š°Šæр_Š¼Š°Ń˜_јуŠ½_јуŠ»_Š°Š²Š³_сŠµŠæ_Š¾Šŗт_Š½Š¾Šµ_Š“ŠµŠŗ".split("_"),weekdays:"Š½ŠµŠ“ŠµŠ»Š°_ŠæŠ¾Š½ŠµŠ“ŠµŠ»Š½ŠøŠŗ_Š²Ń‚Š¾Ń€Š½ŠøŠŗ_срŠµŠ“Š°_чŠµŃ‚Š²Ń€Ń‚Š¾Šŗ_ŠæŠµŃ‚Š¾Šŗ_сŠ°Š±Š¾Ń‚Š°".split("_"),weekdaysShort:"Š½ŠµŠ“_ŠæŠ¾Š½_Š²Ń‚Š¾_срŠµ_чŠµŃ‚_ŠæŠµŃ‚_сŠ°Š±".split("_"),weekdaysMin:"Š½e_Šæo_Š²Ń‚_ср_чŠµ_ŠæŠµ_сa".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[Š”ŠµŠ½ŠµŃ Š²Š¾] LT",nextDay:"[Š£Ń‚Ń€Šµ Š²Š¾] LT",nextWeek:"[Š’Š¾] dddd [Š²Š¾] LT",lastDay:"[Š’чŠµŃ€Š° Š²Š¾] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[Š˜Š·Š¼ŠøŠ½Š°Ń‚Š°Ń‚Š°] dddd [Š²Š¾] LT";case 1:case 2:case 4:case 5:return"[Š˜Š·Š¼ŠøŠ½Š°Ń‚ŠøŠ¾Ń‚] dddd [Š²Š¾] LT"}},sameElse:"L"},relativeTime:{future:"Š·Š° %s",past:"ŠæрŠµŠ“ %s",s:"Š½ŠµŠŗŠ¾Š»Šŗу сŠµŠŗуŠ½Š“Šø",ss:"%d сŠµŠŗуŠ½Š“Šø",m:"ŠµŠ“Š½Š° Š¼ŠøŠ½ŃƒŃ‚Š°",mm:"%d Š¼ŠøŠ½ŃƒŃ‚Šø",h:"ŠµŠ“ŠµŠ½ чŠ°Ń",hh:"%d чŠ°ŃŠ°",d:"ŠµŠ“ŠµŠ½ Š“ŠµŠ½",dd:"%d Š“ŠµŠ½Š°",M:"ŠµŠ“ŠµŠ½ Š¼ŠµŃŠµŃ†",MM:"%d Š¼ŠµŃŠµŃ†Šø",y:"ŠµŠ“Š½Š° Š³Š¾Š“ŠøŠ½Š°",yy:"%d Š³Š¾Š“ŠøŠ½Šø"},dayOfMonthOrdinalParse:/\d{1,2}-(ŠµŠ²|ŠµŠ½|тŠø|Š²Šø|рŠø|Š¼Šø)/,ordinal:function(e){var t=e%10,n=e%100;if(e===0)return e+"-ŠµŠ²";else if(n===0)return e+"-ŠµŠ½";else if(n>10&&n<20)return e+"-тŠø";else if(t===1)return e+"-Š²Šø";else if(t===2)return e+"-рŠø";else if(t===7||t===8)return e+"-Š¼Šø";else return e+"-тŠø"},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("ml",{months:"ą“œą“Øąµą“µą“°ą“æ_ą“«ąµ†ą“¬ąµą“°ąµą“µą“°ą“æ_ą“®ą“¾ąµ¼ą“šąµą“šąµ_ą“ą“Ŗąµą“°ą“æąµ½_ą“®ąµ‡ą“Æąµ_ą“œąµ‚ąµŗ_ą“œąµ‚ą“²ąµˆ_ą““ą“—ą“øąµą“±ąµą“±ąµ_ą“øąµ†ą“Ŗąµą“±ąµą“±ą“‚ą“¬ąµ¼_ą“’ą“•ąµą“Ÿąµ‹ą“¬ąµ¼_ą“Øą“µą“‚ą“¬ąµ¼_ą“”ą“æą“øą“‚ą“¬ąµ¼".split("_"),monthsShort:"ą“œą“Øąµ._ą“«ąµ†ą“¬ąµą“°ąµ._ą“®ą“¾ąµ¼._ą“ą“Ŗąµą“°ą“æ._ą“®ąµ‡ą“Æąµ_ą“œąµ‚ąµŗ_ą“œąµ‚ą“²ąµˆ._ą““ą“—._ą“øąµ†ą“Ŗąµą“±ąµą“±._ą“’ą“•ąµą“Ÿąµ‹._ą“Øą“µą“‚._ą“”ą“æą“øą“‚.".split("_"),monthsParseExact:true,weekdays:"ą“žą“¾ą“Æą“±ą“¾ą““ąµą“š_ą“¤ą“æą“™ąµą“•ą“³ą“¾ą““ąµą“š_ą“šąµŠą“µąµą“µą“¾ą““ąµą“š_ą“¬ąµą“§ą“Øą“¾ą““ąµą“š_ą“µąµą“Æą“¾ą““ą“¾ą““ąµą“š_ą“µąµ†ą“³ąµą“³ą“æą“Æą“¾ą““ąµą“š_ą“¶ą“Øą“æą“Æą“¾ą““ąµą“š".split("_"),weekdaysShort:"ą“žą“¾ą“Æąµ¼_ą“¤ą“æą“™ąµą“•ąµ¾_ą“šąµŠą“µąµą“µ_ą“¬ąµą“§ąµ»_ą“µąµą“Æą“¾ą““ą“‚_ą“µąµ†ą“³ąµą“³ą“æ_ą“¶ą“Øą“æ".split("_"),weekdaysMin:"ą“žą“¾_ą“¤ą“æ_ą“šąµŠ_ą“¬ąµ_ą“µąµą“Æą“¾_ą“µąµ†_ą“¶".split("_"),longDateFormat:{LT:"A h:mm -ą“Øąµ",LTS:"A h:mm:ss -ą“Øąµ",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm -ą“Øąµ",LLLL:"dddd, D MMMM YYYY, A h:mm -ą“Øąµ"},calendar:{sameDay:"[ą“‡ą“Øąµą“Øąµ] LT",nextDay:"[ą“Øą“¾ą“³ąµ†] LT",nextWeek:"dddd, LT",lastDay:"[ą“‡ą“Øąµą“Øą“²ąµ†] LT",lastWeek:"[ą“•ą““ą“æą“žąµą“ž] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą“•ą““ą“æą“žąµą“žąµ",past:"%s ą“®ąµąµ»ą“Ŗąµ",s:"ą“…ąµ½ą“Ŗ ą“Øą“æą“®ą“æą“·ą“™ąµą“™ąµ¾",ss:"%d ą“øąµ†ą“•ąµą“•ąµ»ą“”ąµ",m:"ą“’ą“°ąµ ą“®ą“æą“Øą“æą“±ąµą“±ąµ",mm:"%d ą“®ą“æą“Øą“æą“±ąµą“±ąµ",h:"ą“’ą“°ąµ ą“®ą“£ą“æą“•ąµą“•ąµ‚ąµ¼",hh:"%d ą“®ą“£ą“æą“•ąµą“•ąµ‚ąµ¼",d:"ą“’ą“°ąµ ą“¦ą“æą“µą“øą“‚",dd:"%d ą“¦ą“æą“µą“øą“‚",M:"ą“’ą“°ąµ ą“®ą“¾ą“øą“‚",MM:"%d ą“®ą“¾ą“øą“‚",y:"ą“’ą“°ąµ ą“µąµ¼ą“·ą“‚",yy:"%d ą“µąµ¼ą“·ą“‚"},meridiemParse:/ą“°ą“¾ą“¤ąµą“°ą“æ|ą“°ą“¾ą“µą“æą“²ąµ†|ą“‰ą“šąµą“š ą“•ą““ą“æą“žąµą“žąµ|ą“µąµˆą“•ąµą“Øąµą“Øąµ‡ą“°ą“‚|ą“°ą“¾ą“¤ąµą“°ą“æ/i,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą“°ą“¾ą“¤ąµą“°ą“æ"&&e>=4||t==="ą“‰ą“šąµą“š ą“•ą““ą“æą“žąµą“žąµ"||t==="ą“µąµˆą“•ąµą“Øąµą“Øąµ‡ą“°ą“‚")return e+12;else return e},meridiem:function(e,t,n){if(e<4)return"ą“°ą“¾ą“¤ąµą“°ą“æ";else if(e<12)return"ą“°ą“¾ą“µą“æą“²ąµ†";else if(e<17)return"ą“‰ą“šąµą“š ą“•ą““ą“æą“žąµą“žąµ";else if(e<20)return"ą“µąµˆą“•ąµą“Øąµą“Øąµ‡ą“°ą“‚";else return"ą“°ą“¾ą“¤ąµą“°ą“æ"}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("ml",{months:"ą“œą“Øąµą“µą“°ą“æ_ą“«ąµ†ą“¬ąµą“°ąµą“µą“°ą“æ_ą“®ą“¾ąµ¼ą“šąµą“šąµ_ą“ą“Ŗąµą“°ą“æąµ½_ą“®ąµ‡ą“Æąµ_ą“œąµ‚ąµŗ_ą“œąµ‚ą“²ąµˆ_ą““ą“—ą“øąµą“±ąµą“±ąµ_ą“øąµ†ą“Ŗąµą“±ąµą“±ą“‚ą“¬ąµ¼_ą“’ą“•ąµą“Ÿąµ‹ą“¬ąµ¼_ą“Øą“µą“‚ą“¬ąµ¼_ą“”ą“æą“øą“‚ą“¬ąµ¼".split("_"),monthsShort:"ą“œą“Øąµ._ą“«ąµ†ą“¬ąµą“°ąµ._ą“®ą“¾ąµ¼._ą“ą“Ŗąµą“°ą“æ._ą“®ąµ‡ą“Æąµ_ą“œąµ‚ąµŗ_ą“œąµ‚ą“²ąµˆ._ą““ą“—._ą“øąµ†ą“Ŗąµą“±ąµą“±._ą“’ą“•ąµą“Ÿąµ‹._ą“Øą“µą“‚._ą“”ą“æą“øą“‚.".split("_"),monthsParseExact:true,weekdays:"ą“žą“¾ą“Æą“±ą“¾ą““ąµą“š_ą“¤ą“æą“™ąµą“•ą“³ą“¾ą““ąµą“š_ą“šąµŠą“µąµą“µą“¾ą““ąµą“š_ą“¬ąµą“§ą“Øą“¾ą““ąµą“š_ą“µąµą“Æą“¾ą““ą“¾ą““ąµą“š_ą“µąµ†ą“³ąµą“³ą“æą“Æą“¾ą““ąµą“š_ą“¶ą“Øą“æą“Æą“¾ą““ąµą“š".split("_"),weekdaysShort:"ą“žą“¾ą“Æąµ¼_ą“¤ą“æą“™ąµą“•ąµ¾_ą“šąµŠą“µąµą“µ_ą“¬ąµą“§ąµ»_ą“µąµą“Æą“¾ą““ą“‚_ą“µąµ†ą“³ąµą“³ą“æ_ą“¶ą“Øą“æ".split("_"),weekdaysMin:"ą“žą“¾_ą“¤ą“æ_ą“šąµŠ_ą“¬ąµ_ą“µąµą“Æą“¾_ą“µąµ†_ą“¶".split("_"),longDateFormat:{LT:"A h:mm -ą“Øąµ",LTS:"A h:mm:ss -ą“Øąµ",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm -ą“Øąµ",LLLL:"dddd, D MMMM YYYY, A h:mm -ą“Øąµ"},calendar:{sameDay:"[ą“‡ą“Øąµą“Øąµ] LT",nextDay:"[ą“Øą“¾ą“³ąµ†] LT",nextWeek:"dddd, LT",lastDay:"[ą“‡ą“Øąµą“Øą“²ąµ†] LT",lastWeek:"[ą“•ą““ą“æą“žąµą“ž] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą“•ą““ą“æą“žąµą“žąµ",past:"%s ą“®ąµąµ»ą“Ŗąµ",s:"ą“…ąµ½ą“Ŗ ą“Øą“æą“®ą“æą“·ą“™ąµą“™ąµ¾",ss:"%d ą“øąµ†ą“•ąµą“•ąµ»ą“”ąµ",m:"ą“’ą“°ąµ ą“®ą“æą“Øą“æą“±ąµą“±ąµ",mm:"%d ą“®ą“æą“Øą“æą“±ąµą“±ąµ",h:"ą“’ą“°ąµ ą“®ą“£ą“æą“•ąµą“•ąµ‚ąµ¼",hh:"%d ą“®ą“£ą“æą“•ąµą“•ąµ‚ąµ¼",d:"ą“’ą“°ąµ ą“¦ą“æą“µą“øą“‚",dd:"%d ą“¦ą“æą“µą“øą“‚",M:"ą“’ą“°ąµ ą“®ą“¾ą“øą“‚",MM:"%d ą“®ą“¾ą“øą“‚",y:"ą“’ą“°ąµ ą“µąµ¼ą“·ą“‚",yy:"%d ą“µąµ¼ą“·ą“‚"},meridiemParse:/ą“°ą“¾ą“¤ąµą“°ą“æ|ą“°ą“¾ą“µą“æą“²ąµ†|ą“‰ą“šąµą“š ą“•ą““ą“æą“žąµą“žąµ|ą“µąµˆą“•ąµą“Øąµą“Øąµ‡ą“°ą“‚|ą“°ą“¾ą“¤ąµą“°ą“æ/i,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą“°ą“¾ą“¤ąµą“°ą“æ"&&e>=4||t==="ą“‰ą“šąµą“š ą“•ą““ą“æą“žąµą“žąµ"||t==="ą“µąµˆą“•ąµą“Øąµą“Øąµ‡ą“°ą“‚")return e+12;else return e},meridiem:function(e,t,n){if(e<4)return"ą“°ą“¾ą“¤ąµą“°ą“æ";else if(e<12)return"ą“°ą“¾ą“µą“æą“²ąµ†";else if(e<17)return"ą“‰ą“šąµą“š ą“•ą““ą“æą“žąµą“žąµ";else if(e<20)return"ą“µąµˆą“•ąµą“Øąµą“Øąµ‡ą“°ą“‚";else return"ą“°ą“¾ą“¤ąµą“°ą“æ"}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function t(e,t,n,a){switch(n){case"s":return t?"хэŠ“хэŠ½ сŠµŠŗуŠ½Š“":"хэŠ“хэŠ½ сŠµŠŗуŠ½Š“ыŠ½";case"ss":return e+(t?" сŠµŠŗуŠ½Š“":" сŠµŠŗуŠ½Š“ыŠ½");case"m":case"mm":return e+(t?" Š¼ŠøŠ½ŃƒŃ‚":" Š¼ŠøŠ½ŃƒŃ‚Ń‹Š½");case"h":case"hh":return e+(t?" цŠ°Š³":" цŠ°Š³ŠøŠ¹Š½");case"d":case"dd":return e+(t?" Ó©Š“Ó©Ń€":" Ó©Š“рŠøŠ¹Š½");case"M":case"MM":return e+(t?" сŠ°Ń€":" сŠ°Ń€Ń‹Š½");case"y":case"yy":return e+(t?" Š¶ŠøŠ»":" Š¶ŠøŠ»ŠøŠ¹Š½");default:return e}}var n;e.defineLocale("mn",{months:"ŠŃŠ³Š“ŅÆŠ³ŃŃŃ€ сŠ°Ń€_Š„Š¾Ń‘Ń€Š“уŠ³Š°Š°Ń€ сŠ°Ń€_Š“ŃƒŃ€Š°Š²Š“уŠ³Š°Š°Ń€ сŠ°Ń€_Š”Ó©Ń€Ó©Š²Š“ŅÆŠ³ŃŃŃ€ сŠ°Ń€_Š¢Š°Š²Š“уŠ³Š°Š°Ń€ сŠ°Ń€_Š—ŃƒŃ€Š³Š°Š“уŠ³Š°Š°Ń€ сŠ°Ń€_Š”Š¾Š»Š“уŠ³Š°Š°Ń€ сŠ°Ń€_ŠŠ°Š¹Š¼Š“уŠ³Š°Š°Ń€ сŠ°Ń€_Š•ŃŠ“ŅÆŠ³ŃŃŃ€ сŠ°Ń€_ŠŃ€Š°Š²Š“уŠ³Š°Š°Ń€ сŠ°Ń€_ŠŃ€Š²Š°Š½ Š½ŃŠ³Š“ŅÆŠ³ŃŃŃ€ сŠ°Ń€_ŠŃ€Š²Š°Š½ хŠ¾Ń‘Ń€Š“уŠ³Š°Š°Ń€ сŠ°Ń€".split("_"),monthsShort:"1 сŠ°Ń€_2 сŠ°Ń€_3 сŠ°Ń€_4 сŠ°Ń€_5 сŠ°Ń€_6 сŠ°Ń€_7 сŠ°Ń€_8 сŠ°Ń€_9 сŠ°Ń€_10 сŠ°Ń€_11 сŠ°Ń€_12 сŠ°Ń€".split("_"),monthsParseExact:true,weekdays:"ŠŃŠ¼_Š”Š°Š²Š°Š°_ŠœŃŠ³Š¼Š°Ń€_Š›Ń…Š°Š³Š²Š°_ŠŸŅÆрэŠ²_Š‘Š°Š°ŃŠ°Š½_Š‘яŠ¼Š±Š°".split("_"),weekdaysShort:"ŠŃŠ¼_Š”Š°Š²_ŠœŃŠ³_Š›Ń…Š°_ŠŸŅÆр_Š‘Š°Š°_Š‘яŠ¼".split("_"),weekdaysMin:"ŠŃ_Š”Š°_ŠœŃ_Š›Ń…_ŠŸŅÆ_Š‘Š°_Š‘я".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY Š¾Š½Ń‹ MMMMыŠ½ D",LLL:"YYYY Š¾Š½Ń‹ MMMMыŠ½ D HH:mm",LLLL:"dddd, YYYY Š¾Š½Ń‹ MMMMыŠ½ D HH:mm"},meridiemParse:/Ņ®ÓØ|Ņ®Š„/i,isPM:function(e){return e==="Ņ®Š„"},meridiem:function(e,t,n){if(e<12)return"Ņ®ÓØ";else return"Ņ®Š„"},calendar:{sameDay:"[ÓØŠ½Ó©Ó©Š“Ó©Ń€] LT",nextDay:"[ŠœŠ°Ń€Š³Š°Š°Ńˆ] LT",nextWeek:"[Š˜Ń€ŃŃ…] dddd LT",lastDay:"[ÓØчŠøŠ³Š“Ó©Ń€] LT",lastWeek:"[ÓØŠ½Š³Ó©Ń€ŃÓ©Š½] dddd LT",sameElse:"L"},relativeTime:{future:"%s Š“Š°Ń€Š°Š°",past:"%s Ó©Š¼Š½Ó©",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2} Ó©Š“Ó©Ń€/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+" Ó©Š“Ó©Ń€";default:return e}}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function t(e,t,n,a){switch(n){case"s":return t?"хэŠ“хэŠ½ сŠµŠŗуŠ½Š“":"хэŠ“хэŠ½ сŠµŠŗуŠ½Š“ыŠ½";case"ss":return e+(t?" сŠµŠŗуŠ½Š“":" сŠµŠŗуŠ½Š“ыŠ½");case"m":case"mm":return e+(t?" Š¼ŠøŠ½ŃƒŃ‚":" Š¼ŠøŠ½ŃƒŃ‚Ń‹Š½");case"h":case"hh":return e+(t?" цŠ°Š³":" цŠ°Š³ŠøŠ¹Š½");case"d":case"dd":return e+(t?" Ó©Š“Ó©Ń€":" Ó©Š“рŠøŠ¹Š½");case"M":case"MM":return e+(t?" сŠ°Ń€":" сŠ°Ń€Ń‹Š½");case"y":case"yy":return e+(t?" Š¶ŠøŠ»":" Š¶ŠøŠ»ŠøŠ¹Š½");default:return e}}var n;e.defineLocale("mn",{months:"ŠŃŠ³Š“ŅÆŠ³ŃŃŃ€ сŠ°Ń€_Š„Š¾Ń‘Ń€Š“уŠ³Š°Š°Ń€ сŠ°Ń€_Š“ŃƒŃ€Š°Š²Š“уŠ³Š°Š°Ń€ сŠ°Ń€_Š”Ó©Ń€Ó©Š²Š“ŅÆŠ³ŃŃŃ€ сŠ°Ń€_Š¢Š°Š²Š“уŠ³Š°Š°Ń€ сŠ°Ń€_Š—ŃƒŃ€Š³Š°Š“уŠ³Š°Š°Ń€ сŠ°Ń€_Š”Š¾Š»Š“уŠ³Š°Š°Ń€ сŠ°Ń€_ŠŠ°Š¹Š¼Š“уŠ³Š°Š°Ń€ сŠ°Ń€_Š•ŃŠ“ŅÆŠ³ŃŃŃ€ сŠ°Ń€_ŠŃ€Š°Š²Š“уŠ³Š°Š°Ń€ сŠ°Ń€_ŠŃ€Š²Š°Š½ Š½ŃŠ³Š“ŅÆŠ³ŃŃŃ€ сŠ°Ń€_ŠŃ€Š²Š°Š½ хŠ¾Ń‘Ń€Š“уŠ³Š°Š°Ń€ сŠ°Ń€".split("_"),monthsShort:"1 сŠ°Ń€_2 сŠ°Ń€_3 сŠ°Ń€_4 сŠ°Ń€_5 сŠ°Ń€_6 сŠ°Ń€_7 сŠ°Ń€_8 сŠ°Ń€_9 сŠ°Ń€_10 сŠ°Ń€_11 сŠ°Ń€_12 сŠ°Ń€".split("_"),monthsParseExact:true,weekdays:"ŠŃŠ¼_Š”Š°Š²Š°Š°_ŠœŃŠ³Š¼Š°Ń€_Š›Ń…Š°Š³Š²Š°_ŠŸŅÆрэŠ²_Š‘Š°Š°ŃŠ°Š½_Š‘яŠ¼Š±Š°".split("_"),weekdaysShort:"ŠŃŠ¼_Š”Š°Š²_ŠœŃŠ³_Š›Ń…Š°_ŠŸŅÆр_Š‘Š°Š°_Š‘яŠ¼".split("_"),weekdaysMin:"ŠŃ_Š”Š°_ŠœŃ_Š›Ń…_ŠŸŅÆ_Š‘Š°_Š‘я".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY Š¾Š½Ń‹ MMMMыŠ½ D",LLL:"YYYY Š¾Š½Ń‹ MMMMыŠ½ D HH:mm",LLLL:"dddd, YYYY Š¾Š½Ń‹ MMMMыŠ½ D HH:mm"},meridiemParse:/Ņ®ÓØ|Ņ®Š„/i,isPM:function(e){return e==="Ņ®Š„"},meridiem:function(e,t,n){if(e<12)return"Ņ®ÓØ";else return"Ņ®Š„"},calendar:{sameDay:"[ÓØŠ½Ó©Ó©Š“Ó©Ń€] LT",nextDay:"[ŠœŠ°Ń€Š³Š°Š°Ńˆ] LT",nextWeek:"[Š˜Ń€ŃŃ…] dddd LT",lastDay:"[ÓØчŠøŠ³Š“Ó©Ń€] LT",lastWeek:"[ÓØŠ½Š³Ó©Ń€ŃÓ©Š½] dddd LT",sameElse:"L"},relativeTime:{future:"%s Š“Š°Ń€Š°Š°",past:"%s Ó©Š¼Š½Ó©",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2} Ó©Š“Ó©Ń€/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+" Ó©Š“Ó©Ń€";default:return e}}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"ą„§",2:"ą„Ø",3:"ą„©",4:"ą„Ŗ",5:"ą„«",6:"ą„¬",7:"ą„­",8:"ą„®",9:"ą„Æ",0:"ą„¦"},n={"ą„§":"1","ą„Ø":"2","ą„©":"3","ą„Ŗ":"4","ą„«":"5","ą„¬":"6","ą„­":"7","ą„®":"8","ą„Æ":"9","ą„¦":"0"},a;function r(e,t,n,a){var r="";if(t)switch(n){case"s":r="ą¤•ą¤¾ą¤¹ą„€ ą¤øą„‡ą¤•ą¤‚ą¤¦";break;case"ss":r="%d ą¤øą„‡ą¤•ą¤‚ą¤¦";break;case"m":r="ą¤ą¤• ą¤®ą¤æą¤Øą¤æą¤Ÿ";break;case"mm":r="%d ą¤®ą¤æą¤Øą¤æą¤Ÿą„‡";break;case"h":r="ą¤ą¤• ą¤¤ą¤¾ą¤ø";break;case"hh":r="%d ą¤¤ą¤¾ą¤ø";break;case"d":r="ą¤ą¤• ą¤¦ą¤æą¤µą¤ø";break;case"dd":r="%d ą¤¦ą¤æą¤µą¤ø";break;case"M":r="ą¤ą¤• ą¤®ą¤¹ą¤æą¤Øą¤¾";break;case"MM":r="%d ą¤®ą¤¹ą¤æą¤Øą„‡";break;case"y":r="ą¤ą¤• ą¤µą¤°ą„ą¤·";break;case"yy":r="%d ą¤µą¤°ą„ą¤·ą„‡";break}else switch(n){case"s":r="ą¤•ą¤¾ą¤¹ą„€ ą¤øą„‡ą¤•ą¤‚ą¤¦ą¤¾ą¤‚";break;case"ss":r="%d ą¤øą„‡ą¤•ą¤‚ą¤¦ą¤¾ą¤‚";break;case"m":r="ą¤ą¤•ą¤¾ ą¤®ą¤æą¤Øą¤æą¤Ÿą¤¾";break;case"mm":r="%d ą¤®ą¤æą¤Øą¤æą¤Ÿą¤¾ą¤‚";break;case"h":r="ą¤ą¤•ą¤¾ ą¤¤ą¤¾ą¤øą¤¾";break;case"hh":r="%d ą¤¤ą¤¾ą¤øą¤¾ą¤‚";break;case"d":r="ą¤ą¤•ą¤¾ ą¤¦ą¤æą¤µą¤øą¤¾";break;case"dd":r="%d ą¤¦ą¤æą¤µą¤øą¤¾ą¤‚";break;case"M":r="ą¤ą¤•ą¤¾ ą¤®ą¤¹ą¤æą¤Øą„ą¤Æą¤¾";break;case"MM":r="%d ą¤®ą¤¹ą¤æą¤Øą„ą¤Æą¤¾ą¤‚";break;case"y":r="ą¤ą¤•ą¤¾ ą¤µą¤°ą„ą¤·ą¤¾";break;case"yy":r="%d ą¤µą¤°ą„ą¤·ą¤¾ą¤‚";break}return r.replace(/%d/i,e)}e.defineLocale("mr",{months:"ą¤œą¤¾ą¤Øą„‡ą¤µą¤¾ą¤°ą„€_ą¤«ą„‡ą¤¬ą„ą¤°ą„ą¤µą¤¾ą¤°ą„€_ą¤®ą¤¾ą¤°ą„ą¤š_ą¤ą¤Ŗą„ą¤°ą¤æą¤²_ą¤®ą„‡_ą¤œą„‚ą¤Ø_ą¤œą„ą¤²ą„ˆ_ą¤‘ą¤—ą¤øą„ą¤Ÿ_ą¤øą¤Ŗą„ą¤Ÿą„‡ą¤‚ą¤¬ą¤°_ą¤‘ą¤•ą„ą¤Ÿą„‹ą¤¬ą¤°_ą¤Øą„‹ą¤µą„ą¤¹ą„‡ą¤‚ą¤¬ą¤°_ą¤”ą¤æą¤øą„‡ą¤‚ą¤¬ą¤°".split("_"),monthsShort:"ą¤œą¤¾ą¤Øą„‡._ą¤«ą„‡ą¤¬ą„ą¤°ą„._ą¤®ą¤¾ą¤°ą„ą¤š._ą¤ą¤Ŗą„ą¤°ą¤æ._ą¤®ą„‡._ą¤œą„‚ą¤Ø._ą¤œą„ą¤²ą„ˆ._ą¤‘ą¤—._ą¤øą¤Ŗą„ą¤Ÿą„‡ą¤‚._ą¤‘ą¤•ą„ą¤Ÿą„‹._ą¤Øą„‹ą¤µą„ą¤¹ą„‡ą¤‚._ą¤”ą¤æą¤øą„‡ą¤‚.".split("_"),monthsParseExact:true,weekdays:"ą¤°ą¤µą¤æą¤µą¤¾ą¤°_ą¤øą„‹ą¤®ą¤µą¤¾ą¤°_ą¤®ą¤‚ą¤—ą¤³ą¤µą¤¾ą¤°_ą¤¬ą„ą¤§ą¤µą¤¾ą¤°_ą¤—ą„ą¤°ą„‚ą¤µą¤¾ą¤°_ą¤¶ą„ą¤•ą„ą¤°ą¤µą¤¾ą¤°_ą¤¶ą¤Øą¤æą¤µą¤¾ą¤°".split("_"),weekdaysShort:"ą¤°ą¤µą¤æ_ą¤øą„‹ą¤®_ą¤®ą¤‚ą¤—ą¤³_ą¤¬ą„ą¤§_ą¤—ą„ą¤°ą„‚_ą¤¶ą„ą¤•ą„ą¤°_ą¤¶ą¤Øą¤æ".split("_"),weekdaysMin:"ą¤°_ą¤øą„‹_ą¤®ą¤‚_ą¤¬ą„_ą¤—ą„_ą¤¶ą„_ą¤¶".split("_"),longDateFormat:{LT:"A h:mm ą¤µą¤¾ą¤œą¤¤ą¤¾",LTS:"A h:mm:ss ą¤µą¤¾ą¤œą¤¤ą¤¾",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ą¤µą¤¾ą¤œą¤¤ą¤¾",LLLL:"dddd, D MMMM YYYY, A h:mm ą¤µą¤¾ą¤œą¤¤ą¤¾"},calendar:{sameDay:"[ą¤†ą¤œ] LT",nextDay:"[ą¤‰ą¤¦ą„ą¤Æą¤¾] LT",nextWeek:"dddd, LT",lastDay:"[ą¤•ą¤¾ą¤²] LT",lastWeek:"[ą¤®ą¤¾ą¤—ą„€ą¤²] dddd, LT",sameElse:"L"},relativeTime:{future:"%są¤®ą¤§ą„ą¤Æą„‡",past:"%są¤Ŗą„‚ą¤°ą„ą¤µą„€",s:r,ss:r,m:r,mm:r,h:r,hh:r,d:r,dd:r,M:r,MM:r,y:r,yy:r},preparse:function(e){return e.replace(/[ą„§ą„Øą„©ą„Ŗą„«ą„¬ą„­ą„®ą„Æą„¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą¤Ŗą¤¹ą¤¾ą¤Ÿą„‡|ą¤øą¤•ą¤¾ą¤³ą„€|ą¤¦ą„ą¤Ŗą¤¾ą¤°ą„€|ą¤øą¤¾ą¤Æą¤‚ą¤•ą¤¾ą¤³ą„€|ą¤°ą¤¾ą¤¤ą„ą¤°ą„€/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą¤Ŗą¤¹ą¤¾ą¤Ÿą„‡"||t==="ą¤øą¤•ą¤¾ą¤³ą„€")return e;else if(t==="ą¤¦ą„ą¤Ŗą¤¾ą¤°ą„€"||t==="ą¤øą¤¾ą¤Æą¤‚ą¤•ą¤¾ą¤³ą„€"||t==="ą¤°ą¤¾ą¤¤ą„ą¤°ą„€")return e>=12?e:e+12},meridiem:function(e,t,n){if(e>=0&&e<6)return"ą¤Ŗą¤¹ą¤¾ą¤Ÿą„‡";else if(e<12)return"ą¤øą¤•ą¤¾ą¤³ą„€";else if(e<17)return"ą¤¦ą„ą¤Ŗą¤¾ą¤°ą„€";else if(e<20)return"ą¤øą¤¾ą¤Æą¤‚ą¤•ą¤¾ą¤³ą„€";else return"ą¤°ą¤¾ą¤¤ą„ą¤°ą„€"},week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"ą„§",2:"ą„Ø",3:"ą„©",4:"ą„Ŗ",5:"ą„«",6:"ą„¬",7:"ą„­",8:"ą„®",9:"ą„Æ",0:"ą„¦"},n={"ą„§":"1","ą„Ø":"2","ą„©":"3","ą„Ŗ":"4","ą„«":"5","ą„¬":"6","ą„­":"7","ą„®":"8","ą„Æ":"9","ą„¦":"0"},a;function r(e,t,n,a){var r="";if(t)switch(n){case"s":r="ą¤•ą¤¾ą¤¹ą„€ ą¤øą„‡ą¤•ą¤‚ą¤¦";break;case"ss":r="%d ą¤øą„‡ą¤•ą¤‚ą¤¦";break;case"m":r="ą¤ą¤• ą¤®ą¤æą¤Øą¤æą¤Ÿ";break;case"mm":r="%d ą¤®ą¤æą¤Øą¤æą¤Ÿą„‡";break;case"h":r="ą¤ą¤• ą¤¤ą¤¾ą¤ø";break;case"hh":r="%d ą¤¤ą¤¾ą¤ø";break;case"d":r="ą¤ą¤• ą¤¦ą¤æą¤µą¤ø";break;case"dd":r="%d ą¤¦ą¤æą¤µą¤ø";break;case"M":r="ą¤ą¤• ą¤®ą¤¹ą¤æą¤Øą¤¾";break;case"MM":r="%d ą¤®ą¤¹ą¤æą¤Øą„‡";break;case"y":r="ą¤ą¤• ą¤µą¤°ą„ą¤·";break;case"yy":r="%d ą¤µą¤°ą„ą¤·ą„‡";break}else switch(n){case"s":r="ą¤•ą¤¾ą¤¹ą„€ ą¤øą„‡ą¤•ą¤‚ą¤¦ą¤¾ą¤‚";break;case"ss":r="%d ą¤øą„‡ą¤•ą¤‚ą¤¦ą¤¾ą¤‚";break;case"m":r="ą¤ą¤•ą¤¾ ą¤®ą¤æą¤Øą¤æą¤Ÿą¤¾";break;case"mm":r="%d ą¤®ą¤æą¤Øą¤æą¤Ÿą¤¾ą¤‚";break;case"h":r="ą¤ą¤•ą¤¾ ą¤¤ą¤¾ą¤øą¤¾";break;case"hh":r="%d ą¤¤ą¤¾ą¤øą¤¾ą¤‚";break;case"d":r="ą¤ą¤•ą¤¾ ą¤¦ą¤æą¤µą¤øą¤¾";break;case"dd":r="%d ą¤¦ą¤æą¤µą¤øą¤¾ą¤‚";break;case"M":r="ą¤ą¤•ą¤¾ ą¤®ą¤¹ą¤æą¤Øą„ą¤Æą¤¾";break;case"MM":r="%d ą¤®ą¤¹ą¤æą¤Øą„ą¤Æą¤¾ą¤‚";break;case"y":r="ą¤ą¤•ą¤¾ ą¤µą¤°ą„ą¤·ą¤¾";break;case"yy":r="%d ą¤µą¤°ą„ą¤·ą¤¾ą¤‚";break}return r.replace(/%d/i,e)}e.defineLocale("mr",{months:"ą¤œą¤¾ą¤Øą„‡ą¤µą¤¾ą¤°ą„€_ą¤«ą„‡ą¤¬ą„ą¤°ą„ą¤µą¤¾ą¤°ą„€_ą¤®ą¤¾ą¤°ą„ą¤š_ą¤ą¤Ŗą„ą¤°ą¤æą¤²_ą¤®ą„‡_ą¤œą„‚ą¤Ø_ą¤œą„ą¤²ą„ˆ_ą¤‘ą¤—ą¤øą„ą¤Ÿ_ą¤øą¤Ŗą„ą¤Ÿą„‡ą¤‚ą¤¬ą¤°_ą¤‘ą¤•ą„ą¤Ÿą„‹ą¤¬ą¤°_ą¤Øą„‹ą¤µą„ą¤¹ą„‡ą¤‚ą¤¬ą¤°_ą¤”ą¤æą¤øą„‡ą¤‚ą¤¬ą¤°".split("_"),monthsShort:"ą¤œą¤¾ą¤Øą„‡._ą¤«ą„‡ą¤¬ą„ą¤°ą„._ą¤®ą¤¾ą¤°ą„ą¤š._ą¤ą¤Ŗą„ą¤°ą¤æ._ą¤®ą„‡._ą¤œą„‚ą¤Ø._ą¤œą„ą¤²ą„ˆ._ą¤‘ą¤—._ą¤øą¤Ŗą„ą¤Ÿą„‡ą¤‚._ą¤‘ą¤•ą„ą¤Ÿą„‹._ą¤Øą„‹ą¤µą„ą¤¹ą„‡ą¤‚._ą¤”ą¤æą¤øą„‡ą¤‚.".split("_"),monthsParseExact:true,weekdays:"ą¤°ą¤µą¤æą¤µą¤¾ą¤°_ą¤øą„‹ą¤®ą¤µą¤¾ą¤°_ą¤®ą¤‚ą¤—ą¤³ą¤µą¤¾ą¤°_ą¤¬ą„ą¤§ą¤µą¤¾ą¤°_ą¤—ą„ą¤°ą„‚ą¤µą¤¾ą¤°_ą¤¶ą„ą¤•ą„ą¤°ą¤µą¤¾ą¤°_ą¤¶ą¤Øą¤æą¤µą¤¾ą¤°".split("_"),weekdaysShort:"ą¤°ą¤µą¤æ_ą¤øą„‹ą¤®_ą¤®ą¤‚ą¤—ą¤³_ą¤¬ą„ą¤§_ą¤—ą„ą¤°ą„‚_ą¤¶ą„ą¤•ą„ą¤°_ą¤¶ą¤Øą¤æ".split("_"),weekdaysMin:"ą¤°_ą¤øą„‹_ą¤®ą¤‚_ą¤¬ą„_ą¤—ą„_ą¤¶ą„_ą¤¶".split("_"),longDateFormat:{LT:"A h:mm ą¤µą¤¾ą¤œą¤¤ą¤¾",LTS:"A h:mm:ss ą¤µą¤¾ą¤œą¤¤ą¤¾",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ą¤µą¤¾ą¤œą¤¤ą¤¾",LLLL:"dddd, D MMMM YYYY, A h:mm ą¤µą¤¾ą¤œą¤¤ą¤¾"},calendar:{sameDay:"[ą¤†ą¤œ] LT",nextDay:"[ą¤‰ą¤¦ą„ą¤Æą¤¾] LT",nextWeek:"dddd, LT",lastDay:"[ą¤•ą¤¾ą¤²] LT",lastWeek:"[ą¤®ą¤¾ą¤—ą„€ą¤²] dddd, LT",sameElse:"L"},relativeTime:{future:"%są¤®ą¤§ą„ą¤Æą„‡",past:"%są¤Ŗą„‚ą¤°ą„ą¤µą„€",s:r,ss:r,m:r,mm:r,h:r,hh:r,d:r,dd:r,M:r,MM:r,y:r,yy:r},preparse:function(e){return e.replace(/[ą„§ą„Øą„©ą„Ŗą„«ą„¬ą„­ą„®ą„Æą„¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą¤Ŗą¤¹ą¤¾ą¤Ÿą„‡|ą¤øą¤•ą¤¾ą¤³ą„€|ą¤¦ą„ą¤Ŗą¤¾ą¤°ą„€|ą¤øą¤¾ą¤Æą¤‚ą¤•ą¤¾ą¤³ą„€|ą¤°ą¤¾ą¤¤ą„ą¤°ą„€/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą¤Ŗą¤¹ą¤¾ą¤Ÿą„‡"||t==="ą¤øą¤•ą¤¾ą¤³ą„€")return e;else if(t==="ą¤¦ą„ą¤Ŗą¤¾ą¤°ą„€"||t==="ą¤øą¤¾ą¤Æą¤‚ą¤•ą¤¾ą¤³ą„€"||t==="ą¤°ą¤¾ą¤¤ą„ą¤°ą„€")return e>=12?e:e+12},meridiem:function(e,t,n){if(e>=0&&e<6)return"ą¤Ŗą¤¹ą¤¾ą¤Ÿą„‡";else if(e<12)return"ą¤øą¤•ą¤¾ą¤³ą„€";else if(e<17)return"ą¤¦ą„ą¤Ŗą¤¾ą¤°ą„€";else if(e<20)return"ą¤øą¤¾ą¤Æą¤‚ą¤•ą¤¾ą¤³ą„€";else return"ą¤°ą¤¾ą¤¤ą„ą¤°ą„€"},week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("ms",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="pagi")return e;else if(t==="tengahari")return e>=11?e:e+12;else if(t==="petang"||t==="malam")return e+12},meridiem:function(e,t,n){if(e<11)return"pagi";else if(e<15)return"tengahari";else if(e<19)return"petang";else return"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",ss:"%d saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("ms",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="pagi")return e;else if(t==="tengahari")return e>=11?e:e+12;else if(t==="petang"||t==="malam")return e+12},meridiem:function(e,t,n){if(e<11)return"pagi";else if(e<15)return"tengahari";else if(e<19)return"petang";else return"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",ss:"%d saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("ms-my",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="pagi")return e;else if(t==="tengahari")return e>=11?e:e+12;else if(t==="petang"||t==="malam")return e+12},meridiem:function(e,t,n){if(e<11)return"pagi";else if(e<15)return"tengahari";else if(e<19)return"petang";else return"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",ss:"%d saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("ms-my",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="pagi")return e;else if(t==="tengahari")return e>=11?e:e+12;else if(t==="petang"||t==="malam")return e+12},meridiem:function(e,t,n){if(e<11)return"pagi";else if(e<15)return"tengahari";else if(e<19)return"petang";else return"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",ss:"%d saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("mt",{months:"Jannar_Frar_Marzu_April_Mejju_Ä unju_Lulju_Awwissu_Settembru_Ottubru_Novembru_Diċembru".split("_"),monthsShort:"Jan_Fra_Mar_Apr_Mej_Ä un_Lul_Aww_Set_Ott_Nov_Diċ".split("_"),weekdays:"Il-Ħadd_It-Tnejn_It-Tlieta_L-Erbgħa_Il-Ħamis_Il-Ä imgħa_Is-Sibt".split("_"),weekdaysShort:"Ħad_Tne_Tli_Erb_Ħam_Ä im_Sib".split("_"),weekdaysMin:"Ħa_Tn_Tl_Er_Ħa_Ä i_Si".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Illum fil-]LT",nextDay:"[Għada fil-]LT",nextWeek:"dddd [fil-]LT",lastDay:"[Il-bieraħ fil-]LT",lastWeek:"dddd [li għadda] [fil-]LT",sameElse:"L"},relativeTime:{future:"fā€™ %s",past:"%s ilu",s:"ftit sekondi",ss:"%d sekondi",m:"minuta",mm:"%d minuti",h:"siegħa",hh:"%d siegħat",d:"Ä”urnata",dd:"%d Ä”ranet",M:"xahar",MM:"%d xhur",y:"sena",yy:"%d sni"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("mt",{months:"Jannar_Frar_Marzu_April_Mejju_Ä unju_Lulju_Awwissu_Settembru_Ottubru_Novembru_Diċembru".split("_"),monthsShort:"Jan_Fra_Mar_Apr_Mej_Ä un_Lul_Aww_Set_Ott_Nov_Diċ".split("_"),weekdays:"Il-Ħadd_It-Tnejn_It-Tlieta_L-Erbgħa_Il-Ħamis_Il-Ä imgħa_Is-Sibt".split("_"),weekdaysShort:"Ħad_Tne_Tli_Erb_Ħam_Ä im_Sib".split("_"),weekdaysMin:"Ħa_Tn_Tl_Er_Ħa_Ä i_Si".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Illum fil-]LT",nextDay:"[Għada fil-]LT",nextWeek:"dddd [fil-]LT",lastDay:"[Il-bieraħ fil-]LT",lastWeek:"dddd [li għadda] [fil-]LT",sameElse:"L"},relativeTime:{future:"fā€™ %s",past:"%s ilu",s:"ftit sekondi",ss:"%d sekondi",m:"minuta",mm:"%d minuti",h:"siegħa",hh:"%d siegħat",d:"Ä”urnata",dd:"%d Ä”ranet",M:"xahar",MM:"%d xhur",y:"sena",yy:"%d sni"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"į",2:"į‚",3:"įƒ",4:"į„",5:"į…",6:"į†",7:"į‡",8:"įˆ",9:"į‰",0:"į€"},n={"į":"1","į‚":"2","įƒ":"3","į„":"4","į…":"5","į†":"6","į‡":"7","įˆ":"8","į‰":"9","į€":"0"},a;e.defineLocale("my",{months:"į€‡į€”į€ŗį€”į€į€«į€›į€®_į€–į€±į€–į€±į€¬į€ŗį€į€«į€›į€®_į€™į€į€ŗ_į€§į€•į€¼į€®_į€™į€±_į€‡į€½į€”į€ŗ_į€‡į€°į€œį€­į€Æį€„į€ŗ_į€žį€¼į€‚į€Æį€į€ŗ_į€…į€€į€ŗį€į€„į€ŗį€˜į€¬_į€”į€±į€¬į€€į€ŗį€į€­į€Æį€˜į€¬_į€”į€­į€Æį€į€„į€ŗį€˜į€¬_į€’į€®į€‡į€„į€ŗį€˜į€¬".split("_"),monthsShort:"į€‡į€”į€ŗ_į€–į€±_į€™į€į€ŗ_į€•į€¼į€®_į€™į€±_į€‡į€½į€”į€ŗ_į€œį€­į€Æį€„į€ŗ_į€žį€¼_į€…į€€į€ŗ_į€”į€±į€¬į€€į€ŗ_į€”į€­į€Æ_į€’į€®".split("_"),weekdays:"į€į€”į€„į€ŗį€¹į€‚į€”į€½į€±_į€į€”į€„į€ŗį€¹į€œį€¬_į€”į€„į€ŗį€¹į€‚į€«_į€—į€Æį€’į€¹į€“į€Ÿį€°į€ø_į€€į€¼į€¬į€žį€•į€į€±į€ø_į€žį€±į€¬į€€į€¼į€¬_į€…į€”į€±".split("_"),weekdaysShort:"į€”į€½į€±_į€œį€¬_į€‚į€«_į€Ÿį€°į€ø_į€€į€¼į€¬_į€žį€±į€¬_į€”į€±".split("_"),weekdaysMin:"į€”į€½į€±_į€œį€¬_į€‚į€«_į€Ÿį€°į€ø_į€€į€¼į€¬_į€žį€±į€¬_į€”į€±".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[į€šį€”į€±.] LT [į€™į€¾į€¬]",nextDay:"[į€™į€”į€€į€ŗį€–į€¼į€”į€ŗ] LT [į€™į€¾į€¬]",nextWeek:"dddd LT [į€™į€¾į€¬]",lastDay:"[į€™į€”į€±.į€€] LT [į€™į€¾į€¬]",lastWeek:"[į€•į€¼į€®į€øį€į€²į€·į€žį€±į€¬] dddd LT [į€™į€¾į€¬]",sameElse:"L"},relativeTime:{future:"į€œį€¬į€™į€Šį€ŗį€· %s į€™į€¾į€¬",past:"į€œį€½į€”į€ŗį€į€²į€·į€žį€±į€¬ %s į€€",s:"į€…į€€į€¹į€€į€”į€ŗ.į€”į€”į€Šį€ŗį€øį€„į€šį€ŗ",ss:"%d į€…į€€į€¹į€€į€”į€·į€ŗ",m:"į€į€…į€ŗį€™į€­į€”į€…į€ŗ",mm:"%d į€™į€­į€”į€…į€ŗ",h:"į€į€…į€ŗį€”į€¬į€›į€®",hh:"%d į€”į€¬į€›į€®",d:"į€į€…į€ŗį€›į€€į€ŗ",dd:"%d į€›į€€į€ŗ",M:"į€į€…į€ŗį€œ",MM:"%d į€œ",y:"į€į€…į€ŗį€”į€¾į€…į€ŗ",yy:"%d į€”į€¾į€…į€ŗ"},preparse:function(e){return e.replace(/[įį‚įƒį„į…į†į‡įˆį‰į€]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"į",2:"į‚",3:"įƒ",4:"į„",5:"į…",6:"į†",7:"į‡",8:"įˆ",9:"į‰",0:"į€"},n={"į":"1","į‚":"2","įƒ":"3","į„":"4","į…":"5","į†":"6","į‡":"7","įˆ":"8","į‰":"9","į€":"0"},a;e.defineLocale("my",{months:"į€‡į€”į€ŗį€”į€į€«į€›į€®_į€–į€±į€–į€±į€¬į€ŗį€į€«į€›į€®_į€™į€į€ŗ_į€§į€•į€¼į€®_į€™į€±_į€‡į€½į€”į€ŗ_į€‡į€°į€œį€­į€Æį€„į€ŗ_į€žį€¼į€‚į€Æį€į€ŗ_į€…į€€į€ŗį€į€„į€ŗį€˜į€¬_į€”į€±į€¬į€€į€ŗį€į€­į€Æį€˜į€¬_į€”į€­į€Æį€į€„į€ŗį€˜į€¬_į€’į€®į€‡į€„į€ŗį€˜į€¬".split("_"),monthsShort:"į€‡į€”į€ŗ_į€–į€±_į€™į€į€ŗ_į€•į€¼į€®_į€™į€±_į€‡į€½į€”į€ŗ_į€œį€­į€Æį€„į€ŗ_į€žį€¼_į€…į€€į€ŗ_į€”į€±į€¬į€€į€ŗ_į€”į€­į€Æ_į€’į€®".split("_"),weekdays:"į€į€”į€„į€ŗį€¹į€‚į€”į€½į€±_į€į€”į€„į€ŗį€¹į€œį€¬_į€”į€„į€ŗį€¹į€‚į€«_į€—į€Æį€’į€¹į€“į€Ÿį€°į€ø_į€€į€¼į€¬į€žį€•į€į€±į€ø_į€žį€±į€¬į€€į€¼į€¬_į€…į€”į€±".split("_"),weekdaysShort:"į€”į€½į€±_į€œį€¬_į€‚į€«_į€Ÿį€°į€ø_į€€į€¼į€¬_į€žį€±į€¬_į€”į€±".split("_"),weekdaysMin:"į€”į€½į€±_į€œį€¬_į€‚į€«_į€Ÿį€°į€ø_į€€į€¼į€¬_į€žį€±į€¬_į€”į€±".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[į€šį€”į€±.] LT [į€™į€¾į€¬]",nextDay:"[į€™į€”į€€į€ŗį€–į€¼į€”į€ŗ] LT [į€™į€¾į€¬]",nextWeek:"dddd LT [į€™į€¾į€¬]",lastDay:"[į€™į€”į€±.į€€] LT [į€™į€¾į€¬]",lastWeek:"[į€•į€¼į€®į€øį€į€²į€·į€žį€±į€¬] dddd LT [į€™į€¾į€¬]",sameElse:"L"},relativeTime:{future:"į€œį€¬į€™į€Šį€ŗį€· %s į€™į€¾į€¬",past:"į€œį€½į€”į€ŗį€į€²į€·į€žį€±į€¬ %s į€€",s:"į€…į€€į€¹į€€į€”į€ŗ.į€”į€”į€Šį€ŗį€øį€„į€šį€ŗ",ss:"%d į€…į€€į€¹į€€į€”į€·į€ŗ",m:"į€į€…į€ŗį€™į€­į€”į€…į€ŗ",mm:"%d į€™į€­į€”į€…į€ŗ",h:"į€į€…į€ŗį€”į€¬į€›į€®",hh:"%d į€”į€¬į€›į€®",d:"į€į€…į€ŗį€›į€€į€ŗ",dd:"%d į€›į€€į€ŗ",M:"į€į€…į€ŗį€œ",MM:"%d į€œ",y:"į€į€…į€ŗį€”į€¾į€…į€ŗ",yy:"%d į€”į€¾į€…į€ŗ"},preparse:function(e){return e.replace(/[įį‚įƒį„į…į†į‡įˆį‰į€]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("nb",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan._feb._mars_apr._mai_juni_juli_aug._sep._okt._nov._des.".split("_"),monthsParseExact:true,weekdays:"sĆøndag_mandag_tirsdag_onsdag_torsdag_fredag_lĆørdag".split("_"),weekdaysShort:"sĆø._ma._ti._on._to._fr._lĆø.".split("_"),weekdaysMin:"sĆø_ma_ti_on_to_fr_lĆø".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] HH:mm",LLLL:"dddd D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[i gĆ„r kl.] LT",lastWeek:"[forrige] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"noen sekunder",ss:"%d sekunder",m:"ett minutt",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dager",w:"en uke",ww:"%d uker",M:"en mĆ„ned",MM:"%d mĆ„neder",y:"ett Ć„r",yy:"%d Ć„r"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("nb",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan._feb._mars_apr._mai_juni_juli_aug._sep._okt._nov._des.".split("_"),monthsParseExact:true,weekdays:"sĆøndag_mandag_tirsdag_onsdag_torsdag_fredag_lĆørdag".split("_"),weekdaysShort:"sĆø._ma._ti._on._to._fr._lĆø.".split("_"),weekdaysMin:"sĆø_ma_ti_on_to_fr_lĆø".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] HH:mm",LLLL:"dddd D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[i gĆ„r kl.] LT",lastWeek:"[forrige] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"noen sekunder",ss:"%d sekunder",m:"ett minutt",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dager",w:"en uke",ww:"%d uker",M:"en mĆ„ned",MM:"%d mĆ„neder",y:"ett Ć„r",yy:"%d Ć„r"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"ą„§",2:"ą„Ø",3:"ą„©",4:"ą„Ŗ",5:"ą„«",6:"ą„¬",7:"ą„­",8:"ą„®",9:"ą„Æ",0:"ą„¦"},n={"ą„§":"1","ą„Ø":"2","ą„©":"3","ą„Ŗ":"4","ą„«":"5","ą„¬":"6","ą„­":"7","ą„®":"8","ą„Æ":"9","ą„¦":"0"},a;e.defineLocale("ne",{months:"ą¤œą¤Øą¤µą¤°ą„€_ą¤«ą„‡ą¤¬ą„ą¤°ą„ą¤µą¤°ą„€_ą¤®ą¤¾ą¤°ą„ą¤š_ą¤…ą¤Ŗą„ą¤°ą¤æą¤²_ą¤®ą¤ˆ_ą¤œą„ą¤Ø_ą¤œą„ą¤²ą¤¾ą¤ˆ_ą¤…ą¤—ą¤·ą„ą¤Ÿ_ą¤øą„‡ą¤Ŗą„ą¤Ÿą„‡ą¤®ą„ą¤¬ą¤°_ą¤…ą¤•ą„ą¤Ÿą„‹ą¤¬ą¤°_ą¤Øą„‹ą¤­ą„‡ą¤®ą„ą¤¬ą¤°_ą¤”ą¤æą¤øą„‡ą¤®ą„ą¤¬ą¤°".split("_"),monthsShort:"ą¤œą¤Ø._ą¤«ą„‡ą¤¬ą„ą¤°ą„._ą¤®ą¤¾ą¤°ą„ą¤š_ą¤…ą¤Ŗą„ą¤°ą¤æ._ą¤®ą¤ˆ_ą¤œą„ą¤Ø_ą¤œą„ą¤²ą¤¾ą¤ˆ._ą¤…ą¤—._ą¤øą„‡ą¤Ŗą„ą¤Ÿ._ą¤…ą¤•ą„ą¤Ÿą„‹._ą¤Øą„‹ą¤­ą„‡._ą¤”ą¤æą¤øą„‡.".split("_"),monthsParseExact:true,weekdays:"ą¤†ą¤‡ą¤¤ą¤¬ą¤¾ą¤°_ą¤øą„‹ą¤®ą¤¬ą¤¾ą¤°_ą¤®ą¤™ą„ą¤—ą¤²ą¤¬ą¤¾ą¤°_ą¤¬ą„ą¤§ą¤¬ą¤¾ą¤°_ą¤¬ą¤æą¤¹ą¤æą¤¬ą¤¾ą¤°_ą¤¶ą„ą¤•ą„ą¤°ą¤¬ą¤¾ą¤°_ą¤¶ą¤Øą¤æą¤¬ą¤¾ą¤°".split("_"),weekdaysShort:"ą¤†ą¤‡ą¤¤._ą¤øą„‹ą¤®._ą¤®ą¤™ą„ą¤—ą¤²._ą¤¬ą„ą¤§._ą¤¬ą¤æą¤¹ą¤æ._ą¤¶ą„ą¤•ą„ą¤°._ą¤¶ą¤Øą¤æ.".split("_"),weekdaysMin:"ą¤†._ą¤øą„‹._ą¤®ą¤‚._ą¤¬ą„._ą¤¬ą¤æ._ą¤¶ą„._ą¤¶.".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"Aą¤•ą„‹ h:mm ą¤¬ą¤œą„‡",LTS:"Aą¤•ą„‹ h:mm:ss ą¤¬ą¤œą„‡",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, Aą¤•ą„‹ h:mm ą¤¬ą¤œą„‡",LLLL:"dddd, D MMMM YYYY, Aą¤•ą„‹ h:mm ą¤¬ą¤œą„‡"},preparse:function(e){return e.replace(/[ą„§ą„Øą„©ą„Ŗą„«ą„¬ą„­ą„®ą„Æą„¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą¤°ą¤¾ą¤¤ą¤æ|ą¤¬ą¤æą¤¹ą¤¾ą¤Ø|ą¤¦ą¤æą¤‰ą¤ą¤øą„‹|ą¤øą¤¾ą¤ą¤/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą¤°ą¤¾ą¤¤ą¤æ")return e<4?e:e+12;else if(t==="ą¤¬ą¤æą¤¹ą¤¾ą¤Ø")return e;else if(t==="ą¤¦ą¤æą¤‰ą¤ą¤øą„‹")return e>=10?e:e+12;else if(t==="ą¤øą¤¾ą¤ą¤")return e+12},meridiem:function(e,t,n){if(e<3)return"ą¤°ą¤¾ą¤¤ą¤æ";else if(e<12)return"ą¤¬ą¤æą¤¹ą¤¾ą¤Ø";else if(e<16)return"ą¤¦ą¤æą¤‰ą¤ą¤øą„‹";else if(e<20)return"ą¤øą¤¾ą¤ą¤";else return"ą¤°ą¤¾ą¤¤ą¤æ"},calendar:{sameDay:"[ą¤†ą¤œ] LT",nextDay:"[ą¤­ą„‹ą¤²ą¤æ] LT",nextWeek:"[ą¤†ą¤‰ą¤ą¤¦ą„‹] dddd[,] LT",lastDay:"[ą¤¹ą¤æą¤œą„‹] LT",lastWeek:"[ą¤—ą¤ą¤•ą„‹] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%są¤®ą¤¾",past:"%s ą¤…ą¤—ą¤¾ą¤”ą¤æ",s:"ą¤•ą„‡ą¤¹ą„€ ą¤•ą„ą¤·ą¤£",ss:"%d ą¤øą„‡ą¤•ą„‡ą¤£ą„ą¤”",m:"ą¤ą¤• ą¤®ą¤æą¤Øą„‡ą¤Ÿ",mm:"%d ą¤®ą¤æą¤Øą„‡ą¤Ÿ",h:"ą¤ą¤• ą¤˜ą¤£ą„ą¤Ÿą¤¾",hh:"%d ą¤˜ą¤£ą„ą¤Ÿą¤¾",d:"ą¤ą¤• ą¤¦ą¤æą¤Ø",dd:"%d ą¤¦ą¤æą¤Ø",M:"ą¤ą¤• ą¤®ą¤¹ą¤æą¤Øą¤¾",MM:"%d ą¤®ą¤¹ą¤æą¤Øą¤¾",y:"ą¤ą¤• ą¤¬ą¤°ą„ą¤·",yy:"%d ą¤¬ą¤°ą„ą¤·"},week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"ą„§",2:"ą„Ø",3:"ą„©",4:"ą„Ŗ",5:"ą„«",6:"ą„¬",7:"ą„­",8:"ą„®",9:"ą„Æ",0:"ą„¦"},n={"ą„§":"1","ą„Ø":"2","ą„©":"3","ą„Ŗ":"4","ą„«":"5","ą„¬":"6","ą„­":"7","ą„®":"8","ą„Æ":"9","ą„¦":"0"},a;e.defineLocale("ne",{months:"ą¤œą¤Øą¤µą¤°ą„€_ą¤«ą„‡ą¤¬ą„ą¤°ą„ą¤µą¤°ą„€_ą¤®ą¤¾ą¤°ą„ą¤š_ą¤…ą¤Ŗą„ą¤°ą¤æą¤²_ą¤®ą¤ˆ_ą¤œą„ą¤Ø_ą¤œą„ą¤²ą¤¾ą¤ˆ_ą¤…ą¤—ą¤·ą„ą¤Ÿ_ą¤øą„‡ą¤Ŗą„ą¤Ÿą„‡ą¤®ą„ą¤¬ą¤°_ą¤…ą¤•ą„ą¤Ÿą„‹ą¤¬ą¤°_ą¤Øą„‹ą¤­ą„‡ą¤®ą„ą¤¬ą¤°_ą¤”ą¤æą¤øą„‡ą¤®ą„ą¤¬ą¤°".split("_"),monthsShort:"ą¤œą¤Ø._ą¤«ą„‡ą¤¬ą„ą¤°ą„._ą¤®ą¤¾ą¤°ą„ą¤š_ą¤…ą¤Ŗą„ą¤°ą¤æ._ą¤®ą¤ˆ_ą¤œą„ą¤Ø_ą¤œą„ą¤²ą¤¾ą¤ˆ._ą¤…ą¤—._ą¤øą„‡ą¤Ŗą„ą¤Ÿ._ą¤…ą¤•ą„ą¤Ÿą„‹._ą¤Øą„‹ą¤­ą„‡._ą¤”ą¤æą¤øą„‡.".split("_"),monthsParseExact:true,weekdays:"ą¤†ą¤‡ą¤¤ą¤¬ą¤¾ą¤°_ą¤øą„‹ą¤®ą¤¬ą¤¾ą¤°_ą¤®ą¤™ą„ą¤—ą¤²ą¤¬ą¤¾ą¤°_ą¤¬ą„ą¤§ą¤¬ą¤¾ą¤°_ą¤¬ą¤æą¤¹ą¤æą¤¬ą¤¾ą¤°_ą¤¶ą„ą¤•ą„ą¤°ą¤¬ą¤¾ą¤°_ą¤¶ą¤Øą¤æą¤¬ą¤¾ą¤°".split("_"),weekdaysShort:"ą¤†ą¤‡ą¤¤._ą¤øą„‹ą¤®._ą¤®ą¤™ą„ą¤—ą¤²._ą¤¬ą„ą¤§._ą¤¬ą¤æą¤¹ą¤æ._ą¤¶ą„ą¤•ą„ą¤°._ą¤¶ą¤Øą¤æ.".split("_"),weekdaysMin:"ą¤†._ą¤øą„‹._ą¤®ą¤‚._ą¤¬ą„._ą¤¬ą¤æ._ą¤¶ą„._ą¤¶.".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"Aą¤•ą„‹ h:mm ą¤¬ą¤œą„‡",LTS:"Aą¤•ą„‹ h:mm:ss ą¤¬ą¤œą„‡",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, Aą¤•ą„‹ h:mm ą¤¬ą¤œą„‡",LLLL:"dddd, D MMMM YYYY, Aą¤•ą„‹ h:mm ą¤¬ą¤œą„‡"},preparse:function(e){return e.replace(/[ą„§ą„Øą„©ą„Ŗą„«ą„¬ą„­ą„®ą„Æą„¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą¤°ą¤¾ą¤¤ą¤æ|ą¤¬ą¤æą¤¹ą¤¾ą¤Ø|ą¤¦ą¤æą¤‰ą¤ą¤øą„‹|ą¤øą¤¾ą¤ą¤/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą¤°ą¤¾ą¤¤ą¤æ")return e<4?e:e+12;else if(t==="ą¤¬ą¤æą¤¹ą¤¾ą¤Ø")return e;else if(t==="ą¤¦ą¤æą¤‰ą¤ą¤øą„‹")return e>=10?e:e+12;else if(t==="ą¤øą¤¾ą¤ą¤")return e+12},meridiem:function(e,t,n){if(e<3)return"ą¤°ą¤¾ą¤¤ą¤æ";else if(e<12)return"ą¤¬ą¤æą¤¹ą¤¾ą¤Ø";else if(e<16)return"ą¤¦ą¤æą¤‰ą¤ą¤øą„‹";else if(e<20)return"ą¤øą¤¾ą¤ą¤";else return"ą¤°ą¤¾ą¤¤ą¤æ"},calendar:{sameDay:"[ą¤†ą¤œ] LT",nextDay:"[ą¤­ą„‹ą¤²ą¤æ] LT",nextWeek:"[ą¤†ą¤‰ą¤ą¤¦ą„‹] dddd[,] LT",lastDay:"[ą¤¹ą¤æą¤œą„‹] LT",lastWeek:"[ą¤—ą¤ą¤•ą„‹] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%są¤®ą¤¾",past:"%s ą¤…ą¤—ą¤¾ą¤”ą¤æ",s:"ą¤•ą„‡ą¤¹ą„€ ą¤•ą„ą¤·ą¤£",ss:"%d ą¤øą„‡ą¤•ą„‡ą¤£ą„ą¤”",m:"ą¤ą¤• ą¤®ą¤æą¤Øą„‡ą¤Ÿ",mm:"%d ą¤®ą¤æą¤Øą„‡ą¤Ÿ",h:"ą¤ą¤• ą¤˜ą¤£ą„ą¤Ÿą¤¾",hh:"%d ą¤˜ą¤£ą„ą¤Ÿą¤¾",d:"ą¤ą¤• ą¤¦ą¤æą¤Ø",dd:"%d ą¤¦ą¤æą¤Ø",M:"ą¤ą¤• ą¤®ą¤¹ą¤æą¤Øą¤¾",MM:"%d ą¤®ą¤¹ą¤æą¤Øą¤¾",y:"ą¤ą¤• ą¤¬ą¤°ą„ą¤·",yy:"%d ą¤¬ą¤°ą„ą¤·"},week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var n="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),a="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),t=[/^jan/i,/^feb/i,/^maart|mrt.?$/i,/^apr/i,/^mei$/i,/^jun[i.]?$/i,/^jul[i.]?$/i,/^aug/i,/^sep/i,/^okt/i,/^nov/i,/^dec/i],r=/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,o;e.defineLocale("nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(e,t){if(!e)return n;else if(/-MMM-/.test(t))return a[e.month()];else return n[e.month()]},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december)/i,monthsShortStrictRegex:/^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"zo_ma_di_wo_do_vr_za".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",ss:"%d seconden",m:"Ć©Ć©n minuut",mm:"%d minuten",h:"Ć©Ć©n uur",hh:"%d uur",d:"Ć©Ć©n dag",dd:"%d dagen",w:"Ć©Ć©n week",ww:"%d weken",M:"Ć©Ć©n maand",MM:"%d maanden",y:"Ć©Ć©n jaar",yy:"%d jaar"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(e===1||e===8||e>=20?"ste":"de")},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var n="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),a="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),t=[/^jan/i,/^feb/i,/^maart|mrt.?$/i,/^apr/i,/^mei$/i,/^jun[i.]?$/i,/^jul[i.]?$/i,/^aug/i,/^sep/i,/^okt/i,/^nov/i,/^dec/i],r=/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,o;e.defineLocale("nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(e,t){if(!e)return n;else if(/-MMM-/.test(t))return a[e.month()];else return n[e.month()]},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december)/i,monthsShortStrictRegex:/^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"zo_ma_di_wo_do_vr_za".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",ss:"%d seconden",m:"Ć©Ć©n minuut",mm:"%d minuten",h:"Ć©Ć©n uur",hh:"%d uur",d:"Ć©Ć©n dag",dd:"%d dagen",w:"Ć©Ć©n week",ww:"%d weken",M:"Ć©Ć©n maand",MM:"%d maanden",y:"Ć©Ć©n jaar",yy:"%d jaar"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(e===1||e===8||e>=20?"ste":"de")},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var n="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),a="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),t=[/^jan/i,/^feb/i,/^maart|mrt.?$/i,/^apr/i,/^mei$/i,/^jun[i.]?$/i,/^jul[i.]?$/i,/^aug/i,/^sep/i,/^okt/i,/^nov/i,/^dec/i],r=/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,o;e.defineLocale("nl-be",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(e,t){if(!e)return n;else if(/-MMM-/.test(t))return a[e.month()];else return n[e.month()]},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december)/i,monthsShortStrictRegex:/^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"zo_ma_di_wo_do_vr_za".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",ss:"%d seconden",m:"Ć©Ć©n minuut",mm:"%d minuten",h:"Ć©Ć©n uur",hh:"%d uur",d:"Ć©Ć©n dag",dd:"%d dagen",M:"Ć©Ć©n maand",MM:"%d maanden",y:"Ć©Ć©n jaar",yy:"%d jaar"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(e===1||e===8||e>=20?"ste":"de")},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var n="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),a="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),t=[/^jan/i,/^feb/i,/^maart|mrt.?$/i,/^apr/i,/^mei$/i,/^jun[i.]?$/i,/^jul[i.]?$/i,/^aug/i,/^sep/i,/^okt/i,/^nov/i,/^dec/i],r=/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,o;e.defineLocale("nl-be",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(e,t){if(!e)return n;else if(/-MMM-/.test(t))return a[e.month()];else return n[e.month()]},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december)/i,monthsShortStrictRegex:/^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"zo_ma_di_wo_do_vr_za".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",ss:"%d seconden",m:"Ć©Ć©n minuut",mm:"%d minuten",h:"Ć©Ć©n uur",hh:"%d uur",d:"Ć©Ć©n dag",dd:"%d dagen",M:"Ć©Ć©n maand",MM:"%d maanden",y:"Ć©Ć©n jaar",yy:"%d jaar"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(e===1||e===8||e>=20?"ste":"de")},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("nn",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan._feb._mars_apr._mai_juni_juli_aug._sep._okt._nov._des.".split("_"),monthsParseExact:true,weekdays:"sundag_mĆ„ndag_tysdag_onsdag_torsdag_fredag_laurdag".split("_"),weekdaysShort:"su._mĆ„._ty._on._to._fr._lau.".split("_"),weekdaysMin:"su_mĆ„_ty_on_to_fr_la".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] H:mm",LLLL:"dddd D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[I dag klokka] LT",nextDay:"[I morgon klokka] LT",nextWeek:"dddd [klokka] LT",lastDay:"[I gĆ„r klokka] LT",lastWeek:"[FĆøregĆ„ande] dddd [klokka] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s sidan",s:"nokre sekund",ss:"%d sekund",m:"eit minutt",mm:"%d minutt",h:"ein time",hh:"%d timar",d:"ein dag",dd:"%d dagar",w:"ei veke",ww:"%d veker",M:"ein mĆ„nad",MM:"%d mĆ„nader",y:"eit Ć„r",yy:"%d Ć„r"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("nn",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan._feb._mars_apr._mai_juni_juli_aug._sep._okt._nov._des.".split("_"),monthsParseExact:true,weekdays:"sundag_mĆ„ndag_tysdag_onsdag_torsdag_fredag_laurdag".split("_"),weekdaysShort:"su._mĆ„._ty._on._to._fr._lau.".split("_"),weekdaysMin:"su_mĆ„_ty_on_to_fr_la".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] H:mm",LLLL:"dddd D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[I dag klokka] LT",nextDay:"[I morgon klokka] LT",nextWeek:"dddd [klokka] LT",lastDay:"[I gĆ„r klokka] LT",lastWeek:"[FĆøregĆ„ande] dddd [klokka] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s sidan",s:"nokre sekund",ss:"%d sekund",m:"eit minutt",mm:"%d minutt",h:"ein time",hh:"%d timar",d:"ein dag",dd:"%d dagar",w:"ei veke",ww:"%d veker",M:"ein mĆ„nad",MM:"%d mĆ„nader",y:"eit Ć„r",yy:"%d Ć„r"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("oc-lnc",{months:{standalone:"geniĆØr_febriĆØr_marƧ_abril_mai_junh_julhet_agost_setembre_octĆ²bre_novembre_decembre".split("_"),format:"de geniĆØr_de febriĆØr_de marƧ_d'abril_de mai_de junh_de julhet_d'agost_de setembre_d'octĆ²bre_de novembre_de decembre".split("_"),isFormat:/D[oD]?(\s)+MMMM/},monthsShort:"gen._febr._marƧ_abr._mai_junh_julh._ago._set._oct._nov._dec.".split("_"),monthsParseExact:true,weekdays:"dimenge_diluns_dimars_dimĆØcres_dijĆ²us_divendres_dissabte".split("_"),weekdaysShort:"dg._dl._dm._dc._dj._dv._ds.".split("_"),weekdaysMin:"dg_dl_dm_dc_dj_dv_ds".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [de] YYYY",ll:"D MMM YYYY",LLL:"D MMMM [de] YYYY [a] H:mm",lll:"D MMM YYYY, H:mm",LLLL:"dddd D MMMM [de] YYYY [a] H:mm",llll:"ddd D MMM YYYY, H:mm"},calendar:{sameDay:"[uĆØi a] LT",nextDay:"[deman a] LT",nextWeek:"dddd [a] LT",lastDay:"[iĆØr a] LT",lastWeek:"dddd [passat a] LT",sameElse:"L"},relativeTime:{future:"d'aquĆ­ %s",past:"fa %s",s:"unas segondas",ss:"%d segondas",m:"una minuta",mm:"%d minutas",h:"una ora",hh:"%d oras",d:"un jorn",dd:"%d jorns",M:"un mes",MM:"%d meses",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(r|n|t|ĆØ|a)/,ordinal:function(e,t){var n=e===1?"r":e===2?"n":e===3?"r":e===4?"t":"ĆØ";if(t==="w"||t==="W")n="a";return e+n},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("oc-lnc",{months:{standalone:"geniĆØr_febriĆØr_marƧ_abril_mai_junh_julhet_agost_setembre_octĆ²bre_novembre_decembre".split("_"),format:"de geniĆØr_de febriĆØr_de marƧ_d'abril_de mai_de junh_de julhet_d'agost_de setembre_d'octĆ²bre_de novembre_de decembre".split("_"),isFormat:/D[oD]?(\s)+MMMM/},monthsShort:"gen._febr._marƧ_abr._mai_junh_julh._ago._set._oct._nov._dec.".split("_"),monthsParseExact:true,weekdays:"dimenge_diluns_dimars_dimĆØcres_dijĆ²us_divendres_dissabte".split("_"),weekdaysShort:"dg._dl._dm._dc._dj._dv._ds.".split("_"),weekdaysMin:"dg_dl_dm_dc_dj_dv_ds".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [de] YYYY",ll:"D MMM YYYY",LLL:"D MMMM [de] YYYY [a] H:mm",lll:"D MMM YYYY, H:mm",LLLL:"dddd D MMMM [de] YYYY [a] H:mm",llll:"ddd D MMM YYYY, H:mm"},calendar:{sameDay:"[uĆØi a] LT",nextDay:"[deman a] LT",nextWeek:"dddd [a] LT",lastDay:"[iĆØr a] LT",lastWeek:"dddd [passat a] LT",sameElse:"L"},relativeTime:{future:"d'aquĆ­ %s",past:"fa %s",s:"unas segondas",ss:"%d segondas",m:"una minuta",mm:"%d minutas",h:"una ora",hh:"%d oras",d:"un jorn",dd:"%d jorns",M:"un mes",MM:"%d meses",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(r|n|t|ĆØ|a)/,ordinal:function(e,t){var n=e===1?"r":e===2?"n":e===3?"r":e===4?"t":"ĆØ";if(t==="w"||t==="W")n="a";return e+n},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"ą©§",2:"ą©Ø",3:"ą©©",4:"ą©Ŗ",5:"ą©«",6:"ą©¬",7:"ą©­",8:"ą©®",9:"ą©Æ",0:"ą©¦"},n={"ą©§":"1","ą©Ø":"2","ą©©":"3","ą©Ŗ":"4","ą©«":"5","ą©¬":"6","ą©­":"7","ą©®":"8","ą©Æ":"9","ą©¦":"0"},a;e.defineLocale("pa-in",{months:"ąØœąØØąØµąØ°ą©€_ąØ«ąØ¼ąØ°ąØµąØ°ą©€_ąØ®ąØ¾ąØ°ąØš_ąØ…ąØŖą©ąØ°ą©ˆąØ²_ąØ®ąØˆ_ąØœą©‚ąØØ_ąØœą©ąØ²ąØ¾ąØˆ_ąØ…ąØ—ąØøąØ¤_ąØøąØ¤ą©°ąØ¬ąØ°_ąØ…ąØ•ąØ¤ą©‚ąØ¬ąØ°_ąØØąØµą©°ąØ¬ąØ°_ąØ¦ąØøą©°ąØ¬ąØ°".split("_"),monthsShort:"ąØœąØØąØµąØ°ą©€_ąØ«ąØ¼ąØ°ąØµąØ°ą©€_ąØ®ąØ¾ąØ°ąØš_ąØ…ąØŖą©ąØ°ą©ˆąØ²_ąØ®ąØˆ_ąØœą©‚ąØØ_ąØœą©ąØ²ąØ¾ąØˆ_ąØ…ąØ—ąØøąØ¤_ąØøąØ¤ą©°ąØ¬ąØ°_ąØ…ąØ•ąØ¤ą©‚ąØ¬ąØ°_ąØØąØµą©°ąØ¬ąØ°_ąØ¦ąØøą©°ąØ¬ąØ°".split("_"),weekdays:"ąØąØ¤ąØµąØ¾ąØ°_ąØøą©‹ąØ®ąØµąØ¾ąØ°_ąØ®ą©°ąØ—ąØ²ąØµąØ¾ąØ°_ąØ¬ą©ąØ§ąØµąØ¾ąØ°_ąØµą©€ąØ°ąØµąØ¾ąØ°_ąØøąØ¼ą©ą©±ąØ•ąØ°ąØµąØ¾ąØ°_ąØøąØ¼ąØØą©€ąØšąØ°ąØµąØ¾ąØ°".split("_"),weekdaysShort:"ąØąØ¤_ąØøą©‹ąØ®_ąØ®ą©°ąØ—ąØ²_ąØ¬ą©ąØ§_ąØµą©€ąØ°_ąØøąØ¼ą©ąØ•ąØ°_ąØøąØ¼ąØØą©€".split("_"),weekdaysMin:"ąØąØ¤_ąØøą©‹ąØ®_ąØ®ą©°ąØ—ąØ²_ąØ¬ą©ąØ§_ąØµą©€ąØ°_ąØøąØ¼ą©ąØ•ąØ°_ąØøąØ¼ąØØą©€".split("_"),longDateFormat:{LT:"A h:mm ąØµąØœą©‡",LTS:"A h:mm:ss ąØµąØœą©‡",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ąØµąØœą©‡",LLLL:"dddd, D MMMM YYYY, A h:mm ąØµąØœą©‡"},calendar:{sameDay:"[ąØ…ąØœ] LT",nextDay:"[ąØ•ąØ²] LT",nextWeek:"[ąØ…ąØ—ąØ²ąØ¾] dddd, LT",lastDay:"[ąØ•ąØ²] LT",lastWeek:"[ąØŖąØæąØ›ąØ²ą©‡] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ąØµąØæą©±ąØš",past:"%s ąØŖąØæąØ›ąØ²ą©‡",s:"ąØ•ą©ąØ ąØøąØ•ąØæą©°ąØŸ",ss:"%d ąØøąØ•ąØæą©°ąØŸ",m:"ąØ‡ąØ• ąØ®ąØæą©°ąØŸ",mm:"%d ąØ®ąØæą©°ąØŸ",h:"ąØ‡ą©±ąØ• ąØ˜ą©°ąØŸąØ¾",hh:"%d ąØ˜ą©°ąØŸą©‡",d:"ąØ‡ą©±ąØ• ąØ¦ąØæąØØ",dd:"%d ąØ¦ąØæąØØ",M:"ąØ‡ą©±ąØ• ąØ®ąØ¹ą©€ąØØąØ¾",MM:"%d ąØ®ąØ¹ą©€ąØØą©‡",y:"ąØ‡ą©±ąØ• ąØøąØ¾ąØ²",yy:"%d ąØøąØ¾ąØ²"},preparse:function(e){return e.replace(/[ą©§ą©Øą©©ą©Ŗą©«ą©¬ą©­ą©®ą©Æą©¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ąØ°ąØ¾ąØ¤|ąØøąØµą©‡ąØ°|ąØ¦ą©ąØŖąØ¹ąØæąØ°|ąØøąØ¼ąØ¾ąØ®/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ąØ°ąØ¾ąØ¤")return e<4?e:e+12;else if(t==="ąØøąØµą©‡ąØ°")return e;else if(t==="ąØ¦ą©ąØŖąØ¹ąØæąØ°")return e>=10?e:e+12;else if(t==="ąØøąØ¼ąØ¾ąØ®")return e+12},meridiem:function(e,t,n){if(e<4)return"ąØ°ąØ¾ąØ¤";else if(e<10)return"ąØøąØµą©‡ąØ°";else if(e<17)return"ąØ¦ą©ąØŖąØ¹ąØæąØ°";else if(e<20)return"ąØøąØ¼ąØ¾ąØ®";else return"ąØ°ąØ¾ąØ¤"},week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"ą©§",2:"ą©Ø",3:"ą©©",4:"ą©Ŗ",5:"ą©«",6:"ą©¬",7:"ą©­",8:"ą©®",9:"ą©Æ",0:"ą©¦"},n={"ą©§":"1","ą©Ø":"2","ą©©":"3","ą©Ŗ":"4","ą©«":"5","ą©¬":"6","ą©­":"7","ą©®":"8","ą©Æ":"9","ą©¦":"0"},a;e.defineLocale("pa-in",{months:"ąØœąØØąØµąØ°ą©€_ąØ«ąØ¼ąØ°ąØµąØ°ą©€_ąØ®ąØ¾ąØ°ąØš_ąØ…ąØŖą©ąØ°ą©ˆąØ²_ąØ®ąØˆ_ąØœą©‚ąØØ_ąØœą©ąØ²ąØ¾ąØˆ_ąØ…ąØ—ąØøąØ¤_ąØøąØ¤ą©°ąØ¬ąØ°_ąØ…ąØ•ąØ¤ą©‚ąØ¬ąØ°_ąØØąØµą©°ąØ¬ąØ°_ąØ¦ąØøą©°ąØ¬ąØ°".split("_"),monthsShort:"ąØœąØØąØµąØ°ą©€_ąØ«ąØ¼ąØ°ąØµąØ°ą©€_ąØ®ąØ¾ąØ°ąØš_ąØ…ąØŖą©ąØ°ą©ˆąØ²_ąØ®ąØˆ_ąØœą©‚ąØØ_ąØœą©ąØ²ąØ¾ąØˆ_ąØ…ąØ—ąØøąØ¤_ąØøąØ¤ą©°ąØ¬ąØ°_ąØ…ąØ•ąØ¤ą©‚ąØ¬ąØ°_ąØØąØµą©°ąØ¬ąØ°_ąØ¦ąØøą©°ąØ¬ąØ°".split("_"),weekdays:"ąØąØ¤ąØµąØ¾ąØ°_ąØøą©‹ąØ®ąØµąØ¾ąØ°_ąØ®ą©°ąØ—ąØ²ąØµąØ¾ąØ°_ąØ¬ą©ąØ§ąØµąØ¾ąØ°_ąØµą©€ąØ°ąØµąØ¾ąØ°_ąØøąØ¼ą©ą©±ąØ•ąØ°ąØµąØ¾ąØ°_ąØøąØ¼ąØØą©€ąØšąØ°ąØµąØ¾ąØ°".split("_"),weekdaysShort:"ąØąØ¤_ąØøą©‹ąØ®_ąØ®ą©°ąØ—ąØ²_ąØ¬ą©ąØ§_ąØµą©€ąØ°_ąØøąØ¼ą©ąØ•ąØ°_ąØøąØ¼ąØØą©€".split("_"),weekdaysMin:"ąØąØ¤_ąØøą©‹ąØ®_ąØ®ą©°ąØ—ąØ²_ąØ¬ą©ąØ§_ąØµą©€ąØ°_ąØøąØ¼ą©ąØ•ąØ°_ąØøąØ¼ąØØą©€".split("_"),longDateFormat:{LT:"A h:mm ąØµąØœą©‡",LTS:"A h:mm:ss ąØµąØœą©‡",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ąØµąØœą©‡",LLLL:"dddd, D MMMM YYYY, A h:mm ąØµąØœą©‡"},calendar:{sameDay:"[ąØ…ąØœ] LT",nextDay:"[ąØ•ąØ²] LT",nextWeek:"[ąØ…ąØ—ąØ²ąØ¾] dddd, LT",lastDay:"[ąØ•ąØ²] LT",lastWeek:"[ąØŖąØæąØ›ąØ²ą©‡] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ąØµąØæą©±ąØš",past:"%s ąØŖąØæąØ›ąØ²ą©‡",s:"ąØ•ą©ąØ ąØøąØ•ąØæą©°ąØŸ",ss:"%d ąØøąØ•ąØæą©°ąØŸ",m:"ąØ‡ąØ• ąØ®ąØæą©°ąØŸ",mm:"%d ąØ®ąØæą©°ąØŸ",h:"ąØ‡ą©±ąØ• ąØ˜ą©°ąØŸąØ¾",hh:"%d ąØ˜ą©°ąØŸą©‡",d:"ąØ‡ą©±ąØ• ąØ¦ąØæąØØ",dd:"%d ąØ¦ąØæąØØ",M:"ąØ‡ą©±ąØ• ąØ®ąØ¹ą©€ąØØąØ¾",MM:"%d ąØ®ąØ¹ą©€ąØØą©‡",y:"ąØ‡ą©±ąØ• ąØøąØ¾ąØ²",yy:"%d ąØøąØ¾ąØ²"},preparse:function(e){return e.replace(/[ą©§ą©Øą©©ą©Ŗą©«ą©¬ą©­ą©®ą©Æą©¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ąØ°ąØ¾ąØ¤|ąØøąØµą©‡ąØ°|ąØ¦ą©ąØŖąØ¹ąØæąØ°|ąØøąØ¼ąØ¾ąØ®/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ąØ°ąØ¾ąØ¤")return e<4?e:e+12;else if(t==="ąØøąØµą©‡ąØ°")return e;else if(t==="ąØ¦ą©ąØŖąØ¹ąØæąØ°")return e>=10?e:e+12;else if(t==="ąØøąØ¼ąØ¾ąØ®")return e+12},meridiem:function(e,t,n){if(e<4)return"ąØ°ąØ¾ąØ¤";else if(e<10)return"ąØøąØµą©‡ąØ°";else if(e<17)return"ąØ¦ą©ąØŖąØ¹ąØæąØ°";else if(e<20)return"ąØøąØ¼ąØ¾ąØ®";else return"ąØ°ąØ¾ąØ¤"},week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var n="styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_paÅŗdziernik_listopad_grudzień".split("_"),a="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_paÅŗdziernika_listopada_grudnia".split("_"),t=[/^sty/i,/^lut/i,/^mar/i,/^kwi/i,/^maj/i,/^cze/i,/^lip/i,/^sie/i,/^wrz/i,/^paÅŗ/i,/^lis/i,/^gru/i],r;function o(e){return e%10<5&&e%10>1&&~~(e/10)%10!==1}function i(e,t,n){var a=e+" ";switch(n){case"ss":return a+(o(e)?"sekundy":"sekund");case"m":return t?"minuta":"minutę";case"mm":return a+(o(e)?"minuty":"minut");case"h":return t?"godzina":"godzinę";case"hh":return a+(o(e)?"godziny":"godzin");case"ww":return a+(o(e)?"tygodnie":"tygodni");case"MM":return a+(o(e)?"miesiące":"miesięcy");case"yy":return a+(o(e)?"lata":"lat")}}e.defineLocale("pl",{months:function(e,t){if(!e)return n;else if(/D MMMM/.test(t))return a[e.month()];else return n[e.month()]},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paÅŗ_lis_gru".split("_"),monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),weekdaysShort:"ndz_pon_wt_śr_czw_pt_sob".split("_"),weekdaysMin:"Nd_Pn_Wt_Śr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Dziś o] LT",nextDay:"[Jutro o] LT",nextWeek:function(){switch(this.day()){case 0:return"[W niedzielę o] LT";case 2:return"[We wtorek o] LT";case 3:return"[W środę o] LT";case 6:return"[W sobotę o] LT";default:return"[W] dddd [o] LT"}},lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielę o] LT";case 3:return"[W zeszłą środę o] LT";case 6:return"[W zeszłą sobotę o] LT";default:return"[W zeszły] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",ss:i,m:i,mm:i,h:i,hh:i,d:"1 dzień",dd:"%d dni",w:"tydzień",ww:i,M:"miesiąc",MM:i,y:"rok",yy:i},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var n="styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_paÅŗdziernik_listopad_grudzień".split("_"),a="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_paÅŗdziernika_listopada_grudnia".split("_"),t=[/^sty/i,/^lut/i,/^mar/i,/^kwi/i,/^maj/i,/^cze/i,/^lip/i,/^sie/i,/^wrz/i,/^paÅŗ/i,/^lis/i,/^gru/i],r;function o(e){return e%10<5&&e%10>1&&~~(e/10)%10!==1}function i(e,t,n){var a=e+" ";switch(n){case"ss":return a+(o(e)?"sekundy":"sekund");case"m":return t?"minuta":"minutę";case"mm":return a+(o(e)?"minuty":"minut");case"h":return t?"godzina":"godzinę";case"hh":return a+(o(e)?"godziny":"godzin");case"ww":return a+(o(e)?"tygodnie":"tygodni");case"MM":return a+(o(e)?"miesiące":"miesięcy");case"yy":return a+(o(e)?"lata":"lat")}}e.defineLocale("pl",{months:function(e,t){if(!e)return n;else if(/D MMMM/.test(t))return a[e.month()];else return n[e.month()]},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paÅŗ_lis_gru".split("_"),monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),weekdaysShort:"ndz_pon_wt_śr_czw_pt_sob".split("_"),weekdaysMin:"Nd_Pn_Wt_Śr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Dziś o] LT",nextDay:"[Jutro o] LT",nextWeek:function(){switch(this.day()){case 0:return"[W niedzielę o] LT";case 2:return"[We wtorek o] LT";case 3:return"[W środę o] LT";case 6:return"[W sobotę o] LT";default:return"[W] dddd [o] LT"}},lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielę o] LT";case 3:return"[W zeszłą środę o] LT";case 6:return"[W zeszłą sobotę o] LT";default:return"[W zeszły] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",ss:i,m:i,mm:i,h:i,hh:i,d:"1 dzień",dd:"%d dni",w:"tydzień",ww:i,M:"miesiąc",MM:i,y:"rok",yy:i},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("pt",{months:"janeiro_fevereiro_marƧo_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"Domingo_Segunda-feira_TerƧa-feira_Quarta-feira_Quinta-feira_Sexta-feira_SĆ”bado".split("_"),weekdaysShort:"Dom_Seg_Ter_Qua_Qui_Sex_SĆ”b".split("_"),weekdaysMin:"Do_2ĀŖ_3ĀŖ_4ĀŖ_5ĀŖ_6ĀŖ_SĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY HH:mm",LLLL:"dddd, D [de] MMMM [de] YYYY HH:mm"},calendar:{sameDay:"[Hoje Ć s] LT",nextDay:"[AmanhĆ£ Ć s] LT",nextWeek:"dddd [Ć s] LT",lastDay:"[Ontem Ć s] LT",lastWeek:function(){return this.day()===0||this.day()===6?"[ƚltimo] dddd [Ć s] LT":"[ƚltima] dddd [Ć s] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"hĆ” %s",s:"segundos",ss:"%d segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",w:"uma semana",ww:"%d semanas",M:"um mĆŖs",MM:"%d meses",y:"um ano",yy:"%d anos"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("pt",{months:"janeiro_fevereiro_marƧo_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"Domingo_Segunda-feira_TerƧa-feira_Quarta-feira_Quinta-feira_Sexta-feira_SĆ”bado".split("_"),weekdaysShort:"Dom_Seg_Ter_Qua_Qui_Sex_SĆ”b".split("_"),weekdaysMin:"Do_2ĀŖ_3ĀŖ_4ĀŖ_5ĀŖ_6ĀŖ_SĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY HH:mm",LLLL:"dddd, D [de] MMMM [de] YYYY HH:mm"},calendar:{sameDay:"[Hoje Ć s] LT",nextDay:"[AmanhĆ£ Ć s] LT",nextWeek:"dddd [Ć s] LT",lastDay:"[Ontem Ć s] LT",lastWeek:function(){return this.day()===0||this.day()===6?"[ƚltimo] dddd [Ć s] LT":"[ƚltima] dddd [Ć s] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"hĆ” %s",s:"segundos",ss:"%d segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",w:"uma semana",ww:"%d semanas",M:"um mĆŖs",MM:"%d meses",y:"um ano",yy:"%d anos"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("pt-br",{months:"janeiro_fevereiro_marƧo_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"domingo_segunda-feira_terƧa-feira_quarta-feira_quinta-feira_sexta-feira_sĆ”bado".split("_"),weekdaysShort:"dom_seg_ter_qua_qui_sex_sĆ”b".split("_"),weekdaysMin:"do_2ĀŖ_3ĀŖ_4ĀŖ_5ĀŖ_6ĀŖ_sĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY [Ć s] HH:mm",LLLL:"dddd, D [de] MMMM [de] YYYY [Ć s] HH:mm"},calendar:{sameDay:"[Hoje Ć s] LT",nextDay:"[AmanhĆ£ Ć s] LT",nextWeek:"dddd [Ć s] LT",lastDay:"[Ontem Ć s] LT",lastWeek:function(){return this.day()===0||this.day()===6?"[ƚltimo] dddd [Ć s] LT":"[ƚltima] dddd [Ć s] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"hĆ” %s",s:"poucos segundos",ss:"%d segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mĆŖs",MM:"%d meses",y:"um ano",yy:"%d anos"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",invalidDate:"Data invĆ”lida"})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("pt-br",{months:"janeiro_fevereiro_marƧo_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"domingo_segunda-feira_terƧa-feira_quarta-feira_quinta-feira_sexta-feira_sĆ”bado".split("_"),weekdaysShort:"dom_seg_ter_qua_qui_sex_sĆ”b".split("_"),weekdaysMin:"do_2ĀŖ_3ĀŖ_4ĀŖ_5ĀŖ_6ĀŖ_sĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY [Ć s] HH:mm",LLLL:"dddd, D [de] MMMM [de] YYYY [Ć s] HH:mm"},calendar:{sameDay:"[Hoje Ć s] LT",nextDay:"[AmanhĆ£ Ć s] LT",nextWeek:"dddd [Ć s] LT",lastDay:"[Ontem Ć s] LT",lastWeek:function(){return this.day()===0||this.day()===6?"[ƚltimo] dddd [Ć s] LT":"[ƚltima] dddd [Ć s] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"hĆ” %s",s:"poucos segundos",ss:"%d segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mĆŖs",MM:"%d meses",y:"um ano",yy:"%d anos"},dayOfMonthOrdinalParse:/\d{1,2}Āŗ/,ordinal:"%dĀŗ",invalidDate:"Data invĆ”lida"})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function t(e,t,n){var a={ss:"secunde",mm:"minute",hh:"ore",dd:"zile",ww:"săptămĆ¢ni",MM:"luni",yy:"ani"},r=" ";if(e%100>=20||e>=100&&e%100===0)r=" de ";return e+r+a[n]}var n;e.defineLocale("ro",{months:"ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie".split("_"),monthsShort:"ian._feb._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.".split("_"),monthsParseExact:true,weekdays:"duminică_luni_marți_miercuri_joi_vineri_sĆ¢mbătă".split("_"),weekdaysShort:"Dum_Lun_Mar_Mie_Joi_Vin_SĆ¢m".split("_"),weekdaysMin:"Du_Lu_Ma_Mi_Jo_Vi_SĆ¢".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[azi la] LT",nextDay:"[mĆ¢ine la] LT",nextWeek:"dddd [la] LT",lastDay:"[ieri la] LT",lastWeek:"[fosta] dddd [la] LT",sameElse:"L"},relativeTime:{future:"peste %s",past:"%s Ć®n urmă",s:"cĆ¢teva secunde",ss:t,m:"un minut",mm:t,h:"o oră",hh:t,d:"o zi",dd:t,w:"o săptămĆ¢nă",ww:t,M:"o lună",MM:t,y:"un an",yy:t},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function t(e,t,n){var a={ss:"secunde",mm:"minute",hh:"ore",dd:"zile",ww:"săptămĆ¢ni",MM:"luni",yy:"ani"},r=" ";if(e%100>=20||e>=100&&e%100===0)r=" de ";return e+r+a[n]}var n;e.defineLocale("ro",{months:"ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie".split("_"),monthsShort:"ian._feb._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.".split("_"),monthsParseExact:true,weekdays:"duminică_luni_marți_miercuri_joi_vineri_sĆ¢mbătă".split("_"),weekdaysShort:"Dum_Lun_Mar_Mie_Joi_Vin_SĆ¢m".split("_"),weekdaysMin:"Du_Lu_Ma_Mi_Jo_Vi_SĆ¢".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[azi la] LT",nextDay:"[mĆ¢ine la] LT",nextWeek:"dddd [la] LT",lastDay:"[ieri la] LT",lastWeek:"[fosta] dddd [la] LT",sameElse:"L"},relativeTime:{future:"peste %s",past:"%s Ć®n urmă",s:"cĆ¢teva secunde",ss:t,m:"un minut",mm:t,h:"o oră",hh:t,d:"o zi",dd:t,w:"o săptămĆ¢nă",ww:t,M:"o lună",MM:t,y:"un an",yy:t},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function r(e,t){var n=e.split("_");return t%10===1&&t%100!==11?n[0]:t%10>=2&&t%10<=4&&(t%100<10||t%100>=20)?n[1]:n[2]}function t(e,t,n){var a={ss:t?"сŠµŠŗуŠ½Š“Š°_сŠµŠŗуŠ½Š“ы_сŠµŠŗуŠ½Š“":"сŠµŠŗуŠ½Š“у_сŠµŠŗуŠ½Š“ы_сŠµŠŗуŠ½Š“",mm:t?"Š¼ŠøŠ½ŃƒŃ‚Š°_Š¼ŠøŠ½ŃƒŃ‚Ń‹_Š¼ŠøŠ½ŃƒŃ‚":"Š¼ŠøŠ½ŃƒŃ‚Ńƒ_Š¼ŠøŠ½ŃƒŃ‚Ń‹_Š¼ŠøŠ½ŃƒŃ‚",hh:"чŠ°Ń_чŠ°ŃŠ°_чŠ°ŃŠ¾Š²",dd:"Š“ŠµŠ½ŃŒ_Š“Š½Ń_Š“Š½ŠµŠ¹",ww:"Š½ŠµŠ“ŠµŠ»Ń_Š½ŠµŠ“ŠµŠ»Šø_Š½ŠµŠ“ŠµŠ»ŃŒ",MM:"Š¼ŠµŃŃŃ†_Š¼ŠµŃŃŃ†Š°_Š¼ŠµŃŃŃ†ŠµŠ²",yy:"Š³Š¾Š“_Š³Š¾Š“Š°_Š»ŠµŃ‚"};if(n==="m")return t?"Š¼ŠøŠ½ŃƒŃ‚Š°":"Š¼ŠøŠ½ŃƒŃ‚Ńƒ";else return e+" "+r(a[n],+e)}var n=[/^яŠ½Š²/i,/^фŠµŠ²/i,/^Š¼Š°Ń€/i,/^Š°Šæр/i,/^Š¼Š°[Š¹Ń]/i,/^ŠøюŠ½/i,/^ŠøюŠ»/i,/^Š°Š²Š³/i,/^сŠµŠ½/i,/^Š¾Šŗт/i,/^Š½Š¾Ń/i,/^Š“ŠµŠŗ/i],a;e.defineLocale("ru",{months:{format:"яŠ½Š²Š°Ń€Ń_фŠµŠ²Ń€Š°Š»Ń_Š¼Š°Ń€Ń‚Š°_Š°ŠæрŠµŠ»Ń_Š¼Š°Ń_ŠøюŠ½Ń_ŠøюŠ»Ń_Š°Š²Š³ŃƒŃŃ‚Š°_сŠµŠ½Ń‚яŠ±Ń€Ń_Š¾ŠŗтяŠ±Ń€Ń_Š½Š¾ŃŠ±Ń€Ń_Š“ŠµŠŗŠ°Š±Ń€Ń".split("_"),standalone:"яŠ½Š²Š°Ń€ŃŒ_фŠµŠ²Ń€Š°Š»ŃŒ_Š¼Š°Ń€Ń‚_Š°ŠæрŠµŠ»ŃŒ_Š¼Š°Š¹_ŠøюŠ½ŃŒ_ŠøюŠ»ŃŒ_Š°Š²Š³ŃƒŃŃ‚_сŠµŠ½Ń‚яŠ±Ń€ŃŒ_Š¾ŠŗтяŠ±Ń€ŃŒ_Š½Š¾ŃŠ±Ń€ŃŒ_Š“ŠµŠŗŠ°Š±Ń€ŃŒ".split("_")},monthsShort:{format:"яŠ½Š²._фŠµŠ²Ń€._Š¼Š°Ń€._Š°Šæр._Š¼Š°Ń_ŠøюŠ½Ń_ŠøюŠ»Ń_Š°Š²Š³._сŠµŠ½Ń‚._Š¾Šŗт._Š½Š¾ŃŠ±._Š“ŠµŠŗ.".split("_"),standalone:"яŠ½Š²._фŠµŠ²Ń€._Š¼Š°Ń€Ń‚_Š°Šæр._Š¼Š°Š¹_ŠøюŠ½ŃŒ_ŠøюŠ»ŃŒ_Š°Š²Š³._сŠµŠ½Ń‚._Š¾Šŗт._Š½Š¾ŃŠ±._Š“ŠµŠŗ.".split("_")},weekdays:{standalone:"Š²Š¾ŃŠŗрŠµŃŠµŠ½ŃŒŠµ_ŠæŠ¾Š½ŠµŠ“ŠµŠ»ŃŒŠ½ŠøŠŗ_Š²Ń‚Š¾Ń€Š½ŠøŠŗ_срŠµŠ“Š°_чŠµŃ‚Š²ŠµŃ€Š³_ŠæятŠ½ŠøцŠ°_суŠ±Š±Š¾Ń‚Š°".split("_"),format:"Š²Š¾ŃŠŗрŠµŃŠµŠ½ŃŒŠµ_ŠæŠ¾Š½ŠµŠ“ŠµŠ»ŃŒŠ½ŠøŠŗ_Š²Ń‚Š¾Ń€Š½ŠøŠŗ_срŠµŠ“у_чŠµŃ‚Š²ŠµŃ€Š³_ŠæятŠ½Šøцу_суŠ±Š±Š¾Ń‚Ńƒ".split("_"),isFormat:/\[ ?[Š’Š²] ?(?:ŠæрŠ¾ŃˆŠ»ŃƒŃŽ|сŠ»ŠµŠ“ующую|эту)? ?] ?dddd/},weekdaysShort:"Š²Ń_ŠæŠ½_Š²Ń‚_ср_чт_Šæт_сŠ±".split("_"),weekdaysMin:"Š²Ń_ŠæŠ½_Š²Ń‚_ср_чт_Šæт_сŠ±".split("_"),monthsParse:n,longMonthsParse:n,shortMonthsParse:n,monthsRegex:/^(яŠ½Š²Š°Ń€[ья]|яŠ½Š²\.?|фŠµŠ²Ń€Š°Š»[ья]|фŠµŠ²Ń€?\.?|Š¼Š°Ń€Ń‚Š°?|Š¼Š°Ń€\.?|Š°ŠæрŠµŠ»[ья]|Š°Šæр\.?|Š¼Š°[Š¹Ń]|ŠøюŠ½[ья]|ŠøюŠ½\.?|ŠøюŠ»[ья]|ŠøюŠ»\.?|Š°Š²Š³ŃƒŃŃ‚Š°?|Š°Š²Š³\.?|сŠµŠ½Ń‚яŠ±Ń€[ья]|сŠµŠ½Ń‚?\.?|Š¾ŠŗтяŠ±Ń€[ья]|Š¾Šŗт\.?|Š½Š¾ŃŠ±Ń€[ья]|Š½Š¾ŃŠ±?\.?|Š“ŠµŠŗŠ°Š±Ń€[ья]|Š“ŠµŠŗ\.?)/i,monthsShortRegex:/^(яŠ½Š²Š°Ń€[ья]|яŠ½Š²\.?|фŠµŠ²Ń€Š°Š»[ья]|фŠµŠ²Ń€?\.?|Š¼Š°Ń€Ń‚Š°?|Š¼Š°Ń€\.?|Š°ŠæрŠµŠ»[ья]|Š°Šæр\.?|Š¼Š°[Š¹Ń]|ŠøюŠ½[ья]|ŠøюŠ½\.?|ŠøюŠ»[ья]|ŠøюŠ»\.?|Š°Š²Š³ŃƒŃŃ‚Š°?|Š°Š²Š³\.?|сŠµŠ½Ń‚яŠ±Ń€[ья]|сŠµŠ½Ń‚?\.?|Š¾ŠŗтяŠ±Ń€[ья]|Š¾Šŗт\.?|Š½Š¾ŃŠ±Ń€[ья]|Š½Š¾ŃŠ±?\.?|Š“ŠµŠŗŠ°Š±Ń€[ья]|Š“ŠµŠŗ\.?)/i,monthsStrictRegex:/^(яŠ½Š²Š°Ń€[яь]|фŠµŠ²Ń€Š°Š»[яь]|Š¼Š°Ń€Ń‚Š°?|Š°ŠæрŠµŠ»[яь]|Š¼Š°[яŠ¹]|ŠøюŠ½[яь]|ŠøюŠ»[яь]|Š°Š²Š³ŃƒŃŃ‚Š°?|сŠµŠ½Ń‚яŠ±Ń€[яь]|Š¾ŠŗтяŠ±Ń€[яь]|Š½Š¾ŃŠ±Ń€[яь]|Š“ŠµŠŗŠ°Š±Ń€[яь])/i,monthsShortStrictRegex:/^(яŠ½Š²\.|фŠµŠ²Ń€?\.|Š¼Š°Ń€[т.]|Š°Šæр\.|Š¼Š°[яŠ¹]|ŠøюŠ½[ья.]|ŠøюŠ»[ья.]|Š°Š²Š³\.|сŠµŠ½Ń‚?\.|Š¾Šŗт\.|Š½Š¾ŃŠ±?\.|Š“ŠµŠŗ\.)/i,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY Š³.",LLL:"D MMMM YYYY Š³., H:mm",LLLL:"dddd, D MMMM YYYY Š³., H:mm"},calendar:{sameDay:"[Š”ŠµŠ³Š¾Š“Š½Ń, Š²] LT",nextDay:"[Š—Š°Š²Ń‚Ń€Š°, Š²] LT",lastDay:"[Š’чŠµŃ€Š°, Š²] LT",nextWeek:function(e){if(e.week()!==this.week())switch(this.day()){case 0:return"[Š’ сŠ»ŠµŠ“ующŠµŠµ] dddd, [Š²] LT";case 1:case 2:case 4:return"[Š’ сŠ»ŠµŠ“ующŠøŠ¹] dddd, [Š²] LT";case 3:case 5:case 6:return"[Š’ сŠ»ŠµŠ“ующую] dddd, [Š²] LT"}else if(this.day()===2)return"[Š’Š¾] dddd, [Š²] LT";else return"[Š’] dddd, [Š²] LT"},lastWeek:function(e){if(e.week()!==this.week())switch(this.day()){case 0:return"[Š’ ŠæрŠ¾ŃˆŠ»Š¾Šµ] dddd, [Š²] LT";case 1:case 2:case 4:return"[Š’ ŠæрŠ¾ŃˆŠ»Ń‹Š¹] dddd, [Š²] LT";case 3:case 5:case 6:return"[Š’ ŠæрŠ¾ŃˆŠ»ŃƒŃŽ] dddd, [Š²] LT"}else if(this.day()===2)return"[Š’Š¾] dddd, [Š²] LT";else return"[Š’] dddd, [Š²] LT"},sameElse:"L"},relativeTime:{future:"чŠµŃ€ŠµŠ· %s",past:"%s Š½Š°Š·Š°Š“",s:"Š½ŠµŃŠŗŠ¾Š»ŃŒŠŗŠ¾ сŠµŠŗуŠ½Š“",ss:t,m:t,mm:t,h:"чŠ°Ń",hh:t,d:"Š“ŠµŠ½ŃŒ",dd:t,w:"Š½ŠµŠ“ŠµŠ»Ń",ww:t,M:"Š¼ŠµŃŃŃ†",MM:t,y:"Š³Š¾Š“",yy:t},meridiemParse:/Š½Š¾Ń‡Šø|утрŠ°|Š“Š½Ń|Š²ŠµŃ‡ŠµŃ€Š°/i,isPM:function(e){return/^(Š“Š½Ń|Š²ŠµŃ‡ŠµŃ€Š°)$/.test(e)},meridiem:function(e,t,n){if(e<4)return"Š½Š¾Ń‡Šø";else if(e<12)return"утрŠ°";else if(e<17)return"Š“Š½Ń";else return"Š²ŠµŃ‡ŠµŃ€Š°"},dayOfMonthOrdinalParse:/\d{1,2}-(Š¹|Š³Š¾|я)/,ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":return e+"-Š¹";case"D":return e+"-Š³Š¾";case"w":case"W":return e+"-я";default:return e}},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function r(e,t){var n=e.split("_");return t%10===1&&t%100!==11?n[0]:t%10>=2&&t%10<=4&&(t%100<10||t%100>=20)?n[1]:n[2]}function t(e,t,n){var a={ss:t?"сŠµŠŗуŠ½Š“Š°_сŠµŠŗуŠ½Š“ы_сŠµŠŗуŠ½Š“":"сŠµŠŗуŠ½Š“у_сŠµŠŗуŠ½Š“ы_сŠµŠŗуŠ½Š“",mm:t?"Š¼ŠøŠ½ŃƒŃ‚Š°_Š¼ŠøŠ½ŃƒŃ‚Ń‹_Š¼ŠøŠ½ŃƒŃ‚":"Š¼ŠøŠ½ŃƒŃ‚Ńƒ_Š¼ŠøŠ½ŃƒŃ‚Ń‹_Š¼ŠøŠ½ŃƒŃ‚",hh:"чŠ°Ń_чŠ°ŃŠ°_чŠ°ŃŠ¾Š²",dd:"Š“ŠµŠ½ŃŒ_Š“Š½Ń_Š“Š½ŠµŠ¹",ww:"Š½ŠµŠ“ŠµŠ»Ń_Š½ŠµŠ“ŠµŠ»Šø_Š½ŠµŠ“ŠµŠ»ŃŒ",MM:"Š¼ŠµŃŃŃ†_Š¼ŠµŃŃŃ†Š°_Š¼ŠµŃŃŃ†ŠµŠ²",yy:"Š³Š¾Š“_Š³Š¾Š“Š°_Š»ŠµŃ‚"};if(n==="m")return t?"Š¼ŠøŠ½ŃƒŃ‚Š°":"Š¼ŠøŠ½ŃƒŃ‚Ńƒ";else return e+" "+r(a[n],+e)}var n=[/^яŠ½Š²/i,/^фŠµŠ²/i,/^Š¼Š°Ń€/i,/^Š°Šæр/i,/^Š¼Š°[Š¹Ń]/i,/^ŠøюŠ½/i,/^ŠøюŠ»/i,/^Š°Š²Š³/i,/^сŠµŠ½/i,/^Š¾Šŗт/i,/^Š½Š¾Ń/i,/^Š“ŠµŠŗ/i],a;e.defineLocale("ru",{months:{format:"яŠ½Š²Š°Ń€Ń_фŠµŠ²Ń€Š°Š»Ń_Š¼Š°Ń€Ń‚Š°_Š°ŠæрŠµŠ»Ń_Š¼Š°Ń_ŠøюŠ½Ń_ŠøюŠ»Ń_Š°Š²Š³ŃƒŃŃ‚Š°_сŠµŠ½Ń‚яŠ±Ń€Ń_Š¾ŠŗтяŠ±Ń€Ń_Š½Š¾ŃŠ±Ń€Ń_Š“ŠµŠŗŠ°Š±Ń€Ń".split("_"),standalone:"яŠ½Š²Š°Ń€ŃŒ_фŠµŠ²Ń€Š°Š»ŃŒ_Š¼Š°Ń€Ń‚_Š°ŠæрŠµŠ»ŃŒ_Š¼Š°Š¹_ŠøюŠ½ŃŒ_ŠøюŠ»ŃŒ_Š°Š²Š³ŃƒŃŃ‚_сŠµŠ½Ń‚яŠ±Ń€ŃŒ_Š¾ŠŗтяŠ±Ń€ŃŒ_Š½Š¾ŃŠ±Ń€ŃŒ_Š“ŠµŠŗŠ°Š±Ń€ŃŒ".split("_")},monthsShort:{format:"яŠ½Š²._фŠµŠ²Ń€._Š¼Š°Ń€._Š°Šæр._Š¼Š°Ń_ŠøюŠ½Ń_ŠøюŠ»Ń_Š°Š²Š³._сŠµŠ½Ń‚._Š¾Šŗт._Š½Š¾ŃŠ±._Š“ŠµŠŗ.".split("_"),standalone:"яŠ½Š²._фŠµŠ²Ń€._Š¼Š°Ń€Ń‚_Š°Šæр._Š¼Š°Š¹_ŠøюŠ½ŃŒ_ŠøюŠ»ŃŒ_Š°Š²Š³._сŠµŠ½Ń‚._Š¾Šŗт._Š½Š¾ŃŠ±._Š“ŠµŠŗ.".split("_")},weekdays:{standalone:"Š²Š¾ŃŠŗрŠµŃŠµŠ½ŃŒŠµ_ŠæŠ¾Š½ŠµŠ“ŠµŠ»ŃŒŠ½ŠøŠŗ_Š²Ń‚Š¾Ń€Š½ŠøŠŗ_срŠµŠ“Š°_чŠµŃ‚Š²ŠµŃ€Š³_ŠæятŠ½ŠøцŠ°_суŠ±Š±Š¾Ń‚Š°".split("_"),format:"Š²Š¾ŃŠŗрŠµŃŠµŠ½ŃŒŠµ_ŠæŠ¾Š½ŠµŠ“ŠµŠ»ŃŒŠ½ŠøŠŗ_Š²Ń‚Š¾Ń€Š½ŠøŠŗ_срŠµŠ“у_чŠµŃ‚Š²ŠµŃ€Š³_ŠæятŠ½Šøцу_суŠ±Š±Š¾Ń‚Ńƒ".split("_"),isFormat:/\[ ?[Š’Š²] ?(?:ŠæрŠ¾ŃˆŠ»ŃƒŃŽ|сŠ»ŠµŠ“ующую|эту)? ?] ?dddd/},weekdaysShort:"Š²Ń_ŠæŠ½_Š²Ń‚_ср_чт_Šæт_сŠ±".split("_"),weekdaysMin:"Š²Ń_ŠæŠ½_Š²Ń‚_ср_чт_Šæт_сŠ±".split("_"),monthsParse:n,longMonthsParse:n,shortMonthsParse:n,monthsRegex:/^(яŠ½Š²Š°Ń€[ья]|яŠ½Š²\.?|фŠµŠ²Ń€Š°Š»[ья]|фŠµŠ²Ń€?\.?|Š¼Š°Ń€Ń‚Š°?|Š¼Š°Ń€\.?|Š°ŠæрŠµŠ»[ья]|Š°Šæр\.?|Š¼Š°[Š¹Ń]|ŠøюŠ½[ья]|ŠøюŠ½\.?|ŠøюŠ»[ья]|ŠøюŠ»\.?|Š°Š²Š³ŃƒŃŃ‚Š°?|Š°Š²Š³\.?|сŠµŠ½Ń‚яŠ±Ń€[ья]|сŠµŠ½Ń‚?\.?|Š¾ŠŗтяŠ±Ń€[ья]|Š¾Šŗт\.?|Š½Š¾ŃŠ±Ń€[ья]|Š½Š¾ŃŠ±?\.?|Š“ŠµŠŗŠ°Š±Ń€[ья]|Š“ŠµŠŗ\.?)/i,monthsShortRegex:/^(яŠ½Š²Š°Ń€[ья]|яŠ½Š²\.?|фŠµŠ²Ń€Š°Š»[ья]|фŠµŠ²Ń€?\.?|Š¼Š°Ń€Ń‚Š°?|Š¼Š°Ń€\.?|Š°ŠæрŠµŠ»[ья]|Š°Šæр\.?|Š¼Š°[Š¹Ń]|ŠøюŠ½[ья]|ŠøюŠ½\.?|ŠøюŠ»[ья]|ŠøюŠ»\.?|Š°Š²Š³ŃƒŃŃ‚Š°?|Š°Š²Š³\.?|сŠµŠ½Ń‚яŠ±Ń€[ья]|сŠµŠ½Ń‚?\.?|Š¾ŠŗтяŠ±Ń€[ья]|Š¾Šŗт\.?|Š½Š¾ŃŠ±Ń€[ья]|Š½Š¾ŃŠ±?\.?|Š“ŠµŠŗŠ°Š±Ń€[ья]|Š“ŠµŠŗ\.?)/i,monthsStrictRegex:/^(яŠ½Š²Š°Ń€[яь]|фŠµŠ²Ń€Š°Š»[яь]|Š¼Š°Ń€Ń‚Š°?|Š°ŠæрŠµŠ»[яь]|Š¼Š°[яŠ¹]|ŠøюŠ½[яь]|ŠøюŠ»[яь]|Š°Š²Š³ŃƒŃŃ‚Š°?|сŠµŠ½Ń‚яŠ±Ń€[яь]|Š¾ŠŗтяŠ±Ń€[яь]|Š½Š¾ŃŠ±Ń€[яь]|Š“ŠµŠŗŠ°Š±Ń€[яь])/i,monthsShortStrictRegex:/^(яŠ½Š²\.|фŠµŠ²Ń€?\.|Š¼Š°Ń€[т.]|Š°Šæр\.|Š¼Š°[яŠ¹]|ŠøюŠ½[ья.]|ŠøюŠ»[ья.]|Š°Š²Š³\.|сŠµŠ½Ń‚?\.|Š¾Šŗт\.|Š½Š¾ŃŠ±?\.|Š“ŠµŠŗ\.)/i,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY Š³.",LLL:"D MMMM YYYY Š³., H:mm",LLLL:"dddd, D MMMM YYYY Š³., H:mm"},calendar:{sameDay:"[Š”ŠµŠ³Š¾Š“Š½Ń, Š²] LT",nextDay:"[Š—Š°Š²Ń‚Ń€Š°, Š²] LT",lastDay:"[Š’чŠµŃ€Š°, Š²] LT",nextWeek:function(e){if(e.week()!==this.week())switch(this.day()){case 0:return"[Š’ сŠ»ŠµŠ“ующŠµŠµ] dddd, [Š²] LT";case 1:case 2:case 4:return"[Š’ сŠ»ŠµŠ“ующŠøŠ¹] dddd, [Š²] LT";case 3:case 5:case 6:return"[Š’ сŠ»ŠµŠ“ующую] dddd, [Š²] LT"}else if(this.day()===2)return"[Š’Š¾] dddd, [Š²] LT";else return"[Š’] dddd, [Š²] LT"},lastWeek:function(e){if(e.week()!==this.week())switch(this.day()){case 0:return"[Š’ ŠæрŠ¾ŃˆŠ»Š¾Šµ] dddd, [Š²] LT";case 1:case 2:case 4:return"[Š’ ŠæрŠ¾ŃˆŠ»Ń‹Š¹] dddd, [Š²] LT";case 3:case 5:case 6:return"[Š’ ŠæрŠ¾ŃˆŠ»ŃƒŃŽ] dddd, [Š²] LT"}else if(this.day()===2)return"[Š’Š¾] dddd, [Š²] LT";else return"[Š’] dddd, [Š²] LT"},sameElse:"L"},relativeTime:{future:"чŠµŃ€ŠµŠ· %s",past:"%s Š½Š°Š·Š°Š“",s:"Š½ŠµŃŠŗŠ¾Š»ŃŒŠŗŠ¾ сŠµŠŗуŠ½Š“",ss:t,m:t,mm:t,h:"чŠ°Ń",hh:t,d:"Š“ŠµŠ½ŃŒ",dd:t,w:"Š½ŠµŠ“ŠµŠ»Ń",ww:t,M:"Š¼ŠµŃŃŃ†",MM:t,y:"Š³Š¾Š“",yy:t},meridiemParse:/Š½Š¾Ń‡Šø|утрŠ°|Š“Š½Ń|Š²ŠµŃ‡ŠµŃ€Š°/i,isPM:function(e){return/^(Š“Š½Ń|Š²ŠµŃ‡ŠµŃ€Š°)$/.test(e)},meridiem:function(e,t,n){if(e<4)return"Š½Š¾Ń‡Šø";else if(e<12)return"утрŠ°";else if(e<17)return"Š“Š½Ń";else return"Š²ŠµŃ‡ŠµŃ€Š°"},dayOfMonthOrdinalParse:/\d{1,2}-(Š¹|Š³Š¾|я)/,ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":return e+"-Š¹";case"D":return e+"-Š³Š¾";case"w":case"W":return e+"-я";default:return e}},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t=["Ų¬Ł†ŁˆŲ±ŁŠ","ŁŁŠŲØŲ±ŁˆŲ±ŁŠ","Ł…Ų§Ų±Ś†","Ų§Ł¾Ų±ŁŠŁ„","Ł…Ų¦ŁŠ","Ų¬ŁˆŁ†","Ų¬ŁˆŁ„Ų§Ų”Ł","Ų¢ŚÆŲ³Ł½","Ų³ŁŠŁ¾Ł½Ł…ŲØŲ±","Ų¢ŚŖŁ½ŁˆŲØŲ±","Ł†ŁˆŁ…ŲØŲ±","ŚŠŲ³Ł…ŲØŲ±"],n=["Ų¢Ś†Ų±","Ų³ŁˆŁ…Ų±","Ų§Ś±Ų§Ų±Łˆ","Ų§Ų±ŲØŲ¹","Ų®Ł…ŁŠŲ³","Ų¬Ł…Ų¹","Ś‡Ł†Ś‡Ų±"],a;e.defineLocale("sd",{months:t,monthsShort:t,weekdays:n,weekdaysShort:n,weekdaysMin:n,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"ddddŲŒ D MMMM YYYY HH:mm"},meridiemParse:/ŲµŲØŲ­|Ų“Ų§Ł…/,isPM:function(e){return"Ų“Ų§Ł…"===e},meridiem:function(e,t,n){if(e<12)return"ŲµŲØŲ­";return"Ų“Ų§Ł…"},calendar:{sameDay:"[Ų§Ś„] LT",nextDay:"[Ų³Ś€Ų§Ś»ŁŠ] LT",nextWeek:"dddd [Ų§Ś³ŁŠŁ† Ł‡ŁŲŖŁŠ ŲŖŁŠ] LT",lastDay:"[ŚŖŲ§Ł„Ł‡Ł‡] LT",lastWeek:"[ŚÆŲ²Ų±ŁŠŁ„ Ł‡ŁŲŖŁŠ] dddd [ŲŖŁŠ] LT",sameElse:"L"},relativeTime:{future:"%s Ł¾ŁˆŲ”",past:"%s Ų§Ś³",s:"Ś†Ł†ŲÆ Ų³ŁŠŚŖŁ†ŚŠ",ss:"%d Ų³ŁŠŚŖŁ†ŚŠ",m:"Ł‡ŚŖ Ł…Ł†Ł½",mm:"%d Ł…Ł†Ł½",h:"Ł‡ŚŖ ŚŖŁ„Ų§ŚŖ",hh:"%d ŚŖŁ„Ų§ŚŖ",d:"Ł‡ŚŖ ŚŁŠŁ†Ł‡Ł†",dd:"%d ŚŁŠŁ†Ł‡Ł†",M:"Ł‡ŚŖ Ł…Ł‡ŁŠŁ†Łˆ",MM:"%d Ł…Ł‡ŁŠŁ†Ų§",y:"Ł‡ŚŖ Ų³Ų§Ł„",yy:"%d Ų³Ų§Ł„"},preparse:function(e){return e.replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/,/g,"ŲŒ")},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t=["Ų¬Ł†ŁˆŲ±ŁŠ","ŁŁŠŲØŲ±ŁˆŲ±ŁŠ","Ł…Ų§Ų±Ś†","Ų§Ł¾Ų±ŁŠŁ„","Ł…Ų¦ŁŠ","Ų¬ŁˆŁ†","Ų¬ŁˆŁ„Ų§Ų”Ł","Ų¢ŚÆŲ³Ł½","Ų³ŁŠŁ¾Ł½Ł…ŲØŲ±","Ų¢ŚŖŁ½ŁˆŲØŲ±","Ł†ŁˆŁ…ŲØŲ±","ŚŠŲ³Ł…ŲØŲ±"],n=["Ų¢Ś†Ų±","Ų³ŁˆŁ…Ų±","Ų§Ś±Ų§Ų±Łˆ","Ų§Ų±ŲØŲ¹","Ų®Ł…ŁŠŲ³","Ų¬Ł…Ų¹","Ś‡Ł†Ś‡Ų±"],a;e.defineLocale("sd",{months:t,monthsShort:t,weekdays:n,weekdaysShort:n,weekdaysMin:n,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"ddddŲŒ D MMMM YYYY HH:mm"},meridiemParse:/ŲµŲØŲ­|Ų“Ų§Ł…/,isPM:function(e){return"Ų“Ų§Ł…"===e},meridiem:function(e,t,n){if(e<12)return"ŲµŲØŲ­";return"Ų“Ų§Ł…"},calendar:{sameDay:"[Ų§Ś„] LT",nextDay:"[Ų³Ś€Ų§Ś»ŁŠ] LT",nextWeek:"dddd [Ų§Ś³ŁŠŁ† Ł‡ŁŲŖŁŠ ŲŖŁŠ] LT",lastDay:"[ŚŖŲ§Ł„Ł‡Ł‡] LT",lastWeek:"[ŚÆŲ²Ų±ŁŠŁ„ Ł‡ŁŲŖŁŠ] dddd [ŲŖŁŠ] LT",sameElse:"L"},relativeTime:{future:"%s Ł¾ŁˆŲ”",past:"%s Ų§Ś³",s:"Ś†Ł†ŲÆ Ų³ŁŠŚŖŁ†ŚŠ",ss:"%d Ų³ŁŠŚŖŁ†ŚŠ",m:"Ł‡ŚŖ Ł…Ł†Ł½",mm:"%d Ł…Ł†Ł½",h:"Ł‡ŚŖ ŚŖŁ„Ų§ŚŖ",hh:"%d ŚŖŁ„Ų§ŚŖ",d:"Ł‡ŚŖ ŚŁŠŁ†Ł‡Ł†",dd:"%d ŚŁŠŁ†Ł‡Ł†",M:"Ł‡ŚŖ Ł…Ł‡ŁŠŁ†Łˆ",MM:"%d Ł…Ł‡ŁŠŁ†Ų§",y:"Ł‡ŚŖ Ų³Ų§Ł„",yy:"%d Ų³Ų§Ł„"},preparse:function(e){return e.replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/,/g,"ŲŒ")},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("se",{months:"ođđajagemĆ”nnu_guovvamĆ”nnu_njukčamĆ”nnu_cuoŋomĆ”nnu_miessemĆ”nnu_geassemĆ”nnu_suoidnemĆ”nnu_borgemĆ”nnu_čakčamĆ”nnu_golggotmĆ”nnu_skĆ”bmamĆ”nnu_juovlamĆ”nnu".split("_"),monthsShort:"ođđj_guov_njuk_cuo_mies_geas_suoi_borg_čakč_golg_skĆ”b_juov".split("_"),weekdays:"sotnabeaivi_vuossĆ”rga_maŋŋebĆ”rga_gaskavahkku_duorastat_bearjadat_lĆ”vvardat".split("_"),weekdaysShort:"sotn_vuos_maŋ_gask_duor_bear_lĆ”v".split("_"),weekdaysMin:"s_v_m_g_d_b_L".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"MMMM D. [b.] YYYY",LLL:"MMMM D. [b.] YYYY [ti.] HH:mm",LLLL:"dddd, MMMM D. [b.] YYYY [ti.] HH:mm"},calendar:{sameDay:"[otne ti] LT",nextDay:"[ihttin ti] LT",nextWeek:"dddd [ti] LT",lastDay:"[ikte ti] LT",lastWeek:"[ovddit] dddd [ti] LT",sameElse:"L"},relativeTime:{future:"%s geažes",past:"maŋit %s",s:"moadde sekunddat",ss:"%d sekunddat",m:"okta minuhta",mm:"%d minuhtat",h:"okta diimmu",hh:"%d diimmut",d:"okta beaivi",dd:"%d beaivvit",M:"okta mĆ”nnu",MM:"%d mĆ”nut",y:"okta jahki",yy:"%d jagit"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("se",{months:"ođđajagemĆ”nnu_guovvamĆ”nnu_njukčamĆ”nnu_cuoŋomĆ”nnu_miessemĆ”nnu_geassemĆ”nnu_suoidnemĆ”nnu_borgemĆ”nnu_čakčamĆ”nnu_golggotmĆ”nnu_skĆ”bmamĆ”nnu_juovlamĆ”nnu".split("_"),monthsShort:"ođđj_guov_njuk_cuo_mies_geas_suoi_borg_čakč_golg_skĆ”b_juov".split("_"),weekdays:"sotnabeaivi_vuossĆ”rga_maŋŋebĆ”rga_gaskavahkku_duorastat_bearjadat_lĆ”vvardat".split("_"),weekdaysShort:"sotn_vuos_maŋ_gask_duor_bear_lĆ”v".split("_"),weekdaysMin:"s_v_m_g_d_b_L".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"MMMM D. [b.] YYYY",LLL:"MMMM D. [b.] YYYY [ti.] HH:mm",LLLL:"dddd, MMMM D. [b.] YYYY [ti.] HH:mm"},calendar:{sameDay:"[otne ti] LT",nextDay:"[ihttin ti] LT",nextWeek:"dddd [ti] LT",lastDay:"[ikte ti] LT",lastWeek:"[ovddit] dddd [ti] LT",sameElse:"L"},relativeTime:{future:"%s geažes",past:"maŋit %s",s:"moadde sekunddat",ss:"%d sekunddat",m:"okta minuhta",mm:"%d minuhtat",h:"okta diimmu",hh:"%d diimmut",d:"okta beaivi",dd:"%d beaivvit",M:"okta mĆ”nnu",MM:"%d mĆ”nut",y:"okta jahki",yy:"%d jagit"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("si",{months:"ą¶¢ą¶±ą·€ą·ą¶»ą·’_ą¶“ą·™ą¶¶ą¶»ą·€ą·ą¶»ą·’_ą¶øą·ą¶»ą·Šą¶­ą·”_ą¶…ą¶“ą·Šā€ą¶»ą·šą¶½ą·Š_ą¶øą·ą¶ŗą·’_ą¶¢ą·–ą¶±ą·’_ą¶¢ą·–ą¶½ą·’_ą¶…ą¶œą·ą·ƒą·Šą¶­ą·”_ą·ƒą·ą¶“ą·Šą¶­ą·ą¶øą·Šą¶¶ą¶»ą·Š_ą¶”ą¶šą·Šą¶­ą·ą¶¶ą¶»ą·Š_ą¶±ą·œą·€ą·ą¶øą·Šą¶¶ą¶»ą·Š_ą¶Æą·™ą·ƒą·ą¶øą·Šą¶¶ą¶»ą·Š".split("_"),monthsShort:"ą¶¢ą¶±_ą¶“ą·™ą¶¶_ą¶øą·ą¶»ą·Š_ą¶…ą¶“ą·Š_ą¶øą·ą¶ŗą·’_ą¶¢ą·–ą¶±ą·’_ą¶¢ą·–ą¶½ą·’_ą¶…ą¶œą·_ą·ƒą·ą¶“ą·Š_ą¶”ą¶šą·Š_ą¶±ą·œą·€ą·_ą¶Æą·™ą·ƒą·".split("_"),weekdays:"ą¶‰ą¶»ą·’ą¶Æą·_ą·ƒą¶³ą·”ą¶Æą·_ą¶…ą¶Ÿą·„ą¶»ą·”ą·€ą·ą¶Æą·_ą¶¶ą¶Æą·ą¶Æą·_ą¶¶ą·Šā€ą¶»ą·„ą·ƒą·Šą¶“ą¶­ą·’ą¶±ą·Šą¶Æą·_ą·ƒą·’ą¶šą·”ą¶»ą·ą¶Æą·_ą·ƒą·™ą¶±ą·ƒą·”ą¶»ą·ą¶Æą·".split("_"),weekdaysShort:"ą¶‰ą¶»ą·’_ą·ƒą¶³ą·”_ą¶…ą¶Ÿ_ą¶¶ą¶Æą·_ą¶¶ą·Šā€ą¶»ą·„_ą·ƒą·’ą¶šą·”_ą·ƒą·™ą¶±".split("_"),weekdaysMin:"ą¶‰_ą·ƒ_ą¶…_ą¶¶_ą¶¶ą·Šā€ą¶»_ą·ƒą·’_ą·ƒą·™".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"a h:mm",LTS:"a h:mm:ss",L:"YYYY/MM/DD",LL:"YYYY MMMM D",LLL:"YYYY MMMM D, a h:mm",LLLL:"YYYY MMMM D [ą·€ą·ą¶±ą·’] dddd, a h:mm:ss"},calendar:{sameDay:"[ą¶…ą¶Æ] LT[ą¶§]",nextDay:"[ą·„ą·™ą¶§] LT[ą¶§]",nextWeek:"dddd LT[ą¶§]",lastDay:"[ą¶Šą¶ŗą·š] LT[ą¶§]",lastWeek:"[ą¶“ą·ƒą·”ą¶œą·’ą¶ŗ] dddd LT[ą¶§]",sameElse:"L"},relativeTime:{future:"%są¶šą·’ą¶±ą·Š",past:"%są¶šą¶§ ą¶“ą·™ą¶»",s:"ą¶­ą¶­ą·Šą¶“ą¶» ą¶šą·’ą·„ą·’ą¶“ą¶ŗ",ss:"ą¶­ą¶­ą·Šą¶“ą¶» %d",m:"ą¶øą·’ą¶±ą·’ą¶­ą·Šą¶­ą·”ą·€",mm:"ą¶øą·’ą¶±ą·’ą¶­ą·Šą¶­ą·” %d",h:"ą¶“ą·ą¶ŗ",hh:"ą¶“ą·ą¶ŗ %d",d:"ą¶Æą·’ą¶±ą¶ŗ",dd:"ą¶Æą·’ą¶± %d",M:"ą¶øą·ą·ƒą¶ŗ",MM:"ą¶øą·ą·ƒ %d",y:"ą·€ą·ƒą¶»",yy:"ą·€ą·ƒą¶» %d"},dayOfMonthOrdinalParse:/\d{1,2} ą·€ą·ą¶±ą·’/,ordinal:function(e){return e+" ą·€ą·ą¶±ą·’"},meridiemParse:/ą¶“ą·™ą¶» ą·€ą¶»ą·”|ą¶“ą·ƒą·Š ą·€ą¶»ą·”|ą¶“ą·™.ą·€|ą¶“.ą·€./,isPM:function(e){return e==="ą¶“.ą·€."||e==="ą¶“ą·ƒą·Š ą·€ą¶»ą·”"},meridiem:function(e,t,n){if(e>11)return n?"ą¶“.ą·€.":"ą¶“ą·ƒą·Š ą·€ą¶»ą·”";else return n?"ą¶“ą·™.ą·€.":"ą¶“ą·™ą¶» ą·€ą¶»ą·”"}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("si",{months:"ą¶¢ą¶±ą·€ą·ą¶»ą·’_ą¶“ą·™ą¶¶ą¶»ą·€ą·ą¶»ą·’_ą¶øą·ą¶»ą·Šą¶­ą·”_ą¶…ą¶“ą·Šā€ą¶»ą·šą¶½ą·Š_ą¶øą·ą¶ŗą·’_ą¶¢ą·–ą¶±ą·’_ą¶¢ą·–ą¶½ą·’_ą¶…ą¶œą·ą·ƒą·Šą¶­ą·”_ą·ƒą·ą¶“ą·Šą¶­ą·ą¶øą·Šą¶¶ą¶»ą·Š_ą¶”ą¶šą·Šą¶­ą·ą¶¶ą¶»ą·Š_ą¶±ą·œą·€ą·ą¶øą·Šą¶¶ą¶»ą·Š_ą¶Æą·™ą·ƒą·ą¶øą·Šą¶¶ą¶»ą·Š".split("_"),monthsShort:"ą¶¢ą¶±_ą¶“ą·™ą¶¶_ą¶øą·ą¶»ą·Š_ą¶…ą¶“ą·Š_ą¶øą·ą¶ŗą·’_ą¶¢ą·–ą¶±ą·’_ą¶¢ą·–ą¶½ą·’_ą¶…ą¶œą·_ą·ƒą·ą¶“ą·Š_ą¶”ą¶šą·Š_ą¶±ą·œą·€ą·_ą¶Æą·™ą·ƒą·".split("_"),weekdays:"ą¶‰ą¶»ą·’ą¶Æą·_ą·ƒą¶³ą·”ą¶Æą·_ą¶…ą¶Ÿą·„ą¶»ą·”ą·€ą·ą¶Æą·_ą¶¶ą¶Æą·ą¶Æą·_ą¶¶ą·Šā€ą¶»ą·„ą·ƒą·Šą¶“ą¶­ą·’ą¶±ą·Šą¶Æą·_ą·ƒą·’ą¶šą·”ą¶»ą·ą¶Æą·_ą·ƒą·™ą¶±ą·ƒą·”ą¶»ą·ą¶Æą·".split("_"),weekdaysShort:"ą¶‰ą¶»ą·’_ą·ƒą¶³ą·”_ą¶…ą¶Ÿ_ą¶¶ą¶Æą·_ą¶¶ą·Šā€ą¶»ą·„_ą·ƒą·’ą¶šą·”_ą·ƒą·™ą¶±".split("_"),weekdaysMin:"ą¶‰_ą·ƒ_ą¶…_ą¶¶_ą¶¶ą·Šā€ą¶»_ą·ƒą·’_ą·ƒą·™".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"a h:mm",LTS:"a h:mm:ss",L:"YYYY/MM/DD",LL:"YYYY MMMM D",LLL:"YYYY MMMM D, a h:mm",LLLL:"YYYY MMMM D [ą·€ą·ą¶±ą·’] dddd, a h:mm:ss"},calendar:{sameDay:"[ą¶…ą¶Æ] LT[ą¶§]",nextDay:"[ą·„ą·™ą¶§] LT[ą¶§]",nextWeek:"dddd LT[ą¶§]",lastDay:"[ą¶Šą¶ŗą·š] LT[ą¶§]",lastWeek:"[ą¶“ą·ƒą·”ą¶œą·’ą¶ŗ] dddd LT[ą¶§]",sameElse:"L"},relativeTime:{future:"%są¶šą·’ą¶±ą·Š",past:"%są¶šą¶§ ą¶“ą·™ą¶»",s:"ą¶­ą¶­ą·Šą¶“ą¶» ą¶šą·’ą·„ą·’ą¶“ą¶ŗ",ss:"ą¶­ą¶­ą·Šą¶“ą¶» %d",m:"ą¶øą·’ą¶±ą·’ą¶­ą·Šą¶­ą·”ą·€",mm:"ą¶øą·’ą¶±ą·’ą¶­ą·Šą¶­ą·” %d",h:"ą¶“ą·ą¶ŗ",hh:"ą¶“ą·ą¶ŗ %d",d:"ą¶Æą·’ą¶±ą¶ŗ",dd:"ą¶Æą·’ą¶± %d",M:"ą¶øą·ą·ƒą¶ŗ",MM:"ą¶øą·ą·ƒ %d",y:"ą·€ą·ƒą¶»",yy:"ą·€ą·ƒą¶» %d"},dayOfMonthOrdinalParse:/\d{1,2} ą·€ą·ą¶±ą·’/,ordinal:function(e){return e+" ą·€ą·ą¶±ą·’"},meridiemParse:/ą¶“ą·™ą¶» ą·€ą¶»ą·”|ą¶“ą·ƒą·Š ą·€ą¶»ą·”|ą¶“ą·™.ą·€|ą¶“.ą·€./,isPM:function(e){return e==="ą¶“.ą·€."||e==="ą¶“ą·ƒą·Š ą·€ą¶»ą·”"},meridiem:function(e,t,n){if(e>11)return n?"ą¶“.ą·€.":"ą¶“ą·ƒą·Š ą·€ą¶»ą·”";else return n?"ą¶“ą·™.ą·€.":"ą¶“ą·™ą¶» ą·€ą¶»ą·”"}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t="januĆ”r_februĆ”r_marec_aprĆ­l_mĆ”j_jĆŗn_jĆŗl_august_september_oktĆ³ber_november_december".split("_"),n="jan_feb_mar_apr_mĆ”j_jĆŗn_jĆŗl_aug_sep_okt_nov_dec".split("_"),a;function o(e){return e>1&&e<5}function r(e,t,n,a){var r=e+" ";switch(n){case"s":return t||a?"pĆ”r sekĆŗnd":"pĆ”r sekundami";case"ss":if(t||a)return r+(o(e)?"sekundy":"sekĆŗnd");else return r+"sekundami";case"m":return t?"minĆŗta":a?"minĆŗtu":"minĆŗtou";case"mm":if(t||a)return r+(o(e)?"minĆŗty":"minĆŗt");else return r+"minĆŗtami";case"h":return t?"hodina":a?"hodinu":"hodinou";case"hh":if(t||a)return r+(o(e)?"hodiny":"hodĆ­n");else return r+"hodinami";case"d":return t||a?"deň":"dňom";case"dd":if(t||a)return r+(o(e)?"dni":"dnĆ­");else return r+"dňami";case"M":return t||a?"mesiac":"mesiacom";case"MM":if(t||a)return r+(o(e)?"mesiace":"mesiacov");else return r+"mesiacmi";case"y":return t||a?"rok":"rokom";case"yy":if(t||a)return r+(o(e)?"roky":"rokov");else return r+"rokmi"}}e.defineLocale("sk",{months:t,monthsShort:n,weekdays:"nedeľa_pondelok_utorok_streda_Å”tvrtok_piatok_sobota".split("_"),weekdaysShort:"ne_po_ut_st_Å”t_pi_so".split("_"),weekdaysMin:"ne_po_ut_st_Å”t_pi_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd D. MMMM YYYY H:mm"},calendar:{sameDay:"[dnes o] LT",nextDay:"[zajtra o] LT",nextWeek:function(){switch(this.day()){case 0:return"[v nedeľu o] LT";case 1:case 2:return"[v] dddd [o] LT";case 3:return"[v stredu o] LT";case 4:return"[vo Å”tvrtok o] LT";case 5:return"[v piatok o] LT";case 6:return"[v sobotu o] LT"}},lastDay:"[včera o] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulĆŗ nedeľu o] LT";case 1:case 2:return"[minulĆ½] dddd [o] LT";case 3:return"[minulĆŗ stredu o] LT";case 4:case 5:return"[minulĆ½] dddd [o] LT";case 6:return"[minulĆŗ sobotu o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"pred %s",s:r,ss:r,m:r,mm:r,h:r,hh:r,d:r,dd:r,M:r,MM:r,y:r,yy:r},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t="januĆ”r_februĆ”r_marec_aprĆ­l_mĆ”j_jĆŗn_jĆŗl_august_september_oktĆ³ber_november_december".split("_"),n="jan_feb_mar_apr_mĆ”j_jĆŗn_jĆŗl_aug_sep_okt_nov_dec".split("_"),a;function o(e){return e>1&&e<5}function r(e,t,n,a){var r=e+" ";switch(n){case"s":return t||a?"pĆ”r sekĆŗnd":"pĆ”r sekundami";case"ss":if(t||a)return r+(o(e)?"sekundy":"sekĆŗnd");else return r+"sekundami";case"m":return t?"minĆŗta":a?"minĆŗtu":"minĆŗtou";case"mm":if(t||a)return r+(o(e)?"minĆŗty":"minĆŗt");else return r+"minĆŗtami";case"h":return t?"hodina":a?"hodinu":"hodinou";case"hh":if(t||a)return r+(o(e)?"hodiny":"hodĆ­n");else return r+"hodinami";case"d":return t||a?"deň":"dňom";case"dd":if(t||a)return r+(o(e)?"dni":"dnĆ­");else return r+"dňami";case"M":return t||a?"mesiac":"mesiacom";case"MM":if(t||a)return r+(o(e)?"mesiace":"mesiacov");else return r+"mesiacmi";case"y":return t||a?"rok":"rokom";case"yy":if(t||a)return r+(o(e)?"roky":"rokov");else return r+"rokmi"}}e.defineLocale("sk",{months:t,monthsShort:n,weekdays:"nedeľa_pondelok_utorok_streda_Å”tvrtok_piatok_sobota".split("_"),weekdaysShort:"ne_po_ut_st_Å”t_pi_so".split("_"),weekdaysMin:"ne_po_ut_st_Å”t_pi_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd D. MMMM YYYY H:mm"},calendar:{sameDay:"[dnes o] LT",nextDay:"[zajtra o] LT",nextWeek:function(){switch(this.day()){case 0:return"[v nedeľu o] LT";case 1:case 2:return"[v] dddd [o] LT";case 3:return"[v stredu o] LT";case 4:return"[vo Å”tvrtok o] LT";case 5:return"[v piatok o] LT";case 6:return"[v sobotu o] LT"}},lastDay:"[včera o] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulĆŗ nedeľu o] LT";case 1:case 2:return"[minulĆ½] dddd [o] LT";case 3:return"[minulĆŗ stredu o] LT";case 4:case 5:return"[minulĆ½] dddd [o] LT";case 6:return"[minulĆŗ sobotu o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"pred %s",s:r,ss:r,m:r,mm:r,h:r,hh:r,d:r,dd:r,M:r,MM:r,y:r,yy:r},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function t(e,t,n,a){var r=e+" ";switch(n){case"s":return t||a?"nekaj sekund":"nekaj sekundami";case"ss":if(e===1)r+=t?"sekundo":"sekundi";else if(e===2)r+=t||a?"sekundi":"sekundah";else if(e<5)r+=t||a?"sekunde":"sekundah";else r+="sekund";return r;case"m":return t?"ena minuta":"eno minuto";case"mm":if(e===1)r+=t?"minuta":"minuto";else if(e===2)r+=t||a?"minuti":"minutama";else if(e<5)r+=t||a?"minute":"minutami";else r+=t||a?"minut":"minutami";return r;case"h":return t?"ena ura":"eno uro";case"hh":if(e===1)r+=t?"ura":"uro";else if(e===2)r+=t||a?"uri":"urama";else if(e<5)r+=t||a?"ure":"urami";else r+=t||a?"ur":"urami";return r;case"d":return t||a?"en dan":"enim dnem";case"dd":if(e===1)r+=t||a?"dan":"dnem";else if(e===2)r+=t||a?"dni":"dnevoma";else r+=t||a?"dni":"dnevi";return r;case"M":return t||a?"en mesec":"enim mesecem";case"MM":if(e===1)r+=t||a?"mesec":"mesecem";else if(e===2)r+=t||a?"meseca":"mesecema";else if(e<5)r+=t||a?"mesece":"meseci";else r+=t||a?"mesecev":"meseci";return r;case"y":return t||a?"eno leto":"enim letom";case"yy":if(e===1)r+=t||a?"leto":"letom";else if(e===2)r+=t||a?"leti":"letoma";else if(e<5)r+=t||a?"leta":"leti";else r+=t||a?"let":"leti";return r}}var n;e.defineLocale("sl",{months:"januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"),monthsParseExact:true,weekdays:"nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota".split("_"),weekdaysShort:"ned._pon._tor._sre._čet._pet._sob.".split("_"),weekdaysMin:"ne_po_to_sr_če_pe_so".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danes ob] LT",nextDay:"[jutri ob] LT",nextWeek:function(){switch(this.day()){case 0:return"[v] [nedeljo] [ob] LT";case 3:return"[v] [sredo] [ob] LT";case 6:return"[v] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[v] dddd [ob] LT"}},lastDay:"[včeraj ob] LT",lastWeek:function(){switch(this.day()){case 0:return"[prejÅ”njo] [nedeljo] [ob] LT";case 3:return"[prejÅ”njo] [sredo] [ob] LT";case 6:return"[prejÅ”njo] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[prejÅ”nji] dddd [ob] LT"}},sameElse:"L"},relativeTime:{future:"čez %s",past:"pred %s",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function t(e,t,n,a){var r=e+" ";switch(n){case"s":return t||a?"nekaj sekund":"nekaj sekundami";case"ss":if(e===1)r+=t?"sekundo":"sekundi";else if(e===2)r+=t||a?"sekundi":"sekundah";else if(e<5)r+=t||a?"sekunde":"sekundah";else r+="sekund";return r;case"m":return t?"ena minuta":"eno minuto";case"mm":if(e===1)r+=t?"minuta":"minuto";else if(e===2)r+=t||a?"minuti":"minutama";else if(e<5)r+=t||a?"minute":"minutami";else r+=t||a?"minut":"minutami";return r;case"h":return t?"ena ura":"eno uro";case"hh":if(e===1)r+=t?"ura":"uro";else if(e===2)r+=t||a?"uri":"urama";else if(e<5)r+=t||a?"ure":"urami";else r+=t||a?"ur":"urami";return r;case"d":return t||a?"en dan":"enim dnem";case"dd":if(e===1)r+=t||a?"dan":"dnem";else if(e===2)r+=t||a?"dni":"dnevoma";else r+=t||a?"dni":"dnevi";return r;case"M":return t||a?"en mesec":"enim mesecem";case"MM":if(e===1)r+=t||a?"mesec":"mesecem";else if(e===2)r+=t||a?"meseca":"mesecema";else if(e<5)r+=t||a?"mesece":"meseci";else r+=t||a?"mesecev":"meseci";return r;case"y":return t||a?"eno leto":"enim letom";case"yy":if(e===1)r+=t||a?"leto":"letom";else if(e===2)r+=t||a?"leti":"letoma";else if(e<5)r+=t||a?"leta":"leti";else r+=t||a?"let":"leti";return r}}var n;e.defineLocale("sl",{months:"januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"),monthsParseExact:true,weekdays:"nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota".split("_"),weekdaysShort:"ned._pon._tor._sre._čet._pet._sob.".split("_"),weekdaysMin:"ne_po_to_sr_če_pe_so".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danes ob] LT",nextDay:"[jutri ob] LT",nextWeek:function(){switch(this.day()){case 0:return"[v] [nedeljo] [ob] LT";case 3:return"[v] [sredo] [ob] LT";case 6:return"[v] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[v] dddd [ob] LT"}},lastDay:"[včeraj ob] LT",lastWeek:function(){switch(this.day()){case 0:return"[prejÅ”njo] [nedeljo] [ob] LT";case 3:return"[prejÅ”njo] [sredo] [ob] LT";case 6:return"[prejÅ”njo] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[prejÅ”nji] dddd [ob] LT"}},sameElse:"L"},relativeTime:{future:"čez %s",past:"pred %s",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("sq",{months:"Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_NĆ«ntor_Dhjetor".split("_"),monthsShort:"Jan_Shk_Mar_Pri_Maj_Qer_Kor_Gus_Sht_Tet_NĆ«n_Dhj".split("_"),weekdays:"E Diel_E HĆ«nĆ«_E MartĆ«_E MĆ«rkurĆ«_E Enjte_E Premte_E ShtunĆ«".split("_"),weekdaysShort:"Die_HĆ«n_Mar_MĆ«r_Enj_Pre_Sht".split("_"),weekdaysMin:"D_H_Ma_MĆ«_E_P_Sh".split("_"),weekdaysParseExact:true,meridiemParse:/PD|MD/,isPM:function(e){return e.charAt(0)==="M"},meridiem:function(e,t,n){return e<12?"PD":"MD"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Sot nĆ«] LT",nextDay:"[NesĆ«r nĆ«] LT",nextWeek:"dddd [nĆ«] LT",lastDay:"[Dje nĆ«] LT",lastWeek:"dddd [e kaluar nĆ«] LT",sameElse:"L"},relativeTime:{future:"nĆ« %s",past:"%s mĆ« parĆ«",s:"disa sekonda",ss:"%d sekonda",m:"njĆ« minutĆ«",mm:"%d minuta",h:"njĆ« orĆ«",hh:"%d orĆ«",d:"njĆ« ditĆ«",dd:"%d ditĆ«",M:"njĆ« muaj",MM:"%d muaj",y:"njĆ« vit",yy:"%d vite"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("sq",{months:"Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_NĆ«ntor_Dhjetor".split("_"),monthsShort:"Jan_Shk_Mar_Pri_Maj_Qer_Kor_Gus_Sht_Tet_NĆ«n_Dhj".split("_"),weekdays:"E Diel_E HĆ«nĆ«_E MartĆ«_E MĆ«rkurĆ«_E Enjte_E Premte_E ShtunĆ«".split("_"),weekdaysShort:"Die_HĆ«n_Mar_MĆ«r_Enj_Pre_Sht".split("_"),weekdaysMin:"D_H_Ma_MĆ«_E_P_Sh".split("_"),weekdaysParseExact:true,meridiemParse:/PD|MD/,isPM:function(e){return e.charAt(0)==="M"},meridiem:function(e,t,n){return e<12?"PD":"MD"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Sot nĆ«] LT",nextDay:"[NesĆ«r nĆ«] LT",nextWeek:"dddd [nĆ«] LT",lastDay:"[Dje nĆ«] LT",lastWeek:"dddd [e kaluar nĆ«] LT",sameElse:"L"},relativeTime:{future:"nĆ« %s",past:"%s mĆ« parĆ«",s:"disa sekonda",ss:"%d sekonda",m:"njĆ« minutĆ«",mm:"%d minuta",h:"njĆ« orĆ«",hh:"%d orĆ«",d:"njĆ« ditĆ«",dd:"%d ditĆ«",M:"njĆ« muaj",MM:"%d muaj",y:"njĆ« vit",yy:"%d vite"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var i={words:{ss:["sekunda","sekunde","sekundi"],m:["jedan minut","jednog minuta"],mm:["minut","minuta","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],d:["jedan dan","jednog dana"],dd:["dan","dana","dana"],M:["jedan mesec","jednog meseca"],MM:["mesec","meseca","meseci"],y:["jednu godinu","jedne godine"],yy:["godinu","godine","godina"]},correctGrammaticalCase:function(e,t){if(e%10>=1&&e%10<=4&&(e%100<10||e%100>=20))return e%10===1?t[0]:t[1];return t[2]},translate:function(e,t,n,a){var r=i.words[n],o;if(n.length===1){if(n==="y"&&t)return"jedna godina";return a||t?r[0]:r[1]}o=i.correctGrammaticalCase(e,r);if(n==="yy"&&t&&o==="godinu")return e+" godina";return e+" "+o}},t;e.defineLocale("sr",{months:"januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.".split("_"),monthsParseExact:true,weekdays:"nedelja_ponedeljak_utorak_sreda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sre._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D. M. YYYY.",LL:"D. MMMM YYYY.",LLL:"D. MMMM YYYY. H:mm",LLLL:"dddd, D. MMMM YYYY. H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedelju] [u] LT";case 3:return"[u] [sredu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){var e=["[proÅ”le] [nedelje] [u] LT","[proÅ”log] [ponedeljka] [u] LT","[proÅ”log] [utorka] [u] LT","[proÅ”le] [srede] [u] LT","[proÅ”log] [četvrtka] [u] LT","[proÅ”log] [petka] [u] LT","[proÅ”le] [subote] [u] LT"];return e[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"pre %s",s:"nekoliko sekundi",ss:i.translate,m:i.translate,mm:i.translate,h:i.translate,hh:i.translate,d:i.translate,dd:i.translate,M:i.translate,MM:i.translate,y:i.translate,yy:i.translate},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var i={words:{ss:["sekunda","sekunde","sekundi"],m:["jedan minut","jednog minuta"],mm:["minut","minuta","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],d:["jedan dan","jednog dana"],dd:["dan","dana","dana"],M:["jedan mesec","jednog meseca"],MM:["mesec","meseca","meseci"],y:["jednu godinu","jedne godine"],yy:["godinu","godine","godina"]},correctGrammaticalCase:function(e,t){if(e%10>=1&&e%10<=4&&(e%100<10||e%100>=20))return e%10===1?t[0]:t[1];return t[2]},translate:function(e,t,n,a){var r=i.words[n],o;if(n.length===1){if(n==="y"&&t)return"jedna godina";return a||t?r[0]:r[1]}o=i.correctGrammaticalCase(e,r);if(n==="yy"&&t&&o==="godinu")return e+" godina";return e+" "+o}},t;e.defineLocale("sr",{months:"januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.".split("_"),monthsParseExact:true,weekdays:"nedelja_ponedeljak_utorak_sreda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sre._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D. M. YYYY.",LL:"D. MMMM YYYY.",LLL:"D. MMMM YYYY. H:mm",LLLL:"dddd, D. MMMM YYYY. H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedelju] [u] LT";case 3:return"[u] [sredu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){var e=["[proÅ”le] [nedelje] [u] LT","[proÅ”log] [ponedeljka] [u] LT","[proÅ”log] [utorka] [u] LT","[proÅ”le] [srede] [u] LT","[proÅ”log] [četvrtka] [u] LT","[proÅ”log] [petka] [u] LT","[proÅ”le] [subote] [u] LT"];return e[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"pre %s",s:"nekoliko sekundi",ss:i.translate,m:i.translate,mm:i.translate,h:i.translate,hh:i.translate,d:i.translate,dd:i.translate,M:i.translate,MM:i.translate,y:i.translate,yy:i.translate},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var i={words:{ss:["сŠµŠŗуŠ½Š“Š°","сŠµŠŗуŠ½Š“Šµ","сŠµŠŗуŠ½Š“Šø"],m:["јŠµŠ“Š°Š½ Š¼ŠøŠ½ŃƒŃ‚","јŠµŠ“Š½Š¾Š³ Š¼ŠøŠ½ŃƒŃ‚Š°"],mm:["Š¼ŠøŠ½ŃƒŃ‚","Š¼ŠøŠ½ŃƒŃ‚Š°","Š¼ŠøŠ½ŃƒŃ‚Š°"],h:["јŠµŠ“Š°Š½ сŠ°Ń‚","јŠµŠ“Š½Š¾Š³ сŠ°Ń‚Š°"],hh:["сŠ°Ń‚","сŠ°Ń‚Š°","сŠ°Ń‚Šø"],d:["јŠµŠ“Š°Š½ Š“Š°Š½","јŠµŠ“Š½Š¾Š³ Š“Š°Š½Š°"],dd:["Š“Š°Š½","Š“Š°Š½Š°","Š“Š°Š½Š°"],M:["јŠµŠ“Š°Š½ Š¼ŠµŃŠµŃ†","јŠµŠ“Š½Š¾Š³ Š¼ŠµŃŠµŃ†Š°"],MM:["Š¼ŠµŃŠµŃ†","Š¼ŠµŃŠµŃ†Š°","Š¼ŠµŃŠµŃ†Šø"],y:["јŠµŠ“Š½Ńƒ Š³Š¾Š“ŠøŠ½Ńƒ","јŠµŠ“Š½Šµ Š³Š¾Š“ŠøŠ½Šµ"],yy:["Š³Š¾Š“ŠøŠ½Ńƒ","Š³Š¾Š“ŠøŠ½Šµ","Š³Š¾Š“ŠøŠ½Š°"]},correctGrammaticalCase:function(e,t){if(e%10>=1&&e%10<=4&&(e%100<10||e%100>=20))return e%10===1?t[0]:t[1];return t[2]},translate:function(e,t,n,a){var r=i.words[n],o;if(n.length===1){if(n==="y"&&t)return"јŠµŠ“Š½Š° Š³Š¾Š“ŠøŠ½Š°";return a||t?r[0]:r[1]}o=i.correctGrammaticalCase(e,r);if(n==="yy"&&t&&o==="Š³Š¾Š“ŠøŠ½Ńƒ")return e+" Š³Š¾Š“ŠøŠ½Š°";return e+" "+o}},t;e.defineLocale("sr-cyrl",{months:"јŠ°Š½ŃƒŠ°Ń€_фŠµŠ±Ń€ŃƒŠ°Ń€_Š¼Š°Ń€Ń‚_Š°ŠæрŠøŠ»_Š¼Š°Ń˜_јуŠ½_јуŠ»_Š°Š²Š³ŃƒŃŃ‚_сŠµŠæтŠµŠ¼Š±Š°Ń€_Š¾ŠŗтŠ¾Š±Š°Ń€_Š½Š¾Š²ŠµŠ¼Š±Š°Ń€_Š“ŠµŃ†ŠµŠ¼Š±Š°Ń€".split("_"),monthsShort:"јŠ°Š½._фŠµŠ±._Š¼Š°Ń€._Š°Šæр._Š¼Š°Ń˜_јуŠ½_јуŠ»_Š°Š²Š³._сŠµŠæ._Š¾Šŗт._Š½Š¾Š²._Š“ŠµŃ†.".split("_"),monthsParseExact:true,weekdays:"Š½ŠµŠ“ŠµŃ™Š°_ŠæŠ¾Š½ŠµŠ“ŠµŃ™Š°Šŗ_утŠ¾Ń€Š°Šŗ_срŠµŠ“Š°_чŠµŃ‚Š²Ń€Ń‚Š°Šŗ_ŠæŠµŃ‚Š°Šŗ_суŠ±Š¾Ń‚Š°".split("_"),weekdaysShort:"Š½ŠµŠ“._ŠæŠ¾Š½._утŠ¾._срŠµ._чŠµŃ‚._ŠæŠµŃ‚._суŠ±.".split("_"),weekdaysMin:"Š½Šµ_ŠæŠ¾_ут_ср_чŠµ_ŠæŠµ_су".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D. M. YYYY.",LL:"D. MMMM YYYY.",LLL:"D. MMMM YYYY. H:mm",LLLL:"dddd, D. MMMM YYYY. H:mm"},calendar:{sameDay:"[Š“Š°Š½Š°Ń у] LT",nextDay:"[сутрŠ° у] LT",nextWeek:function(){switch(this.day()){case 0:return"[у] [Š½ŠµŠ“ŠµŃ™Ńƒ] [у] LT";case 3:return"[у] [срŠµŠ“у] [у] LT";case 6:return"[у] [суŠ±Š¾Ń‚Ńƒ] [у] LT";case 1:case 2:case 4:case 5:return"[у] dddd [у] LT"}},lastDay:"[јучŠµ у] LT",lastWeek:function(){var e=["[ŠæрŠ¾ŃˆŠ»Šµ] [Š½ŠµŠ“ŠµŃ™Šµ] [у] LT","[ŠæрŠ¾ŃˆŠ»Š¾Š³] [ŠæŠ¾Š½ŠµŠ“ŠµŃ™ŠŗŠ°] [у] LT","[ŠæрŠ¾ŃˆŠ»Š¾Š³] [утŠ¾Ń€ŠŗŠ°] [у] LT","[ŠæрŠ¾ŃˆŠ»Šµ] [срŠµŠ“Šµ] [у] LT","[ŠæрŠ¾ŃˆŠ»Š¾Š³] [чŠµŃ‚Š²Ń€Ń‚ŠŗŠ°] [у] LT","[ŠæрŠ¾ŃˆŠ»Š¾Š³] [ŠæŠµŃ‚ŠŗŠ°] [у] LT","[ŠæрŠ¾ŃˆŠ»Šµ] [суŠ±Š¾Ń‚Šµ] [у] LT"];return e[this.day()]},sameElse:"L"},relativeTime:{future:"Š·Š° %s",past:"ŠæрŠµ %s",s:"Š½ŠµŠŗŠ¾Š»ŠøŠŗŠ¾ сŠµŠŗуŠ½Š“Šø",ss:i.translate,m:i.translate,mm:i.translate,h:i.translate,hh:i.translate,d:i.translate,dd:i.translate,M:i.translate,MM:i.translate,y:i.translate,yy:i.translate},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var i={words:{ss:["сŠµŠŗуŠ½Š“Š°","сŠµŠŗуŠ½Š“Šµ","сŠµŠŗуŠ½Š“Šø"],m:["јŠµŠ“Š°Š½ Š¼ŠøŠ½ŃƒŃ‚","јŠµŠ“Š½Š¾Š³ Š¼ŠøŠ½ŃƒŃ‚Š°"],mm:["Š¼ŠøŠ½ŃƒŃ‚","Š¼ŠøŠ½ŃƒŃ‚Š°","Š¼ŠøŠ½ŃƒŃ‚Š°"],h:["јŠµŠ“Š°Š½ сŠ°Ń‚","јŠµŠ“Š½Š¾Š³ сŠ°Ń‚Š°"],hh:["сŠ°Ń‚","сŠ°Ń‚Š°","сŠ°Ń‚Šø"],d:["јŠµŠ“Š°Š½ Š“Š°Š½","јŠµŠ“Š½Š¾Š³ Š“Š°Š½Š°"],dd:["Š“Š°Š½","Š“Š°Š½Š°","Š“Š°Š½Š°"],M:["јŠµŠ“Š°Š½ Š¼ŠµŃŠµŃ†","јŠµŠ“Š½Š¾Š³ Š¼ŠµŃŠµŃ†Š°"],MM:["Š¼ŠµŃŠµŃ†","Š¼ŠµŃŠµŃ†Š°","Š¼ŠµŃŠµŃ†Šø"],y:["јŠµŠ“Š½Ńƒ Š³Š¾Š“ŠøŠ½Ńƒ","јŠµŠ“Š½Šµ Š³Š¾Š“ŠøŠ½Šµ"],yy:["Š³Š¾Š“ŠøŠ½Ńƒ","Š³Š¾Š“ŠøŠ½Šµ","Š³Š¾Š“ŠøŠ½Š°"]},correctGrammaticalCase:function(e,t){if(e%10>=1&&e%10<=4&&(e%100<10||e%100>=20))return e%10===1?t[0]:t[1];return t[2]},translate:function(e,t,n,a){var r=i.words[n],o;if(n.length===1){if(n==="y"&&t)return"јŠµŠ“Š½Š° Š³Š¾Š“ŠøŠ½Š°";return a||t?r[0]:r[1]}o=i.correctGrammaticalCase(e,r);if(n==="yy"&&t&&o==="Š³Š¾Š“ŠøŠ½Ńƒ")return e+" Š³Š¾Š“ŠøŠ½Š°";return e+" "+o}},t;e.defineLocale("sr-cyrl",{months:"јŠ°Š½ŃƒŠ°Ń€_фŠµŠ±Ń€ŃƒŠ°Ń€_Š¼Š°Ń€Ń‚_Š°ŠæрŠøŠ»_Š¼Š°Ń˜_јуŠ½_јуŠ»_Š°Š²Š³ŃƒŃŃ‚_сŠµŠæтŠµŠ¼Š±Š°Ń€_Š¾ŠŗтŠ¾Š±Š°Ń€_Š½Š¾Š²ŠµŠ¼Š±Š°Ń€_Š“ŠµŃ†ŠµŠ¼Š±Š°Ń€".split("_"),monthsShort:"јŠ°Š½._фŠµŠ±._Š¼Š°Ń€._Š°Šæр._Š¼Š°Ń˜_јуŠ½_јуŠ»_Š°Š²Š³._сŠµŠæ._Š¾Šŗт._Š½Š¾Š²._Š“ŠµŃ†.".split("_"),monthsParseExact:true,weekdays:"Š½ŠµŠ“ŠµŃ™Š°_ŠæŠ¾Š½ŠµŠ“ŠµŃ™Š°Šŗ_утŠ¾Ń€Š°Šŗ_срŠµŠ“Š°_чŠµŃ‚Š²Ń€Ń‚Š°Šŗ_ŠæŠµŃ‚Š°Šŗ_суŠ±Š¾Ń‚Š°".split("_"),weekdaysShort:"Š½ŠµŠ“._ŠæŠ¾Š½._утŠ¾._срŠµ._чŠµŃ‚._ŠæŠµŃ‚._суŠ±.".split("_"),weekdaysMin:"Š½Šµ_ŠæŠ¾_ут_ср_чŠµ_ŠæŠµ_су".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D. M. YYYY.",LL:"D. MMMM YYYY.",LLL:"D. MMMM YYYY. H:mm",LLLL:"dddd, D. MMMM YYYY. H:mm"},calendar:{sameDay:"[Š“Š°Š½Š°Ń у] LT",nextDay:"[сутрŠ° у] LT",nextWeek:function(){switch(this.day()){case 0:return"[у] [Š½ŠµŠ“ŠµŃ™Ńƒ] [у] LT";case 3:return"[у] [срŠµŠ“у] [у] LT";case 6:return"[у] [суŠ±Š¾Ń‚Ńƒ] [у] LT";case 1:case 2:case 4:case 5:return"[у] dddd [у] LT"}},lastDay:"[јучŠµ у] LT",lastWeek:function(){var e=["[ŠæрŠ¾ŃˆŠ»Šµ] [Š½ŠµŠ“ŠµŃ™Šµ] [у] LT","[ŠæрŠ¾ŃˆŠ»Š¾Š³] [ŠæŠ¾Š½ŠµŠ“ŠµŃ™ŠŗŠ°] [у] LT","[ŠæрŠ¾ŃˆŠ»Š¾Š³] [утŠ¾Ń€ŠŗŠ°] [у] LT","[ŠæрŠ¾ŃˆŠ»Šµ] [срŠµŠ“Šµ] [у] LT","[ŠæрŠ¾ŃˆŠ»Š¾Š³] [чŠµŃ‚Š²Ń€Ń‚ŠŗŠ°] [у] LT","[ŠæрŠ¾ŃˆŠ»Š¾Š³] [ŠæŠµŃ‚ŠŗŠ°] [у] LT","[ŠæрŠ¾ŃˆŠ»Šµ] [суŠ±Š¾Ń‚Šµ] [у] LT"];return e[this.day()]},sameElse:"L"},relativeTime:{future:"Š·Š° %s",past:"ŠæрŠµ %s",s:"Š½ŠµŠŗŠ¾Š»ŠøŠŗŠ¾ сŠµŠŗуŠ½Š“Šø",ss:i.translate,m:i.translate,mm:i.translate,h:i.translate,hh:i.translate,d:i.translate,dd:i.translate,M:i.translate,MM:i.translate,y:i.translate,yy:i.translate},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("ss",{months:"Bhimbidvwane_Indlovana_Indlov'lenkhulu_Mabasa_Inkhwekhweti_Inhlaba_Kholwane_Ingci_Inyoni_Imphala_Lweti_Ingongoni".split("_"),monthsShort:"Bhi_Ina_Inu_Mab_Ink_Inh_Kho_Igc_Iny_Imp_Lwe_Igo".split("_"),weekdays:"Lisontfo_Umsombuluko_Lesibili_Lesitsatfu_Lesine_Lesihlanu_Umgcibelo".split("_"),weekdaysShort:"Lis_Umb_Lsb_Les_Lsi_Lsh_Umg".split("_"),weekdaysMin:"Li_Us_Lb_Lt_Ls_Lh_Ug".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Namuhla nga] LT",nextDay:"[Kusasa nga] LT",nextWeek:"dddd [nga] LT",lastDay:"[Itolo nga] LT",lastWeek:"dddd [leliphelile] [nga] LT",sameElse:"L"},relativeTime:{future:"nga %s",past:"wenteka nga %s",s:"emizuzwana lomcane",ss:"%d mzuzwana",m:"umzuzu",mm:"%d emizuzu",h:"lihora",hh:"%d emahora",d:"lilanga",dd:"%d emalanga",M:"inyanga",MM:"%d tinyanga",y:"umnyaka",yy:"%d iminyaka"},meridiemParse:/ekuseni|emini|entsambama|ebusuku/,meridiem:function(e,t,n){if(e<11)return"ekuseni";else if(e<15)return"emini";else if(e<19)return"entsambama";else return"ebusuku"},meridiemHour:function(e,t){if(e===12)e=0;if(t==="ekuseni")return e;else if(t==="emini")return e>=11?e:e+12;else if(t==="entsambama"||t==="ebusuku"){if(e===0)return 0;return e+12}},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:"%d",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("ss",{months:"Bhimbidvwane_Indlovana_Indlov'lenkhulu_Mabasa_Inkhwekhweti_Inhlaba_Kholwane_Ingci_Inyoni_Imphala_Lweti_Ingongoni".split("_"),monthsShort:"Bhi_Ina_Inu_Mab_Ink_Inh_Kho_Igc_Iny_Imp_Lwe_Igo".split("_"),weekdays:"Lisontfo_Umsombuluko_Lesibili_Lesitsatfu_Lesine_Lesihlanu_Umgcibelo".split("_"),weekdaysShort:"Lis_Umb_Lsb_Les_Lsi_Lsh_Umg".split("_"),weekdaysMin:"Li_Us_Lb_Lt_Ls_Lh_Ug".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Namuhla nga] LT",nextDay:"[Kusasa nga] LT",nextWeek:"dddd [nga] LT",lastDay:"[Itolo nga] LT",lastWeek:"dddd [leliphelile] [nga] LT",sameElse:"L"},relativeTime:{future:"nga %s",past:"wenteka nga %s",s:"emizuzwana lomcane",ss:"%d mzuzwana",m:"umzuzu",mm:"%d emizuzu",h:"lihora",hh:"%d emahora",d:"lilanga",dd:"%d emalanga",M:"inyanga",MM:"%d tinyanga",y:"umnyaka",yy:"%d iminyaka"},meridiemParse:/ekuseni|emini|entsambama|ebusuku/,meridiem:function(e,t,n){if(e<11)return"ekuseni";else if(e<15)return"emini";else if(e<19)return"entsambama";else return"ebusuku"},meridiemHour:function(e,t){if(e===12)e=0;if(t==="ekuseni")return e;else if(t==="emini")return e>=11?e:e+12;else if(t==="entsambama"||t==="ebusuku"){if(e===0)return 0;return e+12}},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:"%d",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("sv",{months:"januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"sƶndag_mĆ„ndag_tisdag_onsdag_torsdag_fredag_lƶrdag".split("_"),weekdaysShort:"sƶn_mĆ„n_tis_ons_tor_fre_lƶr".split("_"),weekdaysMin:"sƶ_mĆ„_ti_on_to_fr_lƶ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [kl.] HH:mm",LLLL:"dddd D MMMM YYYY [kl.] HH:mm",lll:"D MMM YYYY HH:mm",llll:"ddd D MMM YYYY HH:mm"},calendar:{sameDay:"[Idag] LT",nextDay:"[Imorgon] LT",lastDay:"[IgĆ„r] LT",nextWeek:"[PĆ„] dddd LT",lastWeek:"[I] dddd[s] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"fƶr %s sedan",s:"nĆ„gra sekunder",ss:"%d sekunder",m:"en minut",mm:"%d minuter",h:"en timme",hh:"%d timmar",d:"en dag",dd:"%d dagar",M:"en mĆ„nad",MM:"%d mĆ„nader",y:"ett Ć„r",yy:"%d Ć„r"},dayOfMonthOrdinalParse:/\d{1,2}(\:e|\:a)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?":e":t===1?":a":t===2?":a":t===3?":e":":e";return e+n},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("sv",{months:"januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"sƶndag_mĆ„ndag_tisdag_onsdag_torsdag_fredag_lƶrdag".split("_"),weekdaysShort:"sƶn_mĆ„n_tis_ons_tor_fre_lƶr".split("_"),weekdaysMin:"sƶ_mĆ„_ti_on_to_fr_lƶ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [kl.] HH:mm",LLLL:"dddd D MMMM YYYY [kl.] HH:mm",lll:"D MMM YYYY HH:mm",llll:"ddd D MMM YYYY HH:mm"},calendar:{sameDay:"[Idag] LT",nextDay:"[Imorgon] LT",lastDay:"[IgĆ„r] LT",nextWeek:"[PĆ„] dddd LT",lastWeek:"[I] dddd[s] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"fƶr %s sedan",s:"nĆ„gra sekunder",ss:"%d sekunder",m:"en minut",mm:"%d minuter",h:"en timme",hh:"%d timmar",d:"en dag",dd:"%d dagar",M:"en mĆ„nad",MM:"%d mĆ„nader",y:"ett Ć„r",yy:"%d Ć„r"},dayOfMonthOrdinalParse:/\d{1,2}(\:e|\:a)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?":e":t===1?":a":t===2?":a":t===3?":e":":e";return e+n},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("sw",{months:"Januari_Februari_Machi_Aprili_Mei_Juni_Julai_Agosti_Septemba_Oktoba_Novemba_Desemba".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ago_Sep_Okt_Nov_Des".split("_"),weekdays:"Jumapili_Jumatatu_Jumanne_Jumatano_Alhamisi_Ijumaa_Jumamosi".split("_"),weekdaysShort:"Jpl_Jtat_Jnne_Jtan_Alh_Ijm_Jmos".split("_"),weekdaysMin:"J2_J3_J4_J5_Al_Ij_J1".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"hh:mm A",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[leo saa] LT",nextDay:"[kesho saa] LT",nextWeek:"[wiki ijayo] dddd [saat] LT",lastDay:"[jana] LT",lastWeek:"[wiki iliyopita] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s baadaye",past:"tokea %s",s:"hivi punde",ss:"sekunde %d",m:"dakika moja",mm:"dakika %d",h:"saa limoja",hh:"masaa %d",d:"siku moja",dd:"siku %d",M:"mwezi mmoja",MM:"miezi %d",y:"mwaka mmoja",yy:"miaka %d"},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("sw",{months:"Januari_Februari_Machi_Aprili_Mei_Juni_Julai_Agosti_Septemba_Oktoba_Novemba_Desemba".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ago_Sep_Okt_Nov_Des".split("_"),weekdays:"Jumapili_Jumatatu_Jumanne_Jumatano_Alhamisi_Ijumaa_Jumamosi".split("_"),weekdaysShort:"Jpl_Jtat_Jnne_Jtan_Alh_Ijm_Jmos".split("_"),weekdaysMin:"J2_J3_J4_J5_Al_Ij_J1".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"hh:mm A",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[leo saa] LT",nextDay:"[kesho saa] LT",nextWeek:"[wiki ijayo] dddd [saat] LT",lastDay:"[jana] LT",lastWeek:"[wiki iliyopita] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s baadaye",past:"tokea %s",s:"hivi punde",ss:"sekunde %d",m:"dakika moja",mm:"dakika %d",h:"saa limoja",hh:"masaa %d",d:"siku moja",dd:"siku %d",M:"mwezi mmoja",MM:"miezi %d",y:"mwaka mmoja",yy:"miaka %d"},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t={1:"ąÆ§",2:"ąÆØ",3:"ąÆ©",4:"ąÆŖ",5:"ąÆ«",6:"ąÆ¬",7:"ąÆ­",8:"ąÆ®",9:"ąÆÆ",0:"ąÆ¦"},n={"ąÆ§":"1","ąÆØ":"2","ąÆ©":"3","ąÆŖ":"4","ąÆ«":"5","ąÆ¬":"6","ąÆ­":"7","ąÆ®":"8","ąÆÆ":"9","ąÆ¦":"0"},a;e.defineLocale("ta",{months:"ą®œą®©ą®µą®°ą®æ_ą®Ŗą®æą®ŖąÆą®°ą®µą®°ą®æ_ą®®ą®¾ą®°ąÆą®šąÆ_ą®ą®ŖąÆą®°ą®²ąÆ_ą®®ąÆ‡_ą®œąÆ‚ą®©ąÆ_ą®œąÆ‚ą®²ąÆˆ_ą®†ą®•ą®øąÆą®ŸąÆ_ą®šąÆ†ą®ŖąÆą®ŸąÆ†ą®®ąÆą®Ŗą®°ąÆ_ą®…ą®•ąÆą®ŸąÆ‡ą®¾ą®Ŗą®°ąÆ_ą®Øą®µą®®ąÆą®Ŗą®°ąÆ_ą®Ÿą®æą®šą®®ąÆą®Ŗą®°ąÆ".split("_"),monthsShort:"ą®œą®©ą®µą®°ą®æ_ą®Ŗą®æą®ŖąÆą®°ą®µą®°ą®æ_ą®®ą®¾ą®°ąÆą®šąÆ_ą®ą®ŖąÆą®°ą®²ąÆ_ą®®ąÆ‡_ą®œąÆ‚ą®©ąÆ_ą®œąÆ‚ą®²ąÆˆ_ą®†ą®•ą®øąÆą®ŸąÆ_ą®šąÆ†ą®ŖąÆą®ŸąÆ†ą®®ąÆą®Ŗą®°ąÆ_ą®…ą®•ąÆą®ŸąÆ‡ą®¾ą®Ŗą®°ąÆ_ą®Øą®µą®®ąÆą®Ŗą®°ąÆ_ą®Ÿą®æą®šą®®ąÆą®Ŗą®°ąÆ".split("_"),weekdays:"ą®žą®¾ą®Æą®æą®±ąÆą®±ąÆą®•ąÆą®•ą®æą®“ą®®ąÆˆ_ą®¤ą®æą®™ąÆą®•ą®ŸąÆą®•ą®æą®“ą®®ąÆˆ_ą®šąÆ†ą®µąÆą®µą®¾ą®ÆąÆą®•ą®æą®“ą®®ąÆˆ_ą®ŖąÆą®¤ą®©ąÆą®•ą®æą®“ą®®ąÆˆ_ą®µą®æą®Æą®¾ą®“ą®•ąÆą®•ą®æą®“ą®®ąÆˆ_ą®µąÆ†ą®³ąÆą®³ą®æą®•ąÆą®•ą®æą®“ą®®ąÆˆ_ą®šą®©ą®æą®•ąÆą®•ą®æą®“ą®®ąÆˆ".split("_"),weekdaysShort:"ą®žą®¾ą®Æą®æą®±ąÆ_ą®¤ą®æą®™ąÆą®•ą®³ąÆ_ą®šąÆ†ą®µąÆą®µą®¾ą®ÆąÆ_ą®ŖąÆą®¤ą®©ąÆ_ą®µą®æą®Æą®¾ą®“ą®©ąÆ_ą®µąÆ†ą®³ąÆą®³ą®æ_ą®šą®©ą®æ".split("_"),weekdaysMin:"ą®žą®¾_ą®¤ą®æ_ą®šąÆ†_ą®ŖąÆ_ą®µą®æ_ą®µąÆ†_ą®š".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, HH:mm",LLLL:"dddd, D MMMM YYYY, HH:mm"},calendar:{sameDay:"[ą®‡ą®©ąÆą®±ąÆ] LT",nextDay:"[ą®Øą®¾ą®³ąÆˆ] LT",nextWeek:"dddd, LT",lastDay:"[ą®ØąÆ‡ą®±ąÆą®±ąÆ] LT",lastWeek:"[ą®•ą®Ÿą®ØąÆą®¤ ą®µą®¾ą®°ą®®ąÆ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą®‡ą®²ąÆ",past:"%s ą®®ąÆą®©ąÆ",s:"ą®’ą®°ąÆ ą®šą®æą®² ą®µą®æą®Øą®¾ą®Ÿą®æą®•ą®³ąÆ",ss:"%d ą®µą®æą®Øą®¾ą®Ÿą®æą®•ą®³ąÆ",m:"ą®’ą®°ąÆ ą®Øą®æą®®ą®æą®Ÿą®®ąÆ",mm:"%d ą®Øą®æą®®ą®æą®Ÿą®™ąÆą®•ą®³ąÆ",h:"ą®’ą®°ąÆ ą®®ą®£ą®æ ą®ØąÆ‡ą®°ą®®ąÆ",hh:"%d ą®®ą®£ą®æ ą®ØąÆ‡ą®°ą®®ąÆ",d:"ą®’ą®°ąÆ ą®Øą®¾ą®³ąÆ",dd:"%d ą®Øą®¾ą®ŸąÆą®•ą®³ąÆ",M:"ą®’ą®°ąÆ ą®®ą®¾ą®¤ą®®ąÆ",MM:"%d ą®®ą®¾ą®¤ą®™ąÆą®•ą®³ąÆ",y:"ą®’ą®°ąÆ ą®µą®°ąÆą®Ÿą®®ąÆ",yy:"%d ą®†ą®£ąÆą®ŸąÆą®•ą®³ąÆ"},dayOfMonthOrdinalParse:/\d{1,2}ą®µą®¤ąÆ/,ordinal:function(e){return e+"ą®µą®¤ąÆ"},preparse:function(e){return e.replace(/[ąÆ§ąÆØąÆ©ąÆŖąÆ«ąÆ¬ąÆ­ąÆ®ąÆÆąÆ¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą®Æą®¾ą®®ą®®ąÆ|ą®µąÆˆą®•ą®±ąÆˆ|ą®•ą®¾ą®²ąÆˆ|ą®Øą®£ąÆą®Ŗą®•ą®²ąÆ|ą®Žą®±ąÆą®Ŗą®¾ą®ŸąÆ|ą®®ą®¾ą®²ąÆˆ/,meridiem:function(e,t,n){if(e<2)return" ą®Æą®¾ą®®ą®®ąÆ";else if(e<6)return" ą®µąÆˆą®•ą®±ąÆˆ";else if(e<10)return" ą®•ą®¾ą®²ąÆˆ";else if(e<14)return" ą®Øą®£ąÆą®Ŗą®•ą®²ąÆ";else if(e<18)return" ą®Žą®±ąÆą®Ŗą®¾ą®ŸąÆ";else if(e<22)return" ą®®ą®¾ą®²ąÆˆ";else return" ą®Æą®¾ą®®ą®®ąÆ"},meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą®Æą®¾ą®®ą®®ąÆ")return e<2?e:e+12;else if(t==="ą®µąÆˆą®•ą®±ąÆˆ"||t==="ą®•ą®¾ą®²ąÆˆ")return e;else if(t==="ą®Øą®£ąÆą®Ŗą®•ą®²ąÆ")return e>=10?e:e+12;else return e+12},week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t={1:"ąÆ§",2:"ąÆØ",3:"ąÆ©",4:"ąÆŖ",5:"ąÆ«",6:"ąÆ¬",7:"ąÆ­",8:"ąÆ®",9:"ąÆÆ",0:"ąÆ¦"},n={"ąÆ§":"1","ąÆØ":"2","ąÆ©":"3","ąÆŖ":"4","ąÆ«":"5","ąÆ¬":"6","ąÆ­":"7","ąÆ®":"8","ąÆÆ":"9","ąÆ¦":"0"},a;e.defineLocale("ta",{months:"ą®œą®©ą®µą®°ą®æ_ą®Ŗą®æą®ŖąÆą®°ą®µą®°ą®æ_ą®®ą®¾ą®°ąÆą®šąÆ_ą®ą®ŖąÆą®°ą®²ąÆ_ą®®ąÆ‡_ą®œąÆ‚ą®©ąÆ_ą®œąÆ‚ą®²ąÆˆ_ą®†ą®•ą®øąÆą®ŸąÆ_ą®šąÆ†ą®ŖąÆą®ŸąÆ†ą®®ąÆą®Ŗą®°ąÆ_ą®…ą®•ąÆą®ŸąÆ‡ą®¾ą®Ŗą®°ąÆ_ą®Øą®µą®®ąÆą®Ŗą®°ąÆ_ą®Ÿą®æą®šą®®ąÆą®Ŗą®°ąÆ".split("_"),monthsShort:"ą®œą®©ą®µą®°ą®æ_ą®Ŗą®æą®ŖąÆą®°ą®µą®°ą®æ_ą®®ą®¾ą®°ąÆą®šąÆ_ą®ą®ŖąÆą®°ą®²ąÆ_ą®®ąÆ‡_ą®œąÆ‚ą®©ąÆ_ą®œąÆ‚ą®²ąÆˆ_ą®†ą®•ą®øąÆą®ŸąÆ_ą®šąÆ†ą®ŖąÆą®ŸąÆ†ą®®ąÆą®Ŗą®°ąÆ_ą®…ą®•ąÆą®ŸąÆ‡ą®¾ą®Ŗą®°ąÆ_ą®Øą®µą®®ąÆą®Ŗą®°ąÆ_ą®Ÿą®æą®šą®®ąÆą®Ŗą®°ąÆ".split("_"),weekdays:"ą®žą®¾ą®Æą®æą®±ąÆą®±ąÆą®•ąÆą®•ą®æą®“ą®®ąÆˆ_ą®¤ą®æą®™ąÆą®•ą®ŸąÆą®•ą®æą®“ą®®ąÆˆ_ą®šąÆ†ą®µąÆą®µą®¾ą®ÆąÆą®•ą®æą®“ą®®ąÆˆ_ą®ŖąÆą®¤ą®©ąÆą®•ą®æą®“ą®®ąÆˆ_ą®µą®æą®Æą®¾ą®“ą®•ąÆą®•ą®æą®“ą®®ąÆˆ_ą®µąÆ†ą®³ąÆą®³ą®æą®•ąÆą®•ą®æą®“ą®®ąÆˆ_ą®šą®©ą®æą®•ąÆą®•ą®æą®“ą®®ąÆˆ".split("_"),weekdaysShort:"ą®žą®¾ą®Æą®æą®±ąÆ_ą®¤ą®æą®™ąÆą®•ą®³ąÆ_ą®šąÆ†ą®µąÆą®µą®¾ą®ÆąÆ_ą®ŖąÆą®¤ą®©ąÆ_ą®µą®æą®Æą®¾ą®“ą®©ąÆ_ą®µąÆ†ą®³ąÆą®³ą®æ_ą®šą®©ą®æ".split("_"),weekdaysMin:"ą®žą®¾_ą®¤ą®æ_ą®šąÆ†_ą®ŖąÆ_ą®µą®æ_ą®µąÆ†_ą®š".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, HH:mm",LLLL:"dddd, D MMMM YYYY, HH:mm"},calendar:{sameDay:"[ą®‡ą®©ąÆą®±ąÆ] LT",nextDay:"[ą®Øą®¾ą®³ąÆˆ] LT",nextWeek:"dddd, LT",lastDay:"[ą®ØąÆ‡ą®±ąÆą®±ąÆ] LT",lastWeek:"[ą®•ą®Ÿą®ØąÆą®¤ ą®µą®¾ą®°ą®®ąÆ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą®‡ą®²ąÆ",past:"%s ą®®ąÆą®©ąÆ",s:"ą®’ą®°ąÆ ą®šą®æą®² ą®µą®æą®Øą®¾ą®Ÿą®æą®•ą®³ąÆ",ss:"%d ą®µą®æą®Øą®¾ą®Ÿą®æą®•ą®³ąÆ",m:"ą®’ą®°ąÆ ą®Øą®æą®®ą®æą®Ÿą®®ąÆ",mm:"%d ą®Øą®æą®®ą®æą®Ÿą®™ąÆą®•ą®³ąÆ",h:"ą®’ą®°ąÆ ą®®ą®£ą®æ ą®ØąÆ‡ą®°ą®®ąÆ",hh:"%d ą®®ą®£ą®æ ą®ØąÆ‡ą®°ą®®ąÆ",d:"ą®’ą®°ąÆ ą®Øą®¾ą®³ąÆ",dd:"%d ą®Øą®¾ą®ŸąÆą®•ą®³ąÆ",M:"ą®’ą®°ąÆ ą®®ą®¾ą®¤ą®®ąÆ",MM:"%d ą®®ą®¾ą®¤ą®™ąÆą®•ą®³ąÆ",y:"ą®’ą®°ąÆ ą®µą®°ąÆą®Ÿą®®ąÆ",yy:"%d ą®†ą®£ąÆą®ŸąÆą®•ą®³ąÆ"},dayOfMonthOrdinalParse:/\d{1,2}ą®µą®¤ąÆ/,ordinal:function(e){return e+"ą®µą®¤ąÆ"},preparse:function(e){return e.replace(/[ąÆ§ąÆØąÆ©ąÆŖąÆ«ąÆ¬ąÆ­ąÆ®ąÆÆąÆ¦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ą®Æą®¾ą®®ą®®ąÆ|ą®µąÆˆą®•ą®±ąÆˆ|ą®•ą®¾ą®²ąÆˆ|ą®Øą®£ąÆą®Ŗą®•ą®²ąÆ|ą®Žą®±ąÆą®Ŗą®¾ą®ŸąÆ|ą®®ą®¾ą®²ąÆˆ/,meridiem:function(e,t,n){if(e<2)return" ą®Æą®¾ą®®ą®®ąÆ";else if(e<6)return" ą®µąÆˆą®•ą®±ąÆˆ";else if(e<10)return" ą®•ą®¾ą®²ąÆˆ";else if(e<14)return" ą®Øą®£ąÆą®Ŗą®•ą®²ąÆ";else if(e<18)return" ą®Žą®±ąÆą®Ŗą®¾ą®ŸąÆ";else if(e<22)return" ą®®ą®¾ą®²ąÆˆ";else return" ą®Æą®¾ą®®ą®®ąÆ"},meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą®Æą®¾ą®®ą®®ąÆ")return e<2?e:e+12;else if(t==="ą®µąÆˆą®•ą®±ąÆˆ"||t==="ą®•ą®¾ą®²ąÆˆ")return e;else if(t==="ą®Øą®£ąÆą®Ŗą®•ą®²ąÆ")return e>=10?e:e+12;else return e+12},week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("te",{months:"ą°œą°Øą°µą°°ą°æ_ą°«ą°æą°¬ą±ą°°ą°µą°°ą°æ_ą°®ą°¾ą°°ą±ą°šą°æ_ą°ą°Ŗą±ą°°ą°æą°²ą±_ą°®ą±‡_ą°œą±‚ą°Øą±_ą°œą±ą°²ą±ˆ_ą°†ą°—ą°øą±ą°Ÿą±_ą°øą±†ą°Ŗą±ą°Ÿą±†ą°‚ą°¬ą°°ą±_ą°…ą°•ą±ą°Ÿą±‹ą°¬ą°°ą±_ą°Øą°µą°‚ą°¬ą°°ą±_ą°”ą°æą°øą±†ą°‚ą°¬ą°°ą±".split("_"),monthsShort:"ą°œą°Ø._ą°«ą°æą°¬ą±ą°°._ą°®ą°¾ą°°ą±ą°šą°æ_ą°ą°Ŗą±ą°°ą°æ._ą°®ą±‡_ą°œą±‚ą°Øą±_ą°œą±ą°²ą±ˆ_ą°†ą°—._ą°øą±†ą°Ŗą±._ą°…ą°•ą±ą°Ÿą±‹._ą°Øą°µ._ą°”ą°æą°øą±†.".split("_"),monthsParseExact:true,weekdays:"ą°†ą°¦ą°æą°µą°¾ą°°ą°‚_ą°øą±‹ą°®ą°µą°¾ą°°ą°‚_ą°®ą°‚ą°—ą°³ą°µą°¾ą°°ą°‚_ą°¬ą±ą°§ą°µą°¾ą°°ą°‚_ą°—ą±ą°°ą±ą°µą°¾ą°°ą°‚_ą°¶ą±ą°•ą±ą°°ą°µą°¾ą°°ą°‚_ą°¶ą°Øą°æą°µą°¾ą°°ą°‚".split("_"),weekdaysShort:"ą°†ą°¦ą°æ_ą°øą±‹ą°®_ą°®ą°‚ą°—ą°³_ą°¬ą±ą°§_ą°—ą±ą°°ą±_ą°¶ą±ą°•ą±ą°°_ą°¶ą°Øą°æ".split("_"),weekdaysMin:"ą°†_ą°øą±‹_ą°®ą°‚_ą°¬ą±_ą°—ą±_ą°¶ą±_ą°¶".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[ą°Øą±‡ą°”ą±] LT",nextDay:"[ą°°ą±‡ą°Ŗą±] LT",nextWeek:"dddd, LT",lastDay:"[ą°Øą°æą°Øą±ą°Ø] LT",lastWeek:"[ą°—ą°¤] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą°²ą±‹",past:"%s ą°•ą±ą°°ą°æą°¤ą°‚",s:"ą°•ą±Šą°Øą±ą°Øą°æ ą°•ą±ą°·ą°£ą°¾ą°²ą±",ss:"%d ą°øą±†ą°•ą°Øą±ą°²ą±",m:"ą°’ą°• ą°Øą°æą°®ą°æą°·ą°‚",mm:"%d ą°Øą°æą°®ą°æą°·ą°¾ą°²ą±",h:"ą°’ą°• ą°—ą°‚ą°Ÿ",hh:"%d ą°—ą°‚ą°Ÿą°²ą±",d:"ą°’ą°• ą°°ą±‹ą°œą±",dd:"%d ą°°ą±‹ą°œą±ą°²ą±",M:"ą°’ą°• ą°Øą±†ą°²",MM:"%d ą°Øą±†ą°²ą°²ą±",y:"ą°’ą°• ą°øą°‚ą°µą°¤ą±ą°øą°°ą°‚",yy:"%d ą°øą°‚ą°µą°¤ą±ą°øą°°ą°¾ą°²ą±"},dayOfMonthOrdinalParse:/\d{1,2}ą°µ/,ordinal:"%dą°µ",meridiemParse:/ą°°ą°¾ą°¤ą±ą°°ą°æ|ą°‰ą°¦ą°Æą°‚|ą°®ą°§ą±ą°Æą°¾ą°¹ą±ą°Øą°‚|ą°øą°¾ą°Æą°‚ą°¤ą±ą°°ą°‚/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą°°ą°¾ą°¤ą±ą°°ą°æ")return e<4?e:e+12;else if(t==="ą°‰ą°¦ą°Æą°‚")return e;else if(t==="ą°®ą°§ą±ą°Æą°¾ą°¹ą±ą°Øą°‚")return e>=10?e:e+12;else if(t==="ą°øą°¾ą°Æą°‚ą°¤ą±ą°°ą°‚")return e+12},meridiem:function(e,t,n){if(e<4)return"ą°°ą°¾ą°¤ą±ą°°ą°æ";else if(e<10)return"ą°‰ą°¦ą°Æą°‚";else if(e<17)return"ą°®ą°§ą±ą°Æą°¾ą°¹ą±ą°Øą°‚";else if(e<20)return"ą°øą°¾ą°Æą°‚ą°¤ą±ą°°ą°‚";else return"ą°°ą°¾ą°¤ą±ą°°ą°æ"},week:{dow:0,doy:6}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("te",{months:"ą°œą°Øą°µą°°ą°æ_ą°«ą°æą°¬ą±ą°°ą°µą°°ą°æ_ą°®ą°¾ą°°ą±ą°šą°æ_ą°ą°Ŗą±ą°°ą°æą°²ą±_ą°®ą±‡_ą°œą±‚ą°Øą±_ą°œą±ą°²ą±ˆ_ą°†ą°—ą°øą±ą°Ÿą±_ą°øą±†ą°Ŗą±ą°Ÿą±†ą°‚ą°¬ą°°ą±_ą°…ą°•ą±ą°Ÿą±‹ą°¬ą°°ą±_ą°Øą°µą°‚ą°¬ą°°ą±_ą°”ą°æą°øą±†ą°‚ą°¬ą°°ą±".split("_"),monthsShort:"ą°œą°Ø._ą°«ą°æą°¬ą±ą°°._ą°®ą°¾ą°°ą±ą°šą°æ_ą°ą°Ŗą±ą°°ą°æ._ą°®ą±‡_ą°œą±‚ą°Øą±_ą°œą±ą°²ą±ˆ_ą°†ą°—._ą°øą±†ą°Ŗą±._ą°…ą°•ą±ą°Ÿą±‹._ą°Øą°µ._ą°”ą°æą°øą±†.".split("_"),monthsParseExact:true,weekdays:"ą°†ą°¦ą°æą°µą°¾ą°°ą°‚_ą°øą±‹ą°®ą°µą°¾ą°°ą°‚_ą°®ą°‚ą°—ą°³ą°µą°¾ą°°ą°‚_ą°¬ą±ą°§ą°µą°¾ą°°ą°‚_ą°—ą±ą°°ą±ą°µą°¾ą°°ą°‚_ą°¶ą±ą°•ą±ą°°ą°µą°¾ą°°ą°‚_ą°¶ą°Øą°æą°µą°¾ą°°ą°‚".split("_"),weekdaysShort:"ą°†ą°¦ą°æ_ą°øą±‹ą°®_ą°®ą°‚ą°—ą°³_ą°¬ą±ą°§_ą°—ą±ą°°ą±_ą°¶ą±ą°•ą±ą°°_ą°¶ą°Øą°æ".split("_"),weekdaysMin:"ą°†_ą°øą±‹_ą°®ą°‚_ą°¬ą±_ą°—ą±_ą°¶ą±_ą°¶".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[ą°Øą±‡ą°”ą±] LT",nextDay:"[ą°°ą±‡ą°Ŗą±] LT",nextWeek:"dddd, LT",lastDay:"[ą°Øą°æą°Øą±ą°Ø] LT",lastWeek:"[ą°—ą°¤] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ą°²ą±‹",past:"%s ą°•ą±ą°°ą°æą°¤ą°‚",s:"ą°•ą±Šą°Øą±ą°Øą°æ ą°•ą±ą°·ą°£ą°¾ą°²ą±",ss:"%d ą°øą±†ą°•ą°Øą±ą°²ą±",m:"ą°’ą°• ą°Øą°æą°®ą°æą°·ą°‚",mm:"%d ą°Øą°æą°®ą°æą°·ą°¾ą°²ą±",h:"ą°’ą°• ą°—ą°‚ą°Ÿ",hh:"%d ą°—ą°‚ą°Ÿą°²ą±",d:"ą°’ą°• ą°°ą±‹ą°œą±",dd:"%d ą°°ą±‹ą°œą±ą°²ą±",M:"ą°’ą°• ą°Øą±†ą°²",MM:"%d ą°Øą±†ą°²ą°²ą±",y:"ą°’ą°• ą°øą°‚ą°µą°¤ą±ą°øą°°ą°‚",yy:"%d ą°øą°‚ą°µą°¤ą±ą°øą°°ą°¾ą°²ą±"},dayOfMonthOrdinalParse:/\d{1,2}ą°µ/,ordinal:"%dą°µ",meridiemParse:/ą°°ą°¾ą°¤ą±ą°°ą°æ|ą°‰ą°¦ą°Æą°‚|ą°®ą°§ą±ą°Æą°¾ą°¹ą±ą°Øą°‚|ą°øą°¾ą°Æą°‚ą°¤ą±ą°°ą°‚/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ą°°ą°¾ą°¤ą±ą°°ą°æ")return e<4?e:e+12;else if(t==="ą°‰ą°¦ą°Æą°‚")return e;else if(t==="ą°®ą°§ą±ą°Æą°¾ą°¹ą±ą°Øą°‚")return e>=10?e:e+12;else if(t==="ą°øą°¾ą°Æą°‚ą°¤ą±ą°°ą°‚")return e+12},meridiem:function(e,t,n){if(e<4)return"ą°°ą°¾ą°¤ą±ą°°ą°æ";else if(e<10)return"ą°‰ą°¦ą°Æą°‚";else if(e<17)return"ą°®ą°§ą±ą°Æą°¾ą°¹ą±ą°Øą°‚";else if(e<20)return"ą°øą°¾ą°Æą°‚ą°¤ą±ą°°ą°‚";else return"ą°°ą°¾ą°¤ą±ą°°ą°æ"},week:{dow:0,doy:6}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("tet",{months:"Janeiru_Fevereiru_Marsu_Abril_Maiu_JuƱu_Jullu_Agustu_Setembru_Outubru_Novembru_Dezembru".split("_"),monthsShort:"Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez".split("_"),weekdays:"Domingu_Segunda_Tersa_Kuarta_Kinta_Sesta_Sabadu".split("_"),weekdaysShort:"Dom_Seg_Ters_Kua_Kint_Sest_Sab".split("_"),weekdaysMin:"Do_Seg_Te_Ku_Ki_Ses_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Ohin iha] LT",nextDay:"[Aban iha] LT",nextWeek:"dddd [iha] LT",lastDay:"[Horiseik iha] LT",lastWeek:"dddd [semana kotuk] [iha] LT",sameElse:"L"},relativeTime:{future:"iha %s",past:"%s liuba",s:"segundu balun",ss:"segundu %d",m:"minutu ida",mm:"minutu %d",h:"oras ida",hh:"oras %d",d:"loron ida",dd:"loron %d",M:"fulan ida",MM:"fulan %d",y:"tinan ida",yy:"tinan %d"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("tet",{months:"Janeiru_Fevereiru_Marsu_Abril_Maiu_JuƱu_Jullu_Agustu_Setembru_Outubru_Novembru_Dezembru".split("_"),monthsShort:"Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez".split("_"),weekdays:"Domingu_Segunda_Tersa_Kuarta_Kinta_Sesta_Sabadu".split("_"),weekdaysShort:"Dom_Seg_Ters_Kua_Kint_Sest_Sab".split("_"),weekdaysMin:"Do_Seg_Te_Ku_Ki_Ses_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Ohin iha] LT",nextDay:"[Aban iha] LT",nextWeek:"dddd [iha] LT",lastDay:"[Horiseik iha] LT",lastWeek:"dddd [semana kotuk] [iha] LT",sameElse:"L"},relativeTime:{future:"iha %s",past:"%s liuba",s:"segundu balun",ss:"segundu %d",m:"minutu ida",mm:"minutu %d",h:"oras ida",hh:"oras %d",d:"loron ida",dd:"loron %d",M:"fulan ida",MM:"fulan %d",y:"tinan ida",yy:"tinan %d"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var a={0:"-уŠ¼",1:"-уŠ¼",2:"-юŠ¼",3:"-юŠ¼",4:"-уŠ¼",5:"-уŠ¼",6:"-уŠ¼",7:"-уŠ¼",8:"-уŠ¼",9:"-уŠ¼",10:"-уŠ¼",12:"-уŠ¼",13:"-уŠ¼",20:"-уŠ¼",30:"-юŠ¼",40:"-уŠ¼",50:"-уŠ¼",60:"-уŠ¼",70:"-уŠ¼",80:"-уŠ¼",90:"-уŠ¼",100:"-уŠ¼"},t;e.defineLocale("tg",{months:{format:"яŠ½Š²Š°Ń€Šø_фŠµŠ²Ń€Š°Š»Šø_Š¼Š°Ń€Ń‚Šø_Š°ŠæрŠµŠ»Šø_Š¼Š°Š¹Šø_ŠøюŠ½Šø_ŠøюŠ»Šø_Š°Š²Š³ŃƒŃŃ‚Šø_сŠµŠ½Ń‚яŠ±Ń€Šø_Š¾ŠŗтяŠ±Ń€Šø_Š½Š¾ŃŠ±Ń€Šø_Š“ŠµŠŗŠ°Š±Ń€Šø".split("_"),standalone:"яŠ½Š²Š°Ń€_фŠµŠ²Ń€Š°Š»_Š¼Š°Ń€Ń‚_Š°ŠæрŠµŠ»_Š¼Š°Š¹_ŠøюŠ½_ŠøюŠ»_Š°Š²Š³ŃƒŃŃ‚_сŠµŠ½Ń‚яŠ±Ń€_Š¾ŠŗтяŠ±Ń€_Š½Š¾ŃŠ±Ń€_Š“ŠµŠŗŠ°Š±Ń€".split("_")},monthsShort:"яŠ½Š²_фŠµŠ²_Š¼Š°Ń€_Š°Šæр_Š¼Š°Š¹_ŠøюŠ½_ŠøюŠ»_Š°Š²Š³_сŠµŠ½_Š¾Šŗт_Š½Š¾Ń_Š“ŠµŠŗ".split("_"),weekdays:"яŠŗшŠ°Š½Š±Šµ_Š“ушŠ°Š½Š±Šµ_сŠµŃˆŠ°Š½Š±Šµ_чŠ¾Ń€ŃˆŠ°Š½Š±Šµ_ŠæŠ°Š½Ņ·ŃˆŠ°Š½Š±Šµ_Ņ·ŃƒŠ¼ŃŠŠ°_шŠ°Š½Š±Šµ".split("_"),weekdaysShort:"яшŠ±_Š“шŠ±_сшŠ±_чшŠ±_ŠæшŠ±_Ņ·ŃƒŠ¼_шŠ½Š±".split("_"),weekdaysMin:"яш_Š“ш_сш_чш_Šæш_Ņ·Š¼_шŠ±".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Š˜Š¼Ń€ÓÆŠ· сŠ¾Š°Ń‚Šø] LT",nextDay:"[Š¤Š°Ń€Š“Š¾ сŠ¾Š°Ń‚Šø] LT",lastDay:"[Š”ŠøрÓÆŠ· сŠ¾Š°Ń‚Šø] LT",nextWeek:"dddd[Šø] [Ņ³Š°Ń„Ń‚Š°Šø Š¾ŃŠ½Š“Š° сŠ¾Š°Ń‚Šø] LT",lastWeek:"dddd[Šø] [Ņ³Š°Ń„Ń‚Š°Šø Š³ŃƒŠ·Š°ŃˆŃ‚Š° сŠ¾Š°Ń‚Šø] LT",sameElse:"L"},relativeTime:{future:"Š±Š°ŃŠŠ“Šø %s",past:"%s ŠæŠµŃˆ",s:"яŠŗчŠ°Š½Š“ сŠ¾Š½Šøя",m:"яŠŗ Š“Š°Ņ›ŠøŅ›Š°",mm:"%d Š“Š°Ņ›ŠøŅ›Š°",h:"яŠŗ сŠ¾Š°Ń‚",hh:"%d сŠ¾Š°Ń‚",d:"яŠŗ рÓÆŠ·",dd:"%d рÓÆŠ·",M:"яŠŗ Š¼Š¾Ņ³",MM:"%d Š¼Š¾Ņ³",y:"яŠŗ сŠ¾Š»",yy:"%d сŠ¾Š»"},meridiemParse:/шŠ°Š±|суŠ±Ņ³|рÓÆŠ·|Š±ŠµŠ³Š¾Ņ³/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="шŠ°Š±")return e<4?e:e+12;else if(t==="суŠ±Ņ³")return e;else if(t==="рÓÆŠ·")return e>=11?e:e+12;else if(t==="Š±ŠµŠ³Š¾Ņ³")return e+12},meridiem:function(e,t,n){if(e<4)return"шŠ°Š±";else if(e<11)return"суŠ±Ņ³";else if(e<16)return"рÓÆŠ·";else if(e<19)return"Š±ŠµŠ³Š¾Ņ³";else return"шŠ°Š±"},dayOfMonthOrdinalParse:/\d{1,2}-(уŠ¼|юŠ¼)/,ordinal:function(e){var t=e%10,n=e>=100?100:null;return e+(a[e]||a[t]||a[n])},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var a={0:"-уŠ¼",1:"-уŠ¼",2:"-юŠ¼",3:"-юŠ¼",4:"-уŠ¼",5:"-уŠ¼",6:"-уŠ¼",7:"-уŠ¼",8:"-уŠ¼",9:"-уŠ¼",10:"-уŠ¼",12:"-уŠ¼",13:"-уŠ¼",20:"-уŠ¼",30:"-юŠ¼",40:"-уŠ¼",50:"-уŠ¼",60:"-уŠ¼",70:"-уŠ¼",80:"-уŠ¼",90:"-уŠ¼",100:"-уŠ¼"},t;e.defineLocale("tg",{months:{format:"яŠ½Š²Š°Ń€Šø_фŠµŠ²Ń€Š°Š»Šø_Š¼Š°Ń€Ń‚Šø_Š°ŠæрŠµŠ»Šø_Š¼Š°Š¹Šø_ŠøюŠ½Šø_ŠøюŠ»Šø_Š°Š²Š³ŃƒŃŃ‚Šø_сŠµŠ½Ń‚яŠ±Ń€Šø_Š¾ŠŗтяŠ±Ń€Šø_Š½Š¾ŃŠ±Ń€Šø_Š“ŠµŠŗŠ°Š±Ń€Šø".split("_"),standalone:"яŠ½Š²Š°Ń€_фŠµŠ²Ń€Š°Š»_Š¼Š°Ń€Ń‚_Š°ŠæрŠµŠ»_Š¼Š°Š¹_ŠøюŠ½_ŠøюŠ»_Š°Š²Š³ŃƒŃŃ‚_сŠµŠ½Ń‚яŠ±Ń€_Š¾ŠŗтяŠ±Ń€_Š½Š¾ŃŠ±Ń€_Š“ŠµŠŗŠ°Š±Ń€".split("_")},monthsShort:"яŠ½Š²_фŠµŠ²_Š¼Š°Ń€_Š°Šæр_Š¼Š°Š¹_ŠøюŠ½_ŠøюŠ»_Š°Š²Š³_сŠµŠ½_Š¾Šŗт_Š½Š¾Ń_Š“ŠµŠŗ".split("_"),weekdays:"яŠŗшŠ°Š½Š±Šµ_Š“ушŠ°Š½Š±Šµ_сŠµŃˆŠ°Š½Š±Šµ_чŠ¾Ń€ŃˆŠ°Š½Š±Šµ_ŠæŠ°Š½Ņ·ŃˆŠ°Š½Š±Šµ_Ņ·ŃƒŠ¼ŃŠŠ°_шŠ°Š½Š±Šµ".split("_"),weekdaysShort:"яшŠ±_Š“шŠ±_сшŠ±_чшŠ±_ŠæшŠ±_Ņ·ŃƒŠ¼_шŠ½Š±".split("_"),weekdaysMin:"яш_Š“ш_сш_чш_Šæш_Ņ·Š¼_шŠ±".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Š˜Š¼Ń€ÓÆŠ· сŠ¾Š°Ń‚Šø] LT",nextDay:"[Š¤Š°Ń€Š“Š¾ сŠ¾Š°Ń‚Šø] LT",lastDay:"[Š”ŠøрÓÆŠ· сŠ¾Š°Ń‚Šø] LT",nextWeek:"dddd[Šø] [Ņ³Š°Ń„Ń‚Š°Šø Š¾ŃŠ½Š“Š° сŠ¾Š°Ń‚Šø] LT",lastWeek:"dddd[Šø] [Ņ³Š°Ń„Ń‚Š°Šø Š³ŃƒŠ·Š°ŃˆŃ‚Š° сŠ¾Š°Ń‚Šø] LT",sameElse:"L"},relativeTime:{future:"Š±Š°ŃŠŠ“Šø %s",past:"%s ŠæŠµŃˆ",s:"яŠŗчŠ°Š½Š“ сŠ¾Š½Šøя",m:"яŠŗ Š“Š°Ņ›ŠøŅ›Š°",mm:"%d Š“Š°Ņ›ŠøŅ›Š°",h:"яŠŗ сŠ¾Š°Ń‚",hh:"%d сŠ¾Š°Ń‚",d:"яŠŗ рÓÆŠ·",dd:"%d рÓÆŠ·",M:"яŠŗ Š¼Š¾Ņ³",MM:"%d Š¼Š¾Ņ³",y:"яŠŗ сŠ¾Š»",yy:"%d сŠ¾Š»"},meridiemParse:/шŠ°Š±|суŠ±Ņ³|рÓÆŠ·|Š±ŠµŠ³Š¾Ņ³/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="шŠ°Š±")return e<4?e:e+12;else if(t==="суŠ±Ņ³")return e;else if(t==="рÓÆŠ·")return e>=11?e:e+12;else if(t==="Š±ŠµŠ³Š¾Ņ³")return e+12},meridiem:function(e,t,n){if(e<4)return"шŠ°Š±";else if(e<11)return"суŠ±Ņ³";else if(e<16)return"рÓÆŠ·";else if(e<19)return"Š±ŠµŠ³Š¾Ņ³";else return"шŠ°Š±"},dayOfMonthOrdinalParse:/\d{1,2}-(уŠ¼|юŠ¼)/,ordinal:function(e){var t=e%10,n=e>=100?100:null;return e+(a[e]||a[t]||a[n])},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("th",{months:"ąø”ąøąø£ąø²ąø„ąø”_ąøąøøąø”ąø ąø²ąøžąø±ąø™ąø˜ą¹Œ_ąø”ąøµąø™ąø²ąø„ąø”_ą¹€ąø”ąø©ąø²ąø¢ąø™_ąøžąø¤ąø©ąø ąø²ąø„ąø”_ąø”ąø“ąø–ąøøąø™ąø²ąø¢ąø™_ąøąø£ąøąøŽąø²ąø„ąø”_ąøŖąø“ąø‡ąø«ąø²ąø„ąø”_ąøąø±ąø™ąø¢ąø²ąø¢ąø™_ąø•ąøøąø„ąø²ąø„ąø”_ąøžąø¤ąøØąøˆąø“ąøąø²ąø¢ąø™_ąø˜ąø±ąø™ąø§ąø²ąø„ąø”".split("_"),monthsShort:"ąø”.ąø„._ąø.ąøž._ąø”ąøµ.ąø„._ą¹€ąø”.ąø¢._ąøž.ąø„._ąø”ąø“.ąø¢._ąø.ąø„._ąøŖ.ąø„._ąø.ąø¢._ąø•.ąø„._ąøž.ąø¢._ąø˜.ąø„.".split("_"),monthsParseExact:true,weekdays:"ąø­ąø²ąø—ąø“ąø•ąø¢ą¹Œ_ąøˆąø±ąø™ąø—ąø£ą¹Œ_ąø­ąø±ąø‡ąø„ąø²ąø£_ąøžąøøąø˜_ąøžąø¤ąø«ąø±ąøŖąøšąø”ąøµ_ąøØąøøąøąø£ą¹Œ_ą¹€ąøŖąø²ąø£ą¹Œ".split("_"),weekdaysShort:"ąø­ąø²ąø—ąø“ąø•ąø¢ą¹Œ_ąøˆąø±ąø™ąø—ąø£ą¹Œ_ąø­ąø±ąø‡ąø„ąø²ąø£_ąøžąøøąø˜_ąøžąø¤ąø«ąø±ąøŖ_ąøØąøøąøąø£ą¹Œ_ą¹€ąøŖąø²ąø£ą¹Œ".split("_"),weekdaysMin:"ąø­ąø²._ąøˆ._ąø­._ąøž._ąøžąø¤._ąøØ._ąøŖ.".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY ą¹€ąø§ąø„ąø² H:mm",LLLL:"ąø§ąø±ąø™ddddąø—ąøµą¹ˆ D MMMM YYYY ą¹€ąø§ąø„ąø² H:mm"},meridiemParse:/ąøą¹ˆąø­ąø™ą¹€ąø—ąøµą¹ˆąø¢ąø‡|ąø«ąø„ąø±ąø‡ą¹€ąø—ąøµą¹ˆąø¢ąø‡/,isPM:function(e){return e==="ąø«ąø„ąø±ąø‡ą¹€ąø—ąøµą¹ˆąø¢ąø‡"},meridiem:function(e,t,n){if(e<12)return"ąøą¹ˆąø­ąø™ą¹€ąø—ąøµą¹ˆąø¢ąø‡";else return"ąø«ąø„ąø±ąø‡ą¹€ąø—ąøµą¹ˆąø¢ąø‡"},calendar:{sameDay:"[ąø§ąø±ąø™ąø™ąøµą¹‰ ą¹€ąø§ąø„ąø²] LT",nextDay:"[ąøžąø£ąøøą¹ˆąø‡ąø™ąøµą¹‰ ą¹€ąø§ąø„ąø²] LT",nextWeek:"dddd[ąø«ąø™ą¹‰ąø² ą¹€ąø§ąø„ąø²] LT",lastDay:"[ą¹€ąø”ąø·ą¹ˆąø­ąø§ąø²ąø™ąø™ąøµą¹‰ ą¹€ąø§ąø„ąø²] LT",lastWeek:"[ąø§ąø±ąø™]dddd[ąø—ąøµą¹ˆą¹ąø„ą¹‰ąø§ ą¹€ąø§ąø„ąø²] LT",sameElse:"L"},relativeTime:{future:"ąø­ąøµąø %s",past:"%sąø—ąøµą¹ˆą¹ąø„ą¹‰ąø§",s:"ą¹„ąø”ą¹ˆąøąøµą¹ˆąø§ąø“ąø™ąø²ąø—ąøµ",ss:"%d ąø§ąø“ąø™ąø²ąø—ąøµ",m:"1 ąø™ąø²ąø—ąøµ",mm:"%d ąø™ąø²ąø—ąøµ",h:"1 ąøŠąø±ą¹ˆąø§ą¹‚ąø”ąø‡",hh:"%d ąøŠąø±ą¹ˆąø§ą¹‚ąø”ąø‡",d:"1 ąø§ąø±ąø™",dd:"%d ąø§ąø±ąø™",w:"1 ąøŖąø±ąø›ąø”ąø²ąø«ą¹Œ",ww:"%d ąøŖąø±ąø›ąø”ąø²ąø«ą¹Œ",M:"1 ą¹€ąø”ąø·ąø­ąø™",MM:"%d ą¹€ąø”ąø·ąø­ąø™",y:"1 ąø›ąøµ",yy:"%d ąø›ąøµ"}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("th",{months:"ąø”ąøąø£ąø²ąø„ąø”_ąøąøøąø”ąø ąø²ąøžąø±ąø™ąø˜ą¹Œ_ąø”ąøµąø™ąø²ąø„ąø”_ą¹€ąø”ąø©ąø²ąø¢ąø™_ąøžąø¤ąø©ąø ąø²ąø„ąø”_ąø”ąø“ąø–ąøøąø™ąø²ąø¢ąø™_ąøąø£ąøąøŽąø²ąø„ąø”_ąøŖąø“ąø‡ąø«ąø²ąø„ąø”_ąøąø±ąø™ąø¢ąø²ąø¢ąø™_ąø•ąøøąø„ąø²ąø„ąø”_ąøžąø¤ąøØąøˆąø“ąøąø²ąø¢ąø™_ąø˜ąø±ąø™ąø§ąø²ąø„ąø”".split("_"),monthsShort:"ąø”.ąø„._ąø.ąøž._ąø”ąøµ.ąø„._ą¹€ąø”.ąø¢._ąøž.ąø„._ąø”ąø“.ąø¢._ąø.ąø„._ąøŖ.ąø„._ąø.ąø¢._ąø•.ąø„._ąøž.ąø¢._ąø˜.ąø„.".split("_"),monthsParseExact:true,weekdays:"ąø­ąø²ąø—ąø“ąø•ąø¢ą¹Œ_ąøˆąø±ąø™ąø—ąø£ą¹Œ_ąø­ąø±ąø‡ąø„ąø²ąø£_ąøžąøøąø˜_ąøžąø¤ąø«ąø±ąøŖąøšąø”ąøµ_ąøØąøøąøąø£ą¹Œ_ą¹€ąøŖąø²ąø£ą¹Œ".split("_"),weekdaysShort:"ąø­ąø²ąø—ąø“ąø•ąø¢ą¹Œ_ąøˆąø±ąø™ąø—ąø£ą¹Œ_ąø­ąø±ąø‡ąø„ąø²ąø£_ąøžąøøąø˜_ąøžąø¤ąø«ąø±ąøŖ_ąøØąøøąøąø£ą¹Œ_ą¹€ąøŖąø²ąø£ą¹Œ".split("_"),weekdaysMin:"ąø­ąø²._ąøˆ._ąø­._ąøž._ąøžąø¤._ąøØ._ąøŖ.".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY ą¹€ąø§ąø„ąø² H:mm",LLLL:"ąø§ąø±ąø™ddddąø—ąøµą¹ˆ D MMMM YYYY ą¹€ąø§ąø„ąø² H:mm"},meridiemParse:/ąøą¹ˆąø­ąø™ą¹€ąø—ąøµą¹ˆąø¢ąø‡|ąø«ąø„ąø±ąø‡ą¹€ąø—ąøµą¹ˆąø¢ąø‡/,isPM:function(e){return e==="ąø«ąø„ąø±ąø‡ą¹€ąø—ąøµą¹ˆąø¢ąø‡"},meridiem:function(e,t,n){if(e<12)return"ąøą¹ˆąø­ąø™ą¹€ąø—ąøµą¹ˆąø¢ąø‡";else return"ąø«ąø„ąø±ąø‡ą¹€ąø—ąøµą¹ˆąø¢ąø‡"},calendar:{sameDay:"[ąø§ąø±ąø™ąø™ąøµą¹‰ ą¹€ąø§ąø„ąø²] LT",nextDay:"[ąøžąø£ąøøą¹ˆąø‡ąø™ąøµą¹‰ ą¹€ąø§ąø„ąø²] LT",nextWeek:"dddd[ąø«ąø™ą¹‰ąø² ą¹€ąø§ąø„ąø²] LT",lastDay:"[ą¹€ąø”ąø·ą¹ˆąø­ąø§ąø²ąø™ąø™ąøµą¹‰ ą¹€ąø§ąø„ąø²] LT",lastWeek:"[ąø§ąø±ąø™]dddd[ąø—ąøµą¹ˆą¹ąø„ą¹‰ąø§ ą¹€ąø§ąø„ąø²] LT",sameElse:"L"},relativeTime:{future:"ąø­ąøµąø %s",past:"%sąø—ąøµą¹ˆą¹ąø„ą¹‰ąø§",s:"ą¹„ąø”ą¹ˆąøąøµą¹ˆąø§ąø“ąø™ąø²ąø—ąøµ",ss:"%d ąø§ąø“ąø™ąø²ąø—ąøµ",m:"1 ąø™ąø²ąø—ąøµ",mm:"%d ąø™ąø²ąø—ąøµ",h:"1 ąøŠąø±ą¹ˆąø§ą¹‚ąø”ąø‡",hh:"%d ąøŠąø±ą¹ˆąø§ą¹‚ąø”ąø‡",d:"1 ąø§ąø±ąø™",dd:"%d ąø§ąø±ąø™",w:"1 ąøŖąø±ąø›ąø”ąø²ąø«ą¹Œ",ww:"%d ąøŖąø±ąø›ąø”ąø²ąø«ą¹Œ",M:"1 ą¹€ąø”ąø·ąø­ąø™",MM:"%d ą¹€ąø”ąø·ąø­ąø™",y:"1 ąø›ąøµ",yy:"%d ąø›ąøµ"}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var o={1:"'inji",5:"'inji",8:"'inji",70:"'inji",80:"'inji",2:"'nji",7:"'nji",20:"'nji",50:"'nji",3:"'Ć¼nji",4:"'Ć¼nji",100:"'Ć¼nji",6:"'njy",9:"'unjy",10:"'unjy",30:"'unjy",60:"'ynjy",90:"'ynjy"},t;e.defineLocale("tk",{months:"Ɲanwar_Fewral_Mart_Aprel_MaĆ½_IĆ½un_IĆ½ul_Awgust_SentĆ½abr_OktĆ½abr_NoĆ½abr_Dekabr".split("_"),monthsShort:"Ɲan_Few_Mar_Apr_MaĆ½_IĆ½n_IĆ½l_Awg_Sen_Okt_NoĆ½_Dek".split("_"),weekdays:"Ɲekşenbe_Duşenbe_Sişenbe_Ƈarşenbe_Penşenbe_Anna_Şenbe".split("_"),weekdaysShort:"Ɲek_Duş_Siş_Ƈar_Pen_Ann_Şen".split("_"),weekdaysMin:"Ɲk_Dş_Sş_Ƈr_Pn_An_Şn".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bugĆ¼n sagat] LT",nextDay:"[ertir sagat] LT",nextWeek:"[indiki] dddd [sagat] LT",lastDay:"[dĆ¼Ć½n] LT",lastWeek:"[geƧen] dddd [sagat] LT",sameElse:"L"},relativeTime:{future:"%s soň",past:"%s Ć¶Åˆ",s:"birnƤƧe sekunt",m:"bir minut",mm:"%d minut",h:"bir sagat",hh:"%d sagat",d:"bir gĆ¼n",dd:"%d gĆ¼n",M:"bir aĆ½",MM:"%d aĆ½",y:"bir Ć½yl",yy:"%d Ć½yl"},ordinal:function(e,t){switch(t){case"d":case"D":case"Do":case"DD":return e;default:if(e===0)return e+"'unjy";var n=e%10,a=e%100-n,r=e>=100?100:null;return e+(o[n]||o[a]||o[r])}},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var o={1:"'inji",5:"'inji",8:"'inji",70:"'inji",80:"'inji",2:"'nji",7:"'nji",20:"'nji",50:"'nji",3:"'Ć¼nji",4:"'Ć¼nji",100:"'Ć¼nji",6:"'njy",9:"'unjy",10:"'unjy",30:"'unjy",60:"'ynjy",90:"'ynjy"},t;e.defineLocale("tk",{months:"Ɲanwar_Fewral_Mart_Aprel_MaĆ½_IĆ½un_IĆ½ul_Awgust_SentĆ½abr_OktĆ½abr_NoĆ½abr_Dekabr".split("_"),monthsShort:"Ɲan_Few_Mar_Apr_MaĆ½_IĆ½n_IĆ½l_Awg_Sen_Okt_NoĆ½_Dek".split("_"),weekdays:"Ɲekşenbe_Duşenbe_Sişenbe_Ƈarşenbe_Penşenbe_Anna_Şenbe".split("_"),weekdaysShort:"Ɲek_Duş_Siş_Ƈar_Pen_Ann_Şen".split("_"),weekdaysMin:"Ɲk_Dş_Sş_Ƈr_Pn_An_Şn".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bugĆ¼n sagat] LT",nextDay:"[ertir sagat] LT",nextWeek:"[indiki] dddd [sagat] LT",lastDay:"[dĆ¼Ć½n] LT",lastWeek:"[geƧen] dddd [sagat] LT",sameElse:"L"},relativeTime:{future:"%s soň",past:"%s Ć¶Åˆ",s:"birnƤƧe sekunt",m:"bir minut",mm:"%d minut",h:"bir sagat",hh:"%d sagat",d:"bir gĆ¼n",dd:"%d gĆ¼n",M:"bir aĆ½",MM:"%d aĆ½",y:"bir Ć½yl",yy:"%d Ć½yl"},ordinal:function(e,t){switch(t){case"d":case"D":case"Do":case"DD":return e;default:if(e===0)return e+"'unjy";var n=e%10,a=e%100-n,r=e>=100?100:null;return e+(o[n]||o[a]||o[r])}},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("tl-ph",{months:"Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre".split("_"),monthsShort:"Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis".split("_"),weekdays:"Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado".split("_"),weekdaysShort:"Lin_Lun_Mar_Miy_Huw_Biy_Sab".split("_"),weekdaysMin:"Li_Lu_Ma_Mi_Hu_Bi_Sab".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"MM/D/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY HH:mm",LLLL:"dddd, MMMM DD, YYYY HH:mm"},calendar:{sameDay:"LT [ngayong araw]",nextDay:"[Bukas ng] LT",nextWeek:"LT [sa susunod na] dddd",lastDay:"LT [kahapon]",lastWeek:"LT [noong nakaraang] dddd",sameElse:"L"},relativeTime:{future:"sa loob ng %s",past:"%s ang nakalipas",s:"ilang segundo",ss:"%d segundo",m:"isang minuto",mm:"%d minuto",h:"isang oras",hh:"%d oras",d:"isang araw",dd:"%d araw",M:"isang buwan",MM:"%d buwan",y:"isang taon",yy:"%d taon"},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:function(e){return e},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("tl-ph",{months:"Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre".split("_"),monthsShort:"Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis".split("_"),weekdays:"Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado".split("_"),weekdaysShort:"Lin_Lun_Mar_Miy_Huw_Biy_Sab".split("_"),weekdaysMin:"Li_Lu_Ma_Mi_Hu_Bi_Sab".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"MM/D/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY HH:mm",LLLL:"dddd, MMMM DD, YYYY HH:mm"},calendar:{sameDay:"LT [ngayong araw]",nextDay:"[Bukas ng] LT",nextWeek:"LT [sa susunod na] dddd",lastDay:"LT [kahapon]",lastWeek:"LT [noong nakaraang] dddd",sameElse:"L"},relativeTime:{future:"sa loob ng %s",past:"%s ang nakalipas",s:"ilang segundo",ss:"%d segundo",m:"isang minuto",mm:"%d minuto",h:"isang oras",hh:"%d oras",d:"isang araw",dd:"%d araw",M:"isang buwan",MM:"%d buwan",y:"isang taon",yy:"%d taon"},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:function(e){return e},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var o="pagh_waā€™_chaā€™_wej_loS_vagh_jav_Soch_chorgh_Hut".split("_"),t;function n(e){var t=e;t=e.indexOf("jaj")!==-1?t.slice(0,-3)+"leS":e.indexOf("jar")!==-1?t.slice(0,-3)+"waQ":e.indexOf("DIS")!==-1?t.slice(0,-3)+"nem":t+" pIq";return t}function a(e){var t=e;t=e.indexOf("jaj")!==-1?t.slice(0,-3)+"Huā€™":e.indexOf("jar")!==-1?t.slice(0,-3)+"wen":e.indexOf("DIS")!==-1?t.slice(0,-3)+"ben":t+" ret";return t}function r(e,t,n,a){var r=i(e);switch(n){case"ss":return r+" lup";case"mm":return r+" tup";case"hh":return r+" rep";case"dd":return r+" jaj";case"MM":return r+" jar";case"yy":return r+" DIS"}}function i(e){var t=Math.floor(e%1e3/100),n=Math.floor(e%100/10),a=e%10,r="";if(t>0)r+=o[t]+"vatlh";if(n>0)r+=(r!==""?" ":"")+o[n]+"maH";if(a>0)r+=(r!==""?" ":"")+o[a];return r===""?"pagh":r}e.defineLocale("tlh",{months:"teraā€™ jar waā€™_teraā€™ jar chaā€™_teraā€™ jar wej_teraā€™ jar loS_teraā€™ jar vagh_teraā€™ jar jav_teraā€™ jar Soch_teraā€™ jar chorgh_teraā€™ jar Hut_teraā€™ jar waā€™maH_teraā€™ jar waā€™maH waā€™_teraā€™ jar waā€™maH chaā€™".split("_"),monthsShort:"jar waā€™_jar chaā€™_jar wej_jar loS_jar vagh_jar jav_jar Soch_jar chorgh_jar Hut_jar waā€™maH_jar waā€™maH waā€™_jar waā€™maH chaā€™".split("_"),monthsParseExact:true,weekdays:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),weekdaysShort:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),weekdaysMin:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[DaHjaj] LT",nextDay:"[waā€™leS] LT",nextWeek:"LLL",lastDay:"[waā€™Huā€™] LT",lastWeek:"LLL",sameElse:"L"},relativeTime:{future:n,past:a,s:"puS lup",ss:r,m:"waā€™ tup",mm:r,h:"waā€™ rep",hh:r,d:"waā€™ jaj",dd:r,M:"waā€™ jar",MM:r,y:"waā€™ DIS",yy:r},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var o="pagh_waā€™_chaā€™_wej_loS_vagh_jav_Soch_chorgh_Hut".split("_"),t;function n(e){var t=e;t=e.indexOf("jaj")!==-1?t.slice(0,-3)+"leS":e.indexOf("jar")!==-1?t.slice(0,-3)+"waQ":e.indexOf("DIS")!==-1?t.slice(0,-3)+"nem":t+" pIq";return t}function a(e){var t=e;t=e.indexOf("jaj")!==-1?t.slice(0,-3)+"Huā€™":e.indexOf("jar")!==-1?t.slice(0,-3)+"wen":e.indexOf("DIS")!==-1?t.slice(0,-3)+"ben":t+" ret";return t}function r(e,t,n,a){var r=i(e);switch(n){case"ss":return r+" lup";case"mm":return r+" tup";case"hh":return r+" rep";case"dd":return r+" jaj";case"MM":return r+" jar";case"yy":return r+" DIS"}}function i(e){var t=Math.floor(e%1e3/100),n=Math.floor(e%100/10),a=e%10,r="";if(t>0)r+=o[t]+"vatlh";if(n>0)r+=(r!==""?" ":"")+o[n]+"maH";if(a>0)r+=(r!==""?" ":"")+o[a];return r===""?"pagh":r}e.defineLocale("tlh",{months:"teraā€™ jar waā€™_teraā€™ jar chaā€™_teraā€™ jar wej_teraā€™ jar loS_teraā€™ jar vagh_teraā€™ jar jav_teraā€™ jar Soch_teraā€™ jar chorgh_teraā€™ jar Hut_teraā€™ jar waā€™maH_teraā€™ jar waā€™maH waā€™_teraā€™ jar waā€™maH chaā€™".split("_"),monthsShort:"jar waā€™_jar chaā€™_jar wej_jar loS_jar vagh_jar jav_jar Soch_jar chorgh_jar Hut_jar waā€™maH_jar waā€™maH waā€™_jar waā€™maH chaā€™".split("_"),monthsParseExact:true,weekdays:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),weekdaysShort:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),weekdaysMin:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[DaHjaj] LT",nextDay:"[waā€™leS] LT",nextWeek:"LLL",lastDay:"[waā€™Huā€™] LT",lastWeek:"LLL",sameElse:"L"},relativeTime:{future:n,past:a,s:"puS lup",ss:r,m:"waā€™ tup",mm:r,h:"waā€™ rep",hh:r,d:"waā€™ jaj",dd:r,M:"waā€™ jar",MM:r,y:"waā€™ DIS",yy:r},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var o={1:"'inci",5:"'inci",8:"'inci",70:"'inci",80:"'inci",2:"'nci",7:"'nci",20:"'nci",50:"'nci",3:"'Ć¼ncĆ¼",4:"'Ć¼ncĆ¼",100:"'Ć¼ncĆ¼",6:"'ncı",9:"'uncu",10:"'uncu",30:"'uncu",60:"'ıncı",90:"'ıncı"},t;e.defineLocale("tr",{months:"Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_EylĆ¼l_Ekim_Kasım_Aralık".split("_"),monthsShort:"Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara".split("_"),weekdays:"Pazar_Pazartesi_Salı_Ƈarşamba_Perşembe_Cuma_Cumartesi".split("_"),weekdaysShort:"Paz_Pzt_Sal_Ƈar_Per_Cum_Cmt".split("_"),weekdaysMin:"Pz_Pt_Sa_Ƈa_Pe_Cu_Ct".split("_"),meridiem:function(e,t,n){if(e<12)return n?"ƶƶ":"ƖƖ";else return n?"ƶs":"ƖS"},meridiemParse:/ƶƶ|ƖƖ|ƶs|ƖS/,isPM:function(e){return e==="ƶs"||e==="ƖS"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bugĆ¼n saat] LT",nextDay:"[yarın saat] LT",nextWeek:"[gelecek] dddd [saat] LT",lastDay:"[dĆ¼n] LT",lastWeek:"[geƧen] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s ƶnce",s:"birkaƧ saniye",ss:"%d saniye",m:"bir dakika",mm:"%d dakika",h:"bir saat",hh:"%d saat",d:"bir gĆ¼n",dd:"%d gĆ¼n",w:"bir hafta",ww:"%d hafta",M:"bir ay",MM:"%d ay",y:"bir yıl",yy:"%d yıl"},ordinal:function(e,t){switch(t){case"d":case"D":case"Do":case"DD":return e;default:if(e===0)return e+"'ıncı";var n=e%10,a=e%100-n,r=e>=100?100:null;return e+(o[n]||o[a]||o[r])}},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var o={1:"'inci",5:"'inci",8:"'inci",70:"'inci",80:"'inci",2:"'nci",7:"'nci",20:"'nci",50:"'nci",3:"'Ć¼ncĆ¼",4:"'Ć¼ncĆ¼",100:"'Ć¼ncĆ¼",6:"'ncı",9:"'uncu",10:"'uncu",30:"'uncu",60:"'ıncı",90:"'ıncı"},t;e.defineLocale("tr",{months:"Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_EylĆ¼l_Ekim_Kasım_Aralık".split("_"),monthsShort:"Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara".split("_"),weekdays:"Pazar_Pazartesi_Salı_Ƈarşamba_Perşembe_Cuma_Cumartesi".split("_"),weekdaysShort:"Paz_Pzt_Sal_Ƈar_Per_Cum_Cmt".split("_"),weekdaysMin:"Pz_Pt_Sa_Ƈa_Pe_Cu_Ct".split("_"),meridiem:function(e,t,n){if(e<12)return n?"ƶƶ":"ƖƖ";else return n?"ƶs":"ƖS"},meridiemParse:/ƶƶ|ƖƖ|ƶs|ƖS/,isPM:function(e){return e==="ƶs"||e==="ƖS"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bugĆ¼n saat] LT",nextDay:"[yarın saat] LT",nextWeek:"[gelecek] dddd [saat] LT",lastDay:"[dĆ¼n] LT",lastWeek:"[geƧen] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s ƶnce",s:"birkaƧ saniye",ss:"%d saniye",m:"bir dakika",mm:"%d dakika",h:"bir saat",hh:"%d saat",d:"bir gĆ¼n",dd:"%d gĆ¼n",w:"bir hafta",ww:"%d hafta",M:"bir ay",MM:"%d ay",y:"bir yıl",yy:"%d yıl"},ordinal:function(e,t){switch(t){case"d":case"D":case"Do":case"DD":return e;default:if(e===0)return e+"'ıncı";var n=e%10,a=e%100-n,r=e>=100?100:null;return e+(o[n]||o[a]||o[r])}},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;function n(e,t,n,a){var r={s:["viensas secunds","'iensas secunds"],ss:[e+" secunds",""+e+" secunds"],m:["'n mĆ­ut","'iens mĆ­ut"],mm:[e+" mĆ­uts",""+e+" mĆ­uts"],h:["'n Ć¾ora","'iensa Ć¾ora"],hh:[e+" Ć¾oras",""+e+" Ć¾oras"],d:["'n ziua","'iensa ziua"],dd:[e+" ziuas",""+e+" ziuas"],M:["'n mes","'iens mes"],MM:[e+" mesen",""+e+" mesen"],y:["'n ar","'iens ar"],yy:[e+" ars",""+e+" ars"]};return a?r[n][0]:t?r[n][0]:r[n][1]}e.defineLocale("tzl",{months:"Januar_Fevraglh_MarƧ_AvrĆÆu_Mai_GĆ¼n_Julia_Guscht_Setemvar_ListopƤts_Noemvar_Zecemvar".split("_"),monthsShort:"Jan_Fev_Mar_Avr_Mai_GĆ¼n_Jul_Gus_Set_Lis_Noe_Zec".split("_"),weekdays:"SĆŗladi_LĆŗneƧi_Maitzi_MĆ”rcuri_XhĆŗadi_ViĆ©nerƧi_SĆ”turi".split("_"),weekdaysShort:"SĆŗl_LĆŗn_Mai_MĆ”r_XhĆŗ_ViĆ©_SĆ”t".split("_"),weekdaysMin:"SĆŗ_LĆŗ_Ma_MĆ”_Xh_Vi_SĆ”".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"D. MMMM [dallas] YYYY",LLL:"D. MMMM [dallas] YYYY HH.mm",LLLL:"dddd, [li] D. MMMM [dallas] YYYY HH.mm"},meridiemParse:/d\'o|d\'a/i,isPM:function(e){return"d'o"===e.toLowerCase()},meridiem:function(e,t,n){if(e>11)return n?"d'o":"D'O";else return n?"d'a":"D'A"},calendar:{sameDay:"[oxhi Ć ] LT",nextDay:"[demĆ  Ć ] LT",nextWeek:"dddd [Ć ] LT",lastDay:"[ieiri Ć ] LT",lastWeek:"[sĆ¼r el] dddd [lasteu Ć ] LT",sameElse:"L"},relativeTime:{future:"osprei %s",past:"ja%s",s:n,ss:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;function n(e,t,n,a){var r={s:["viensas secunds","'iensas secunds"],ss:[e+" secunds",""+e+" secunds"],m:["'n mĆ­ut","'iens mĆ­ut"],mm:[e+" mĆ­uts",""+e+" mĆ­uts"],h:["'n Ć¾ora","'iensa Ć¾ora"],hh:[e+" Ć¾oras",""+e+" Ć¾oras"],d:["'n ziua","'iensa ziua"],dd:[e+" ziuas",""+e+" ziuas"],M:["'n mes","'iens mes"],MM:[e+" mesen",""+e+" mesen"],y:["'n ar","'iens ar"],yy:[e+" ars",""+e+" ars"]};return a?r[n][0]:t?r[n][0]:r[n][1]}e.defineLocale("tzl",{months:"Januar_Fevraglh_MarƧ_AvrĆÆu_Mai_GĆ¼n_Julia_Guscht_Setemvar_ListopƤts_Noemvar_Zecemvar".split("_"),monthsShort:"Jan_Fev_Mar_Avr_Mai_GĆ¼n_Jul_Gus_Set_Lis_Noe_Zec".split("_"),weekdays:"SĆŗladi_LĆŗneƧi_Maitzi_MĆ”rcuri_XhĆŗadi_ViĆ©nerƧi_SĆ”turi".split("_"),weekdaysShort:"SĆŗl_LĆŗn_Mai_MĆ”r_XhĆŗ_ViĆ©_SĆ”t".split("_"),weekdaysMin:"SĆŗ_LĆŗ_Ma_MĆ”_Xh_Vi_SĆ”".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"D. MMMM [dallas] YYYY",LLL:"D. MMMM [dallas] YYYY HH.mm",LLLL:"dddd, [li] D. MMMM [dallas] YYYY HH.mm"},meridiemParse:/d\'o|d\'a/i,isPM:function(e){return"d'o"===e.toLowerCase()},meridiem:function(e,t,n){if(e>11)return n?"d'o":"D'O";else return n?"d'a":"D'A"},calendar:{sameDay:"[oxhi Ć ] LT",nextDay:"[demĆ  Ć ] LT",nextWeek:"dddd [Ć ] LT",lastDay:"[ieiri Ć ] LT",lastWeek:"[sĆ¼r el] dddd [lasteu Ć ] LT",sameElse:"L"},relativeTime:{future:"osprei %s",past:"ja%s",s:n,ss:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("tzm",{months:"āµ‰āµāµā“°āµ¢āµ”_ā“±āµ•ā“°āµ¢āµ•_āµŽā“°āµ•āµš_āµ‰ā“±āµ”āµ‰āµ”_āµŽā“°āµ¢āµ¢āµ“_āµ¢āµ“āµāµ¢āµ“_āµ¢āµ“āµāµ¢āµ“āµ£_āµ–āµ“āµ›āµœ_āµ›āµ“āµœā“°āµā“±āµ‰āµ”_ā“½āµŸāµ“ā“±āµ•_āµāµ“āµ”ā“°āµā“±āµ‰āµ”_ā“·āµ“āµŠāµā“±āµ‰āµ”".split("_"),monthsShort:"āµ‰āµāµā“°āµ¢āµ”_ā“±āµ•ā“°āµ¢āµ•_āµŽā“°āµ•āµš_āµ‰ā“±āµ”āµ‰āµ”_āµŽā“°āµ¢āµ¢āµ“_āµ¢āµ“āµāµ¢āµ“_āµ¢āµ“āµāµ¢āµ“āµ£_āµ–āµ“āµ›āµœ_āµ›āµ“āµœā“°āµā“±āµ‰āµ”_ā“½āµŸāµ“ā“±āµ•_āµāµ“āµ”ā“°āµā“±āµ‰āµ”_ā“·āµ“āµŠāµā“±āµ‰āµ”".split("_"),weekdays:"ā“°āµ™ā“°āµŽā“°āµ™_ā“°āµ¢āµā“°āµ™_ā“°āµ™āµ‰āµā“°āµ™_ā“°ā“½āµ”ā“°āµ™_ā“°ā“½āµ”ā“°āµ™_ā“°āµ™āµ‰āµŽāµ”ā“°āµ™_ā“°āµ™āµ‰ā“¹āµ¢ā“°āµ™".split("_"),weekdaysShort:"ā“°āµ™ā“°āµŽā“°āµ™_ā“°āµ¢āµā“°āµ™_ā“°āµ™āµ‰āµā“°āµ™_ā“°ā“½āµ”ā“°āµ™_ā“°ā“½āµ”ā“°āµ™_ā“°āµ™āµ‰āµŽāµ”ā“°āµ™_ā“°āµ™āµ‰ā“¹āµ¢ā“°āµ™".split("_"),weekdaysMin:"ā“°āµ™ā“°āµŽā“°āµ™_ā“°āµ¢āµā“°āµ™_ā“°āµ™āµ‰āµā“°āµ™_ā“°ā“½āµ”ā“°āµ™_ā“°ā“½āµ”ā“°āµ™_ā“°āµ™āµ‰āµŽāµ”ā“°āµ™_ā“°āµ™āµ‰ā“¹āµ¢ā“°āµ™".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[ā“°āµ™ā“·āµ… ā““] LT",nextDay:"[ā“°āµ™ā“½ā“° ā““] LT",nextWeek:"dddd [ā““] LT",lastDay:"[ā“°āµšā“°āµāµœ ā““] LT",lastWeek:"dddd [ā““] LT",sameElse:"L"},relativeTime:{future:"ā“·ā“°ā“·āµ… āµ™ āµ¢ā“°āµ %s",past:"āµ¢ā“°āµ %s",s:"āµ‰āµŽāµ‰ā“½",ss:"%d āµ‰āµŽāµ‰ā“½",m:"āµŽāµ‰āµāµ“ā“ŗ",mm:"%d āµŽāµ‰āµāµ“ā“ŗ",h:"āµ™ā“°āµ„ā“°",hh:"%d āµœā“°āµ™āµ™ā“°āµ„āµ‰āµ",d:"ā“°āµ™āµ™",dd:"%d oāµ™āµ™ā“°āµ",M:"ā“°āµ¢oāµ“āµ”",MM:"%d āµ‰āµ¢āµ¢āµ‰āµ”āµ",y:"ā“°āµ™ā“³ā“°āµ™",yy:"%d āµ‰āµ™ā“³ā“°āµ™āµ"},week:{dow:6,doy:12}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("tzm",{months:"āµ‰āµāµā“°āµ¢āµ”_ā“±āµ•ā“°āµ¢āµ•_āµŽā“°āµ•āµš_āµ‰ā“±āµ”āµ‰āµ”_āµŽā“°āµ¢āµ¢āµ“_āµ¢āµ“āµāµ¢āµ“_āµ¢āµ“āµāµ¢āµ“āµ£_āµ–āµ“āµ›āµœ_āµ›āµ“āµœā“°āµā“±āµ‰āµ”_ā“½āµŸāµ“ā“±āµ•_āµāµ“āµ”ā“°āµā“±āµ‰āµ”_ā“·āµ“āµŠāµā“±āµ‰āµ”".split("_"),monthsShort:"āµ‰āµāµā“°āµ¢āµ”_ā“±āµ•ā“°āµ¢āµ•_āµŽā“°āµ•āµš_āµ‰ā“±āµ”āµ‰āµ”_āµŽā“°āµ¢āµ¢āµ“_āµ¢āµ“āµāµ¢āµ“_āµ¢āµ“āµāµ¢āµ“āµ£_āµ–āµ“āµ›āµœ_āµ›āµ“āµœā“°āµā“±āµ‰āµ”_ā“½āµŸāµ“ā“±āµ•_āµāµ“āµ”ā“°āµā“±āµ‰āµ”_ā“·āµ“āµŠāµā“±āµ‰āµ”".split("_"),weekdays:"ā“°āµ™ā“°āµŽā“°āµ™_ā“°āµ¢āµā“°āµ™_ā“°āµ™āµ‰āµā“°āµ™_ā“°ā“½āµ”ā“°āµ™_ā“°ā“½āµ”ā“°āµ™_ā“°āµ™āµ‰āµŽāµ”ā“°āµ™_ā“°āµ™āµ‰ā“¹āµ¢ā“°āµ™".split("_"),weekdaysShort:"ā“°āµ™ā“°āµŽā“°āµ™_ā“°āµ¢āµā“°āµ™_ā“°āµ™āµ‰āµā“°āµ™_ā“°ā“½āµ”ā“°āµ™_ā“°ā“½āµ”ā“°āµ™_ā“°āµ™āµ‰āµŽāµ”ā“°āµ™_ā“°āµ™āµ‰ā“¹āµ¢ā“°āµ™".split("_"),weekdaysMin:"ā“°āµ™ā“°āµŽā“°āµ™_ā“°āµ¢āµā“°āµ™_ā“°āµ™āµ‰āµā“°āµ™_ā“°ā“½āµ”ā“°āµ™_ā“°ā“½āµ”ā“°āµ™_ā“°āµ™āµ‰āµŽāµ”ā“°āµ™_ā“°āµ™āµ‰ā“¹āµ¢ā“°āµ™".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[ā“°āµ™ā“·āµ… ā““] LT",nextDay:"[ā“°āµ™ā“½ā“° ā““] LT",nextWeek:"dddd [ā““] LT",lastDay:"[ā“°āµšā“°āµāµœ ā““] LT",lastWeek:"dddd [ā““] LT",sameElse:"L"},relativeTime:{future:"ā“·ā“°ā“·āµ… āµ™ āµ¢ā“°āµ %s",past:"āµ¢ā“°āµ %s",s:"āµ‰āµŽāµ‰ā“½",ss:"%d āµ‰āµŽāµ‰ā“½",m:"āµŽāµ‰āµāµ“ā“ŗ",mm:"%d āµŽāµ‰āµāµ“ā“ŗ",h:"āµ™ā“°āµ„ā“°",hh:"%d āµœā“°āµ™āµ™ā“°āµ„āµ‰āµ",d:"ā“°āµ™āµ™",dd:"%d oāµ™āµ™ā“°āµ",M:"ā“°āµ¢oāµ“āµ”",MM:"%d āµ‰āµ¢āµ¢āµ‰āµ”āµ",y:"ā“°āµ™ā“³ā“°āµ™",yy:"%d āµ‰āµ™ā“³ā“°āµ™āµ"},week:{dow:6,doy:12}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("tzm-latn",{months:"innayr_brĖ¤ayrĖ¤_marĖ¤sĖ¤_ibrir_mayyw_ywnyw_ywlywz_É£wÅ”t_Å”wtanbir_ktĖ¤wbrĖ¤_nwwanbir_dwjnbir".split("_"),monthsShort:"innayr_brĖ¤ayrĖ¤_marĖ¤sĖ¤_ibrir_mayyw_ywnyw_ywlywz_É£wÅ”t_Å”wtanbir_ktĖ¤wbrĖ¤_nwwanbir_dwjnbir".split("_"),weekdays:"asamas_aynas_asinas_akras_akwas_asimwas_asiįøyas".split("_"),weekdaysShort:"asamas_aynas_asinas_akras_akwas_asimwas_asiįøyas".split("_"),weekdaysMin:"asamas_aynas_asinas_akras_akwas_asimwas_asiįøyas".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[asdkh g] LT",nextDay:"[aska g] LT",nextWeek:"dddd [g] LT",lastDay:"[assant g] LT",lastWeek:"dddd [g] LT",sameElse:"L"},relativeTime:{future:"dadkh s yan %s",past:"yan %s",s:"imik",ss:"%d imik",m:"minuįø",mm:"%d minuįø",h:"saɛa",hh:"%d tassaɛin",d:"ass",dd:"%d ossan",M:"ayowr",MM:"%d iyyirn",y:"asgas",yy:"%d isgasn"},week:{dow:6,doy:12}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("tzm-latn",{months:"innayr_brĖ¤ayrĖ¤_marĖ¤sĖ¤_ibrir_mayyw_ywnyw_ywlywz_É£wÅ”t_Å”wtanbir_ktĖ¤wbrĖ¤_nwwanbir_dwjnbir".split("_"),monthsShort:"innayr_brĖ¤ayrĖ¤_marĖ¤sĖ¤_ibrir_mayyw_ywnyw_ywlywz_É£wÅ”t_Å”wtanbir_ktĖ¤wbrĖ¤_nwwanbir_dwjnbir".split("_"),weekdays:"asamas_aynas_asinas_akras_akwas_asimwas_asiįøyas".split("_"),weekdaysShort:"asamas_aynas_asinas_akras_akwas_asimwas_asiįøyas".split("_"),weekdaysMin:"asamas_aynas_asinas_akras_akwas_asimwas_asiįøyas".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[asdkh g] LT",nextDay:"[aska g] LT",nextWeek:"dddd [g] LT",lastDay:"[assant g] LT",lastWeek:"dddd [g] LT",sameElse:"L"},relativeTime:{future:"dadkh s yan %s",past:"yan %s",s:"imik",ss:"%d imik",m:"minuįø",mm:"%d minuįø",h:"saɛa",hh:"%d tassaɛin",d:"ass",dd:"%d ossan",M:"ayowr",MM:"%d iyyirn",y:"asgas",yy:"%d isgasn"},week:{dow:6,doy:12}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("ug-cn",{months:"ŁŠŲ§Ł†Ū‹Ų§Ų±_ŁŪŪ‹Ų±Ų§Ł„_Ł…Ų§Ų±ŲŖ_Ų¦Ų§Ł¾Ų±ŪŁ„_Ł…Ų§ŁŠ_Ų¦Ł‰ŁŠŪ‡Ł†_Ų¦Ł‰ŁŠŪ‡Ł„_Ų¦Ų§Ū‹ŲŗŪ‡Ų³ŲŖ_Ų³ŪŁ†ŲŖŪ•ŲØŁ‰Ų±_Ų¦Ū†ŁƒŲŖŪ•ŲØŁ‰Ų±_Ł†ŁˆŁŠŲ§ŲØŁ‰Ų±_ŲÆŪŁƒŲ§ŲØŁ‰Ų±".split("_"),monthsShort:"ŁŠŲ§Ł†Ū‹Ų§Ų±_ŁŪŪ‹Ų±Ų§Ł„_Ł…Ų§Ų±ŲŖ_Ų¦Ų§Ł¾Ų±ŪŁ„_Ł…Ų§ŁŠ_Ų¦Ł‰ŁŠŪ‡Ł†_Ų¦Ł‰ŁŠŪ‡Ł„_Ų¦Ų§Ū‹ŲŗŪ‡Ų³ŲŖ_Ų³ŪŁ†ŲŖŪ•ŲØŁ‰Ų±_Ų¦Ū†ŁƒŲŖŪ•ŲØŁ‰Ų±_Ł†ŁˆŁŠŲ§ŲØŁ‰Ų±_ŲÆŪŁƒŲ§ŲØŁ‰Ų±".split("_"),weekdays:"ŁŠŪ•ŁƒŲ“Ū•Ł†ŲØŪ•_ŲÆŪˆŲ“Ū•Ł†ŲØŪ•_Ų³Ū•ŁŠŲ“Ū•Ł†ŲØŪ•_Ś†Ų§Ų±Ų“Ū•Ł†ŲØŪ•_Ł¾Ū•ŁŠŲ“Ū•Ł†ŲØŪ•_Ų¬ŪˆŁ…Ū•_Ų“Ū•Ł†ŲØŪ•".split("_"),weekdaysShort:"ŁŠŪ•_ŲÆŪˆ_Ų³Ū•_Ś†Ų§_Ł¾Ū•_Ų¬Ūˆ_Ų“Ū•".split("_"),weekdaysMin:"ŁŠŪ•_ŲÆŪˆ_Ų³Ū•_Ś†Ų§_Ł¾Ū•_Ų¬Ūˆ_Ų“Ū•".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY-ŁŠŁ‰Ł„Ł‰M-Ų¦Ų§ŁŠŁ†Ł‰Ś­D-ŁƒŪˆŁ†Ł‰",LLL:"YYYY-ŁŠŁ‰Ł„Ł‰M-Ų¦Ų§ŁŠŁ†Ł‰Ś­D-ŁƒŪˆŁ†Ł‰ŲŒ HH:mm",LLLL:"ddddŲŒ YYYY-ŁŠŁ‰Ł„Ł‰M-Ų¦Ų§ŁŠŁ†Ł‰Ś­D-ŁƒŪˆŁ†Ł‰ŲŒ HH:mm"},meridiemParse:/ŁŠŪŲ±Ł‰Ł… ŁƒŪŚ†Ū•|Ų³Ū•Ś¾Ū•Ų±|Ś†ŪˆŲ“ŲŖŁ‰Ł† ŲØŪ‡Ų±Ū‡Ł†|Ś†ŪˆŲ“|Ś†ŪˆŲ“ŲŖŁ‰Ł† ŁƒŪŁŠŁ‰Ł†|ŁƒŪ•Ś†/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ŁŠŪŲ±Ł‰Ł… ŁƒŪŚ†Ū•"||t==="Ų³Ū•Ś¾Ū•Ų±"||t==="Ś†ŪˆŲ“ŲŖŁ‰Ł† ŲØŪ‡Ų±Ū‡Ł†")return e;else if(t==="Ś†ŪˆŲ“ŲŖŁ‰Ł† ŁƒŪŁŠŁ‰Ł†"||t==="ŁƒŪ•Ś†")return e+12;else return e>=11?e:e+12},meridiem:function(e,t,n){var a=e*100+t;if(a<600)return"ŁŠŪŲ±Ł‰Ł… ŁƒŪŚ†Ū•";else if(a<900)return"Ų³Ū•Ś¾Ū•Ų±";else if(a<1130)return"Ś†ŪˆŲ“ŲŖŁ‰Ł† ŲØŪ‡Ų±Ū‡Ł†";else if(a<1230)return"Ś†ŪˆŲ“";else if(a<1800)return"Ś†ŪˆŲ“ŲŖŁ‰Ł† ŁƒŪŁŠŁ‰Ł†";else return"ŁƒŪ•Ś†"},calendar:{sameDay:"[ŲØŪˆŚÆŪˆŁ† Ų³Ų§Ų¦Ū•ŲŖ] LT",nextDay:"[Ų¦Ū•ŲŖŪ• Ų³Ų§Ų¦Ū•ŲŖ] LT",nextWeek:"[ŁƒŪŁ„Ū•Ų±ŁƒŁ‰] dddd [Ų³Ų§Ų¦Ū•ŲŖ] LT",lastDay:"[ŲŖŪ†Ł†ŪˆŚÆŪˆŁ†] LT",lastWeek:"[Ų¦Ų§Ł„ŲÆŁ‰Ł†Ł‚Ł‰] dddd [Ų³Ų§Ų¦Ū•ŲŖ] LT",sameElse:"L"},relativeTime:{future:"%s ŁƒŪŁŠŁ‰Ł†",past:"%s ŲØŪ‡Ų±Ū‡Ł†",s:"Ł†Ū•Ś†Ś†Ū• Ų³ŪŁƒŁˆŁ†ŲŖ",ss:"%d Ų³ŪŁƒŁˆŁ†ŲŖ",m:"ŲØŁ‰Ų± Ł…Ł‰Ł†Ū‡ŲŖ",mm:"%d Ł…Ł‰Ł†Ū‡ŲŖ",h:"ŲØŁ‰Ų± Ų³Ų§Ų¦Ū•ŲŖ",hh:"%d Ų³Ų§Ų¦Ū•ŲŖ",d:"ŲØŁ‰Ų± ŁƒŪˆŁ†",dd:"%d ŁƒŪˆŁ†",M:"ŲØŁ‰Ų± Ų¦Ų§ŁŠ",MM:"%d Ų¦Ų§ŁŠ",y:"ŲØŁ‰Ų± ŁŠŁ‰Ł„",yy:"%d ŁŠŁ‰Ł„"},dayOfMonthOrdinalParse:/\d{1,2}(-ŁƒŪˆŁ†Ł‰|-Ų¦Ų§ŁŠ|-Ś¾Ū•Ł¾ŲŖŪ•)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"-ŁƒŪˆŁ†Ł‰";case"w":case"W":return e+"-Ś¾Ū•Ł¾ŲŖŪ•";default:return e}},preparse:function(e){return e.replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/,/g,"ŲŒ")},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("ug-cn",{months:"ŁŠŲ§Ł†Ū‹Ų§Ų±_ŁŪŪ‹Ų±Ų§Ł„_Ł…Ų§Ų±ŲŖ_Ų¦Ų§Ł¾Ų±ŪŁ„_Ł…Ų§ŁŠ_Ų¦Ł‰ŁŠŪ‡Ł†_Ų¦Ł‰ŁŠŪ‡Ł„_Ų¦Ų§Ū‹ŲŗŪ‡Ų³ŲŖ_Ų³ŪŁ†ŲŖŪ•ŲØŁ‰Ų±_Ų¦Ū†ŁƒŲŖŪ•ŲØŁ‰Ų±_Ł†ŁˆŁŠŲ§ŲØŁ‰Ų±_ŲÆŪŁƒŲ§ŲØŁ‰Ų±".split("_"),monthsShort:"ŁŠŲ§Ł†Ū‹Ų§Ų±_ŁŪŪ‹Ų±Ų§Ł„_Ł…Ų§Ų±ŲŖ_Ų¦Ų§Ł¾Ų±ŪŁ„_Ł…Ų§ŁŠ_Ų¦Ł‰ŁŠŪ‡Ł†_Ų¦Ł‰ŁŠŪ‡Ł„_Ų¦Ų§Ū‹ŲŗŪ‡Ų³ŲŖ_Ų³ŪŁ†ŲŖŪ•ŲØŁ‰Ų±_Ų¦Ū†ŁƒŲŖŪ•ŲØŁ‰Ų±_Ł†ŁˆŁŠŲ§ŲØŁ‰Ų±_ŲÆŪŁƒŲ§ŲØŁ‰Ų±".split("_"),weekdays:"ŁŠŪ•ŁƒŲ“Ū•Ł†ŲØŪ•_ŲÆŪˆŲ“Ū•Ł†ŲØŪ•_Ų³Ū•ŁŠŲ“Ū•Ł†ŲØŪ•_Ś†Ų§Ų±Ų“Ū•Ł†ŲØŪ•_Ł¾Ū•ŁŠŲ“Ū•Ł†ŲØŪ•_Ų¬ŪˆŁ…Ū•_Ų“Ū•Ł†ŲØŪ•".split("_"),weekdaysShort:"ŁŠŪ•_ŲÆŪˆ_Ų³Ū•_Ś†Ų§_Ł¾Ū•_Ų¬Ūˆ_Ų“Ū•".split("_"),weekdaysMin:"ŁŠŪ•_ŲÆŪˆ_Ų³Ū•_Ś†Ų§_Ł¾Ū•_Ų¬Ūˆ_Ų“Ū•".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY-ŁŠŁ‰Ł„Ł‰M-Ų¦Ų§ŁŠŁ†Ł‰Ś­D-ŁƒŪˆŁ†Ł‰",LLL:"YYYY-ŁŠŁ‰Ł„Ł‰M-Ų¦Ų§ŁŠŁ†Ł‰Ś­D-ŁƒŪˆŁ†Ł‰ŲŒ HH:mm",LLLL:"ddddŲŒ YYYY-ŁŠŁ‰Ł„Ł‰M-Ų¦Ų§ŁŠŁ†Ł‰Ś­D-ŁƒŪˆŁ†Ł‰ŲŒ HH:mm"},meridiemParse:/ŁŠŪŲ±Ł‰Ł… ŁƒŪŚ†Ū•|Ų³Ū•Ś¾Ū•Ų±|Ś†ŪˆŲ“ŲŖŁ‰Ł† ŲØŪ‡Ų±Ū‡Ł†|Ś†ŪˆŲ“|Ś†ŪˆŲ“ŲŖŁ‰Ł† ŁƒŪŁŠŁ‰Ł†|ŁƒŪ•Ś†/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="ŁŠŪŲ±Ł‰Ł… ŁƒŪŚ†Ū•"||t==="Ų³Ū•Ś¾Ū•Ų±"||t==="Ś†ŪˆŲ“ŲŖŁ‰Ł† ŲØŪ‡Ų±Ū‡Ł†")return e;else if(t==="Ś†ŪˆŲ“ŲŖŁ‰Ł† ŁƒŪŁŠŁ‰Ł†"||t==="ŁƒŪ•Ś†")return e+12;else return e>=11?e:e+12},meridiem:function(e,t,n){var a=e*100+t;if(a<600)return"ŁŠŪŲ±Ł‰Ł… ŁƒŪŚ†Ū•";else if(a<900)return"Ų³Ū•Ś¾Ū•Ų±";else if(a<1130)return"Ś†ŪˆŲ“ŲŖŁ‰Ł† ŲØŪ‡Ų±Ū‡Ł†";else if(a<1230)return"Ś†ŪˆŲ“";else if(a<1800)return"Ś†ŪˆŲ“ŲŖŁ‰Ł† ŁƒŪŁŠŁ‰Ł†";else return"ŁƒŪ•Ś†"},calendar:{sameDay:"[ŲØŪˆŚÆŪˆŁ† Ų³Ų§Ų¦Ū•ŲŖ] LT",nextDay:"[Ų¦Ū•ŲŖŪ• Ų³Ų§Ų¦Ū•ŲŖ] LT",nextWeek:"[ŁƒŪŁ„Ū•Ų±ŁƒŁ‰] dddd [Ų³Ų§Ų¦Ū•ŲŖ] LT",lastDay:"[ŲŖŪ†Ł†ŪˆŚÆŪˆŁ†] LT",lastWeek:"[Ų¦Ų§Ł„ŲÆŁ‰Ł†Ł‚Ł‰] dddd [Ų³Ų§Ų¦Ū•ŲŖ] LT",sameElse:"L"},relativeTime:{future:"%s ŁƒŪŁŠŁ‰Ł†",past:"%s ŲØŪ‡Ų±Ū‡Ł†",s:"Ł†Ū•Ś†Ś†Ū• Ų³ŪŁƒŁˆŁ†ŲŖ",ss:"%d Ų³ŪŁƒŁˆŁ†ŲŖ",m:"ŲØŁ‰Ų± Ł…Ł‰Ł†Ū‡ŲŖ",mm:"%d Ł…Ł‰Ł†Ū‡ŲŖ",h:"ŲØŁ‰Ų± Ų³Ų§Ų¦Ū•ŲŖ",hh:"%d Ų³Ų§Ų¦Ū•ŲŖ",d:"ŲØŁ‰Ų± ŁƒŪˆŁ†",dd:"%d ŁƒŪˆŁ†",M:"ŲØŁ‰Ų± Ų¦Ų§ŁŠ",MM:"%d Ų¦Ų§ŁŠ",y:"ŲØŁ‰Ų± ŁŠŁ‰Ł„",yy:"%d ŁŠŁ‰Ł„"},dayOfMonthOrdinalParse:/\d{1,2}(-ŁƒŪˆŁ†Ł‰|-Ų¦Ų§ŁŠ|-Ś¾Ū•Ł¾ŲŖŪ•)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"-ŁƒŪˆŁ†Ł‰";case"w":case"W":return e+"-Ś¾Ū•Ł¾ŲŖŪ•";default:return e}},preparse:function(e){return e.replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/,/g,"ŲŒ")},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -function r(e,t){var n=e.split("_");return t%10===1&&t%100!==11?n[0]:t%10>=2&&t%10<=4&&(t%100<10||t%100>=20)?n[1]:n[2]}function t(e,t,n){var a={ss:t?"сŠµŠŗуŠ½Š“Š°_сŠµŠŗуŠ½Š“Šø_сŠµŠŗуŠ½Š“":"сŠµŠŗуŠ½Š“у_сŠµŠŗуŠ½Š“Šø_сŠµŠŗуŠ½Š“",mm:t?"хŠ²ŠøŠ»ŠøŠ½Š°_хŠ²ŠøŠ»ŠøŠ½Šø_хŠ²ŠøŠ»ŠøŠ½":"хŠ²ŠøŠ»ŠøŠ½Ńƒ_хŠ²ŠøŠ»ŠøŠ½Šø_хŠ²ŠøŠ»ŠøŠ½",hh:t?"Š³Š¾Š“ŠøŠ½Š°_Š³Š¾Š“ŠøŠ½Šø_Š³Š¾Š“ŠøŠ½":"Š³Š¾Š“ŠøŠ½Ńƒ_Š³Š¾Š“ŠøŠ½Šø_Š³Š¾Š“ŠøŠ½",dd:"Š“ŠµŠ½ŃŒ_Š“Š½Ń–_Š“Š½Ń–Š²",MM:"Š¼Ń–ŃŃŃ†ŃŒ_Š¼Ń–ŃŃŃ†Ń–_Š¼Ń–ŃŃŃ†Ń–Š²",yy:"ріŠŗ_рŠ¾ŠŗŠø_рŠ¾ŠŗіŠ²"};if(n==="m")return t?"хŠ²ŠøŠ»ŠøŠ½Š°":"хŠ²ŠøŠ»ŠøŠ½Ńƒ";else if(n==="h")return t?"Š³Š¾Š“ŠøŠ½Š°":"Š³Š¾Š“ŠøŠ½Ńƒ";else return e+" "+r(a[n],+e)}function n(e,t){var n={nominative:"Š½ŠµŠ“іŠ»Ń_ŠæŠ¾Š½ŠµŠ“іŠ»Š¾Šŗ_Š²Ń–Š²Ń‚Š¾Ń€Š¾Šŗ_сŠµŃ€ŠµŠ“Š°_чŠµŃ‚Š²ŠµŃ€_Šæā€™ŃŃ‚Š½Šøця_суŠ±Š¾Ń‚Š°".split("_"),accusative:"Š½ŠµŠ“іŠ»ŃŽ_ŠæŠ¾Š½ŠµŠ“іŠ»Š¾Šŗ_Š²Ń–Š²Ń‚Š¾Ń€Š¾Šŗ_сŠµŃ€ŠµŠ“у_чŠµŃ‚Š²ŠµŃ€_Šæā€™ŃŃ‚Š½Šøцю_суŠ±Š¾Ń‚Ńƒ".split("_"),genitive:"Š½ŠµŠ“іŠ»Ń–_ŠæŠ¾Š½ŠµŠ“іŠ»ŠŗŠ°_Š²Ń–Š²Ń‚Š¾Ń€ŠŗŠ°_сŠµŃ€ŠµŠ“Šø_чŠµŃ‚Š²ŠµŃ€Š³Š°_Šæā€™ŃŃ‚Š½Šøці_суŠ±Š¾Ń‚Šø".split("_")},a;if(e===true)return n["nominative"].slice(1,7).concat(n["nominative"].slice(0,1));if(!e)return n["nominative"];a=/(\[[Š’Š²Š£Ńƒ]\]) ?dddd/.test(t)?"accusative":/\[?(?:Š¼ŠøŠ½ŃƒŠ»Š¾Ń—|Š½Š°ŃŃ‚ŃƒŠæŠ½Š¾Ń—)? ?\] ?dddd/.test(t)?"genitive":"nominative";return n[a][e.day()]}function a(e){return function(){return e+"Š¾"+(this.hours()===11?"Š±":"")+"] LT"}}var o;e.defineLocale("uk",{months:{format:"січŠ½Ń_Š»ŃŽŃ‚Š¾Š³Š¾_Š±ŠµŃ€ŠµŠ·Š½Ń_ŠŗŠ²Ń–Ń‚Š½Ń_трŠ°Š²Š½Ń_чŠµŃ€Š²Š½Ń_Š»ŠøŠæŠ½Ń_сŠµŃ€ŠæŠ½Ń_Š²ŠµŃ€ŠµŃŠ½Ń_Š¶Š¾Š²Ń‚Š½Ń_Š»ŠøстŠ¾ŠæŠ°Š“Š°_Š³Ń€ŃƒŠ“Š½Ń".split("_"),standalone:"січŠµŠ½ŃŒ_Š»ŃŽŃ‚ŠøŠ¹_Š±ŠµŃ€ŠµŠ·ŠµŠ½ŃŒ_ŠŗŠ²Ń–Ń‚ŠµŠ½ŃŒ_трŠ°Š²ŠµŠ½ŃŒ_чŠµŃ€Š²ŠµŠ½ŃŒ_Š»ŠøŠæŠµŠ½ŃŒ_сŠµŃ€ŠæŠµŠ½ŃŒ_Š²ŠµŃ€ŠµŃŠµŠ½ŃŒ_Š¶Š¾Š²Ń‚ŠµŠ½ŃŒ_Š»ŠøстŠ¾ŠæŠ°Š“_Š³Ń€ŃƒŠ“ŠµŠ½ŃŒ".split("_")},monthsShort:"січ_Š»ŃŽŃ‚_Š±ŠµŃ€_ŠŗŠ²Ń–Ń‚_трŠ°Š²_чŠµŃ€Š²_Š»ŠøŠæ_сŠµŃ€Šæ_Š²ŠµŃ€_Š¶Š¾Š²Ń‚_Š»Šøст_Š³Ń€ŃƒŠ“".split("_"),weekdays:n,weekdaysShort:"Š½Š“_ŠæŠ½_Š²Ń‚_ср_чт_Šæт_сŠ±".split("_"),weekdaysMin:"Š½Š“_ŠæŠ½_Š²Ń‚_ср_чт_Šæт_сŠ±".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY р.",LLL:"D MMMM YYYY р., HH:mm",LLLL:"dddd, D MMMM YYYY р., HH:mm"},calendar:{sameDay:a("[Š”ьŠ¾Š³Š¾Š“Š½Ń– "),nextDay:a("[Š—Š°Š²Ń‚Ń€Š° "),lastDay:a("[Š’чŠ¾Ń€Š° "),nextWeek:a("[Š£] dddd ["),lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return a("[ŠœŠøŠ½ŃƒŠ»Š¾Ń—] dddd [").call(this);case 1:case 2:case 4:return a("[ŠœŠøŠ½ŃƒŠ»Š¾Š³Š¾] dddd [").call(this)}},sameElse:"L"},relativeTime:{future:"Š·Š° %s",past:"%s тŠ¾Š¼Ńƒ",s:"Š“ŠµŠŗіŠ»ŃŒŠŗŠ° сŠµŠŗуŠ½Š“",ss:t,m:t,mm:t,h:"Š³Š¾Š“ŠøŠ½Ńƒ",hh:t,d:"Š“ŠµŠ½ŃŒ",dd:t,M:"Š¼Ń–ŃŃŃ†ŃŒ",MM:t,y:"ріŠŗ",yy:t},meridiemParse:/Š½Š¾Ń‡Ń–|рŠ°Š½Šŗу|Š“Š½Ń|Š²ŠµŃ‡Š¾Ń€Š°/,isPM:function(e){return/^(Š“Š½Ń|Š²ŠµŃ‡Š¾Ń€Š°)$/.test(e)},meridiem:function(e,t,n){if(e<4)return"Š½Š¾Ń‡Ń–";else if(e<12)return"рŠ°Š½Šŗу";else if(e<17)return"Š“Š½Ń";else return"Š²ŠµŃ‡Š¾Ń€Š°"},dayOfMonthOrdinalParse:/\d{1,2}-(Š¹|Š³Š¾)/,ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":case"w":case"W":return e+"-Š¹";case"D":return e+"-Š³Š¾";default:return e}},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +function r(e,t){var n=e.split("_");return t%10===1&&t%100!==11?n[0]:t%10>=2&&t%10<=4&&(t%100<10||t%100>=20)?n[1]:n[2]}function t(e,t,n){var a={ss:t?"сŠµŠŗуŠ½Š“Š°_сŠµŠŗуŠ½Š“Šø_сŠµŠŗуŠ½Š“":"сŠµŠŗуŠ½Š“у_сŠµŠŗуŠ½Š“Šø_сŠµŠŗуŠ½Š“",mm:t?"хŠ²ŠøŠ»ŠøŠ½Š°_хŠ²ŠøŠ»ŠøŠ½Šø_хŠ²ŠøŠ»ŠøŠ½":"хŠ²ŠøŠ»ŠøŠ½Ńƒ_хŠ²ŠøŠ»ŠøŠ½Šø_хŠ²ŠøŠ»ŠøŠ½",hh:t?"Š³Š¾Š“ŠøŠ½Š°_Š³Š¾Š“ŠøŠ½Šø_Š³Š¾Š“ŠøŠ½":"Š³Š¾Š“ŠøŠ½Ńƒ_Š³Š¾Š“ŠøŠ½Šø_Š³Š¾Š“ŠøŠ½",dd:"Š“ŠµŠ½ŃŒ_Š“Š½Ń–_Š“Š½Ń–Š²",MM:"Š¼Ń–ŃŃŃ†ŃŒ_Š¼Ń–ŃŃŃ†Ń–_Š¼Ń–ŃŃŃ†Ń–Š²",yy:"ріŠŗ_рŠ¾ŠŗŠø_рŠ¾ŠŗіŠ²"};if(n==="m")return t?"хŠ²ŠøŠ»ŠøŠ½Š°":"хŠ²ŠøŠ»ŠøŠ½Ńƒ";else if(n==="h")return t?"Š³Š¾Š“ŠøŠ½Š°":"Š³Š¾Š“ŠøŠ½Ńƒ";else return e+" "+r(a[n],+e)}function n(e,t){var n={nominative:"Š½ŠµŠ“іŠ»Ń_ŠæŠ¾Š½ŠµŠ“іŠ»Š¾Šŗ_Š²Ń–Š²Ń‚Š¾Ń€Š¾Šŗ_сŠµŃ€ŠµŠ“Š°_чŠµŃ‚Š²ŠµŃ€_Šæā€™ŃŃ‚Š½Šøця_суŠ±Š¾Ń‚Š°".split("_"),accusative:"Š½ŠµŠ“іŠ»ŃŽ_ŠæŠ¾Š½ŠµŠ“іŠ»Š¾Šŗ_Š²Ń–Š²Ń‚Š¾Ń€Š¾Šŗ_сŠµŃ€ŠµŠ“у_чŠµŃ‚Š²ŠµŃ€_Šæā€™ŃŃ‚Š½Šøцю_суŠ±Š¾Ń‚Ńƒ".split("_"),genitive:"Š½ŠµŠ“іŠ»Ń–_ŠæŠ¾Š½ŠµŠ“іŠ»ŠŗŠ°_Š²Ń–Š²Ń‚Š¾Ń€ŠŗŠ°_сŠµŃ€ŠµŠ“Šø_чŠµŃ‚Š²ŠµŃ€Š³Š°_Šæā€™ŃŃ‚Š½Šøці_суŠ±Š¾Ń‚Šø".split("_")},a;if(e===true)return n["nominative"].slice(1,7).concat(n["nominative"].slice(0,1));if(!e)return n["nominative"];a=/(\[[Š’Š²Š£Ńƒ]\]) ?dddd/.test(t)?"accusative":/\[?(?:Š¼ŠøŠ½ŃƒŠ»Š¾Ń—|Š½Š°ŃŃ‚ŃƒŠæŠ½Š¾Ń—)? ?\] ?dddd/.test(t)?"genitive":"nominative";return n[a][e.day()]}function a(e){return function(){return e+"Š¾"+(this.hours()===11?"Š±":"")+"] LT"}}var o;e.defineLocale("uk",{months:{format:"січŠ½Ń_Š»ŃŽŃ‚Š¾Š³Š¾_Š±ŠµŃ€ŠµŠ·Š½Ń_ŠŗŠ²Ń–Ń‚Š½Ń_трŠ°Š²Š½Ń_чŠµŃ€Š²Š½Ń_Š»ŠøŠæŠ½Ń_сŠµŃ€ŠæŠ½Ń_Š²ŠµŃ€ŠµŃŠ½Ń_Š¶Š¾Š²Ń‚Š½Ń_Š»ŠøстŠ¾ŠæŠ°Š“Š°_Š³Ń€ŃƒŠ“Š½Ń".split("_"),standalone:"січŠµŠ½ŃŒ_Š»ŃŽŃ‚ŠøŠ¹_Š±ŠµŃ€ŠµŠ·ŠµŠ½ŃŒ_ŠŗŠ²Ń–Ń‚ŠµŠ½ŃŒ_трŠ°Š²ŠµŠ½ŃŒ_чŠµŃ€Š²ŠµŠ½ŃŒ_Š»ŠøŠæŠµŠ½ŃŒ_сŠµŃ€ŠæŠµŠ½ŃŒ_Š²ŠµŃ€ŠµŃŠµŠ½ŃŒ_Š¶Š¾Š²Ń‚ŠµŠ½ŃŒ_Š»ŠøстŠ¾ŠæŠ°Š“_Š³Ń€ŃƒŠ“ŠµŠ½ŃŒ".split("_")},monthsShort:"січ_Š»ŃŽŃ‚_Š±ŠµŃ€_ŠŗŠ²Ń–Ń‚_трŠ°Š²_чŠµŃ€Š²_Š»ŠøŠæ_сŠµŃ€Šæ_Š²ŠµŃ€_Š¶Š¾Š²Ń‚_Š»Šøст_Š³Ń€ŃƒŠ“".split("_"),weekdays:n,weekdaysShort:"Š½Š“_ŠæŠ½_Š²Ń‚_ср_чт_Šæт_сŠ±".split("_"),weekdaysMin:"Š½Š“_ŠæŠ½_Š²Ń‚_ср_чт_Šæт_сŠ±".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY р.",LLL:"D MMMM YYYY р., HH:mm",LLLL:"dddd, D MMMM YYYY р., HH:mm"},calendar:{sameDay:a("[Š”ьŠ¾Š³Š¾Š“Š½Ń– "),nextDay:a("[Š—Š°Š²Ń‚Ń€Š° "),lastDay:a("[Š’чŠ¾Ń€Š° "),nextWeek:a("[Š£] dddd ["),lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return a("[ŠœŠøŠ½ŃƒŠ»Š¾Ń—] dddd [").call(this);case 1:case 2:case 4:return a("[ŠœŠøŠ½ŃƒŠ»Š¾Š³Š¾] dddd [").call(this)}},sameElse:"L"},relativeTime:{future:"Š·Š° %s",past:"%s тŠ¾Š¼Ńƒ",s:"Š“ŠµŠŗіŠ»ŃŒŠŗŠ° сŠµŠŗуŠ½Š“",ss:t,m:t,mm:t,h:"Š³Š¾Š“ŠøŠ½Ńƒ",hh:t,d:"Š“ŠµŠ½ŃŒ",dd:t,M:"Š¼Ń–ŃŃŃ†ŃŒ",MM:t,y:"ріŠŗ",yy:t},meridiemParse:/Š½Š¾Ń‡Ń–|рŠ°Š½Šŗу|Š“Š½Ń|Š²ŠµŃ‡Š¾Ń€Š°/,isPM:function(e){return/^(Š“Š½Ń|Š²ŠµŃ‡Š¾Ń€Š°)$/.test(e)},meridiem:function(e,t,n){if(e<4)return"Š½Š¾Ń‡Ń–";else if(e<12)return"рŠ°Š½Šŗу";else if(e<17)return"Š“Š½Ń";else return"Š²ŠµŃ‡Š¾Ń€Š°"},dayOfMonthOrdinalParse:/\d{1,2}-(Š¹|Š³Š¾)/,ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":case"w":case"W":return e+"-Š¹";case"D":return e+"-Š³Š¾";default:return e}},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t=["Ų¬Ł†ŁˆŲ±ŪŒ","ŁŲ±ŁˆŲ±ŪŒ","Ł…Ų§Ų±Ś†","Ų§Ł¾Ų±ŪŒŁ„","Ł…Ų¦ŪŒ","Ų¬ŁˆŁ†","Ų¬ŁˆŁ„Ų§Ų¦ŪŒ","Ų§ŚÆŲ³ŲŖ","Ų³ŲŖŁ…ŲØŲ±","Ų§Ś©ŲŖŁˆŲØŲ±","Ł†ŁˆŁ…ŲØŲ±","ŲÆŲ³Ł…ŲØŲ±"],n=["Ų§ŲŖŁˆŲ§Ų±","Ł¾ŪŒŲ±","Ł…Ł†ŚÆŁ„","ŲØŲÆŚ¾","Ų¬Ł…Ų¹Ų±Ų§ŲŖ","Ų¬Ł…Ų¹Ū","ŪŁŲŖŪ"],a;e.defineLocale("ur",{months:t,monthsShort:t,weekdays:n,weekdaysShort:n,weekdaysMin:n,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"ddddŲŒ D MMMM YYYY HH:mm"},meridiemParse:/ŲµŲØŲ­|Ų“Ų§Ł…/,isPM:function(e){return"Ų“Ų§Ł…"===e},meridiem:function(e,t,n){if(e<12)return"ŲµŲØŲ­";return"Ų“Ų§Ł…"},calendar:{sameDay:"[Ų¢Ų¬ ŲØŁˆŁ‚ŲŖ] LT",nextDay:"[Ś©Ł„ ŲØŁˆŁ‚ŲŖ] LT",nextWeek:"dddd [ŲØŁˆŁ‚ŲŖ] LT",lastDay:"[ŚÆŲ°Ų“ŲŖŪ Ų±ŁˆŲ² ŲØŁˆŁ‚ŲŖ] LT",lastWeek:"[ŚÆŲ°Ų“ŲŖŪ] dddd [ŲØŁˆŁ‚ŲŖ] LT",sameElse:"L"},relativeTime:{future:"%s ŲØŲ¹ŲÆ",past:"%s Ł‚ŲØŁ„",s:"Ś†Ł†ŲÆ Ų³ŪŒŚ©Ł†Śˆ",ss:"%d Ų³ŪŒŚ©Ł†Śˆ",m:"Ų§ŪŒŚ© Ł…Ł†Ł¹",mm:"%d Ł…Ł†Ł¹",h:"Ų§ŪŒŚ© ŚÆŚ¾Ł†Ł¹Ū",hh:"%d ŚÆŚ¾Ł†Ł¹Ū’",d:"Ų§ŪŒŚ© ŲÆŁ†",dd:"%d ŲÆŁ†",M:"Ų§ŪŒŚ© Ł…Ų§Ū",MM:"%d Ł…Ų§Ū",y:"Ų§ŪŒŚ© Ų³Ų§Ł„",yy:"%d Ų³Ų§Ł„"},preparse:function(e){return e.replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/,/g,"ŲŒ")},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t=["Ų¬Ł†ŁˆŲ±ŪŒ","ŁŲ±ŁˆŲ±ŪŒ","Ł…Ų§Ų±Ś†","Ų§Ł¾Ų±ŪŒŁ„","Ł…Ų¦ŪŒ","Ų¬ŁˆŁ†","Ų¬ŁˆŁ„Ų§Ų¦ŪŒ","Ų§ŚÆŲ³ŲŖ","Ų³ŲŖŁ…ŲØŲ±","Ų§Ś©ŲŖŁˆŲØŲ±","Ł†ŁˆŁ…ŲØŲ±","ŲÆŲ³Ł…ŲØŲ±"],n=["Ų§ŲŖŁˆŲ§Ų±","Ł¾ŪŒŲ±","Ł…Ł†ŚÆŁ„","ŲØŲÆŚ¾","Ų¬Ł…Ų¹Ų±Ų§ŲŖ","Ų¬Ł…Ų¹Ū","ŪŁŲŖŪ"],a;e.defineLocale("ur",{months:t,monthsShort:t,weekdays:n,weekdaysShort:n,weekdaysMin:n,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"ddddŲŒ D MMMM YYYY HH:mm"},meridiemParse:/ŲµŲØŲ­|Ų“Ų§Ł…/,isPM:function(e){return"Ų“Ų§Ł…"===e},meridiem:function(e,t,n){if(e<12)return"ŲµŲØŲ­";return"Ų“Ų§Ł…"},calendar:{sameDay:"[Ų¢Ų¬ ŲØŁˆŁ‚ŲŖ] LT",nextDay:"[Ś©Ł„ ŲØŁˆŁ‚ŲŖ] LT",nextWeek:"dddd [ŲØŁˆŁ‚ŲŖ] LT",lastDay:"[ŚÆŲ°Ų“ŲŖŪ Ų±ŁˆŲ² ŲØŁˆŁ‚ŲŖ] LT",lastWeek:"[ŚÆŲ°Ų“ŲŖŪ] dddd [ŲØŁˆŁ‚ŲŖ] LT",sameElse:"L"},relativeTime:{future:"%s ŲØŲ¹ŲÆ",past:"%s Ł‚ŲØŁ„",s:"Ś†Ł†ŲÆ Ų³ŪŒŚ©Ł†Śˆ",ss:"%d Ų³ŪŒŚ©Ł†Śˆ",m:"Ų§ŪŒŚ© Ł…Ł†Ł¹",mm:"%d Ł…Ł†Ł¹",h:"Ų§ŪŒŚ© ŚÆŚ¾Ł†Ł¹Ū",hh:"%d ŚÆŚ¾Ł†Ł¹Ū’",d:"Ų§ŪŒŚ© ŲÆŁ†",dd:"%d ŲÆŁ†",M:"Ų§ŪŒŚ© Ł…Ų§Ū",MM:"%d Ł…Ų§Ū",y:"Ų§ŪŒŚ© Ų³Ų§Ł„",yy:"%d Ų³Ų§Ł„"},preparse:function(e){return e.replace(/ŲŒ/g,",")},postformat:function(e){return e.replace(/,/g,"ŲŒ")},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("uz",{months:"яŠ½Š²Š°Ń€_фŠµŠ²Ń€Š°Š»_Š¼Š°Ń€Ń‚_Š°ŠæрŠµŠ»_Š¼Š°Š¹_ŠøюŠ½_ŠøюŠ»_Š°Š²Š³ŃƒŃŃ‚_сŠµŠ½Ń‚яŠ±Ń€_Š¾ŠŗтяŠ±Ń€_Š½Š¾ŃŠ±Ń€_Š“ŠµŠŗŠ°Š±Ń€".split("_"),monthsShort:"яŠ½Š²_фŠµŠ²_Š¼Š°Ń€_Š°Šæр_Š¼Š°Š¹_ŠøюŠ½_ŠøюŠ»_Š°Š²Š³_сŠµŠ½_Š¾Šŗт_Š½Š¾Ń_Š“ŠµŠŗ".split("_"),weekdays:"ŠÆŠŗшŠ°Š½Š±Š°_Š”ŃƒŃˆŠ°Š½Š±Š°_Š”ŠµŃˆŠ°Š½Š±Š°_Š§Š¾Ń€ŃˆŠ°Š½Š±Š°_ŠŸŠ°Š¹ŃˆŠ°Š½Š±Š°_Š–ŃƒŠ¼Š°_ŠØŠ°Š½Š±Š°".split("_"),weekdaysShort:"ŠÆŠŗш_Š”ŃƒŃˆ_Š”ŠµŃˆ_Š§Š¾Ń€_ŠŸŠ°Š¹_Š–ŃƒŠ¼_ŠØŠ°Š½".split("_"),weekdaysMin:"ŠÆŠŗ_Š”Ńƒ_Š”Šµ_Š§Š¾_ŠŸŠ°_Š–Ńƒ_ŠØŠ°".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"D MMMM YYYY, dddd HH:mm"},calendar:{sameDay:"[Š‘ŃƒŠ³ŃƒŠ½ сŠ¾Š°Ń‚] LT [Š“Š°]",nextDay:"[Š­Ń€Ń‚Š°Š³Š°] LT [Š“Š°]",nextWeek:"dddd [ŠŗуŠ½Šø сŠ¾Š°Ń‚] LT [Š“Š°]",lastDay:"[ŠšŠµŃ‡Š° сŠ¾Š°Ń‚] LT [Š“Š°]",lastWeek:"[Š£Ń‚Š³Š°Š½] dddd [ŠŗуŠ½Šø сŠ¾Š°Ń‚] LT [Š“Š°]",sameElse:"L"},relativeTime:{future:"ŠÆŠŗŠøŠ½ %s ŠøчŠøŠ“Š°",past:"Š‘Šøр Š½ŠµŃ‡Š° %s Š¾Š»Š“ŠøŠ½",s:"фурсŠ°Ń‚",ss:"%d фурсŠ°Ń‚",m:"Š±Šøр Š“Š°ŠŗŠøŠŗŠ°",mm:"%d Š“Š°ŠŗŠøŠŗŠ°",h:"Š±Šøр сŠ¾Š°Ń‚",hh:"%d сŠ¾Š°Ń‚",d:"Š±Šøр ŠŗуŠ½",dd:"%d ŠŗуŠ½",M:"Š±Šøр Š¾Š¹",MM:"%d Š¾Š¹",y:"Š±Šøр Š¹ŠøŠ»",yy:"%d Š¹ŠøŠ»"},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("uz",{months:"яŠ½Š²Š°Ń€_фŠµŠ²Ń€Š°Š»_Š¼Š°Ń€Ń‚_Š°ŠæрŠµŠ»_Š¼Š°Š¹_ŠøюŠ½_ŠøюŠ»_Š°Š²Š³ŃƒŃŃ‚_сŠµŠ½Ń‚яŠ±Ń€_Š¾ŠŗтяŠ±Ń€_Š½Š¾ŃŠ±Ń€_Š“ŠµŠŗŠ°Š±Ń€".split("_"),monthsShort:"яŠ½Š²_фŠµŠ²_Š¼Š°Ń€_Š°Šæр_Š¼Š°Š¹_ŠøюŠ½_ŠøюŠ»_Š°Š²Š³_сŠµŠ½_Š¾Šŗт_Š½Š¾Ń_Š“ŠµŠŗ".split("_"),weekdays:"ŠÆŠŗшŠ°Š½Š±Š°_Š”ŃƒŃˆŠ°Š½Š±Š°_Š”ŠµŃˆŠ°Š½Š±Š°_Š§Š¾Ń€ŃˆŠ°Š½Š±Š°_ŠŸŠ°Š¹ŃˆŠ°Š½Š±Š°_Š–ŃƒŠ¼Š°_ŠØŠ°Š½Š±Š°".split("_"),weekdaysShort:"ŠÆŠŗш_Š”ŃƒŃˆ_Š”ŠµŃˆ_Š§Š¾Ń€_ŠŸŠ°Š¹_Š–ŃƒŠ¼_ŠØŠ°Š½".split("_"),weekdaysMin:"ŠÆŠŗ_Š”Ńƒ_Š”Šµ_Š§Š¾_ŠŸŠ°_Š–Ńƒ_ŠØŠ°".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"D MMMM YYYY, dddd HH:mm"},calendar:{sameDay:"[Š‘ŃƒŠ³ŃƒŠ½ сŠ¾Š°Ń‚] LT [Š“Š°]",nextDay:"[Š­Ń€Ń‚Š°Š³Š°] LT [Š“Š°]",nextWeek:"dddd [ŠŗуŠ½Šø сŠ¾Š°Ń‚] LT [Š“Š°]",lastDay:"[ŠšŠµŃ‡Š° сŠ¾Š°Ń‚] LT [Š“Š°]",lastWeek:"[Š£Ń‚Š³Š°Š½] dddd [ŠŗуŠ½Šø сŠ¾Š°Ń‚] LT [Š“Š°]",sameElse:"L"},relativeTime:{future:"ŠÆŠŗŠøŠ½ %s ŠøчŠøŠ“Š°",past:"Š‘Šøр Š½ŠµŃ‡Š° %s Š¾Š»Š“ŠøŠ½",s:"фурсŠ°Ń‚",ss:"%d фурсŠ°Ń‚",m:"Š±Šøр Š“Š°ŠŗŠøŠŗŠ°",mm:"%d Š“Š°ŠŗŠøŠŗŠ°",h:"Š±Šøр сŠ¾Š°Ń‚",hh:"%d сŠ¾Š°Ń‚",d:"Š±Šøр ŠŗуŠ½",dd:"%d ŠŗуŠ½",M:"Š±Šøр Š¾Š¹",MM:"%d Š¾Š¹",y:"Š±Šøр Š¹ŠøŠ»",yy:"%d Š¹ŠøŠ»"},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("uz-latn",{months:"Yanvar_Fevral_Mart_Aprel_May_Iyun_Iyul_Avgust_Sentabr_Oktabr_Noyabr_Dekabr".split("_"),monthsShort:"Yan_Fev_Mar_Apr_May_Iyun_Iyul_Avg_Sen_Okt_Noy_Dek".split("_"),weekdays:"Yakshanba_Dushanba_Seshanba_Chorshanba_Payshanba_Juma_Shanba".split("_"),weekdaysShort:"Yak_Dush_Sesh_Chor_Pay_Jum_Shan".split("_"),weekdaysMin:"Ya_Du_Se_Cho_Pa_Ju_Sha".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"D MMMM YYYY, dddd HH:mm"},calendar:{sameDay:"[Bugun soat] LT [da]",nextDay:"[Ertaga] LT [da]",nextWeek:"dddd [kuni soat] LT [da]",lastDay:"[Kecha soat] LT [da]",lastWeek:"[O'tgan] dddd [kuni soat] LT [da]",sameElse:"L"},relativeTime:{future:"Yaqin %s ichida",past:"Bir necha %s oldin",s:"soniya",ss:"%d soniya",m:"bir daqiqa",mm:"%d daqiqa",h:"bir soat",hh:"%d soat",d:"bir kun",dd:"%d kun",M:"bir oy",MM:"%d oy",y:"bir yil",yy:"%d yil"},week:{dow:1,doy:7}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("uz-latn",{months:"Yanvar_Fevral_Mart_Aprel_May_Iyun_Iyul_Avgust_Sentabr_Oktabr_Noyabr_Dekabr".split("_"),monthsShort:"Yan_Fev_Mar_Apr_May_Iyun_Iyul_Avg_Sen_Okt_Noy_Dek".split("_"),weekdays:"Yakshanba_Dushanba_Seshanba_Chorshanba_Payshanba_Juma_Shanba".split("_"),weekdaysShort:"Yak_Dush_Sesh_Chor_Pay_Jum_Shan".split("_"),weekdaysMin:"Ya_Du_Se_Cho_Pa_Ju_Sha".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"D MMMM YYYY, dddd HH:mm"},calendar:{sameDay:"[Bugun soat] LT [da]",nextDay:"[Ertaga] LT [da]",nextWeek:"dddd [kuni soat] LT [da]",lastDay:"[Kecha soat] LT [da]",lastWeek:"[O'tgan] dddd [kuni soat] LT [da]",sameElse:"L"},relativeTime:{future:"Yaqin %s ichida",past:"Bir necha %s oldin",s:"soniya",ss:"%d soniya",m:"bir daqiqa",mm:"%d daqiqa",h:"bir soat",hh:"%d soat",d:"bir kun",dd:"%d kun",M:"bir oy",MM:"%d oy",y:"bir yil",yy:"%d yil"},week:{dow:1,doy:7}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("vi",{months:"thĆ”ng 1_thĆ”ng 2_thĆ”ng 3_thĆ”ng 4_thĆ”ng 5_thĆ”ng 6_thĆ”ng 7_thĆ”ng 8_thĆ”ng 9_thĆ”ng 10_thĆ”ng 11_thĆ”ng 12".split("_"),monthsShort:"Thg 01_Thg 02_Thg 03_Thg 04_Thg 05_Thg 06_Thg 07_Thg 08_Thg 09_Thg 10_Thg 11_Thg 12".split("_"),monthsParseExact:true,weekdays:"chį»§ nhįŗ­t_thį»© hai_thį»© ba_thį»© tĘ°_thį»© năm_thį»© sĆ”u_thį»© bįŗ£y".split("_"),weekdaysShort:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysMin:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysParseExact:true,meridiemParse:/sa|ch/i,isPM:function(e){return/^ch$/i.test(e)},meridiem:function(e,t,n){if(e<12)return n?"sa":"SA";else return n?"ch":"CH"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [năm] YYYY",LLL:"D MMMM [năm] YYYY HH:mm",LLLL:"dddd, D MMMM [năm] YYYY HH:mm",l:"DD/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY HH:mm",llll:"ddd, D MMM YYYY HH:mm"},calendar:{sameDay:"[HĆ“m nay lĆŗc] LT",nextDay:"[NgĆ y mai lĆŗc] LT",nextWeek:"dddd [tuįŗ§n tį»›i lĆŗc] LT",lastDay:"[HĆ“m qua lĆŗc] LT",lastWeek:"dddd [tuįŗ§n trĘ°į»›c lĆŗc] LT",sameElse:"L"},relativeTime:{future:"%s tį»›i",past:"%s trĘ°į»›c",s:"vĆ i giĆ¢y",ss:"%d giĆ¢y",m:"mį»™t phĆŗt",mm:"%d phĆŗt",h:"mį»™t giį»",hh:"%d giį»",d:"mį»™t ngĆ y",dd:"%d ngĆ y",w:"mį»™t tuįŗ§n",ww:"%d tuįŗ§n",M:"mį»™t thĆ”ng",MM:"%d thĆ”ng",y:"mį»™t năm",yy:"%d năm"},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:function(e){return e},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("vi",{months:"thĆ”ng 1_thĆ”ng 2_thĆ”ng 3_thĆ”ng 4_thĆ”ng 5_thĆ”ng 6_thĆ”ng 7_thĆ”ng 8_thĆ”ng 9_thĆ”ng 10_thĆ”ng 11_thĆ”ng 12".split("_"),monthsShort:"Thg 01_Thg 02_Thg 03_Thg 04_Thg 05_Thg 06_Thg 07_Thg 08_Thg 09_Thg 10_Thg 11_Thg 12".split("_"),monthsParseExact:true,weekdays:"chį»§ nhįŗ­t_thį»© hai_thį»© ba_thį»© tĘ°_thį»© năm_thį»© sĆ”u_thį»© bįŗ£y".split("_"),weekdaysShort:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysMin:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysParseExact:true,meridiemParse:/sa|ch/i,isPM:function(e){return/^ch$/i.test(e)},meridiem:function(e,t,n){if(e<12)return n?"sa":"SA";else return n?"ch":"CH"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [năm] YYYY",LLL:"D MMMM [năm] YYYY HH:mm",LLLL:"dddd, D MMMM [năm] YYYY HH:mm",l:"DD/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY HH:mm",llll:"ddd, D MMM YYYY HH:mm"},calendar:{sameDay:"[HĆ“m nay lĆŗc] LT",nextDay:"[NgĆ y mai lĆŗc] LT",nextWeek:"dddd [tuįŗ§n tį»›i lĆŗc] LT",lastDay:"[HĆ“m qua lĆŗc] LT",lastWeek:"dddd [tuįŗ§n trĘ°į»›c lĆŗc] LT",sameElse:"L"},relativeTime:{future:"%s tį»›i",past:"%s trĘ°į»›c",s:"vĆ i giĆ¢y",ss:"%d giĆ¢y",m:"mį»™t phĆŗt",mm:"%d phĆŗt",h:"mį»™t giį»",hh:"%d giį»",d:"mį»™t ngĆ y",dd:"%d ngĆ y",w:"mį»™t tuįŗ§n",ww:"%d tuįŗ§n",M:"mį»™t thĆ”ng",MM:"%d thĆ”ng",y:"mį»™t năm",yy:"%d năm"},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:function(e){return e},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("x-pseudo",{months:"J~ƔƱĆŗĆ”~rĆ½_F~Ć©brĆŗ~Ć”rĆ½_~MĆ”rc~h_Ɓp~rĆ­l_~MĆ”Ć½_~JĆŗƱƩ~_JĆŗl~Ć½_ƁĆŗ~gĆŗst~_SĆ©p~tĆ©mb~Ć©r_Ɠ~ctĆ³b~Ć©r_Ƒ~Ć³vĆ©m~bĆ©r_~DĆ©cĆ©~mbĆ©r".split("_"),monthsShort:"J~ƔƱ_~FĆ©b_~MĆ”r_~Ɓpr_~MĆ”Ć½_~JĆŗƱ_~JĆŗl_~ƁĆŗg_~SĆ©p_~Ɠct_~Ć‘Ć³v_~DĆ©c".split("_"),monthsParseExact:true,weekdays:"S~ĆŗƱdĆ”~Ć½_MĆ³~ƱdĆ”Ć½~_TĆŗĆ©~sdĆ”Ć½~_WĆ©d~ƱƩsd~Ć”Ć½_T~hĆŗrs~dĆ”Ć½_~FrĆ­d~Ć”Ć½_S~Ć”tĆŗr~dĆ”Ć½".split("_"),weekdaysShort:"S~ĆŗƱ_~MĆ³Ć±_~TĆŗĆ©_~WĆ©d_~ThĆŗ_~FrĆ­_~SĆ”t".split("_"),weekdaysMin:"S~Ćŗ_MĆ³~_TĆŗ_~WĆ©_T~h_Fr~_SĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[T~Ć³dĆ”~Ć½ Ć”t] LT",nextDay:"[T~Ć³mĆ³~rrĆ³~w Ć”t] LT",nextWeek:"dddd [Ć”t] LT",lastDay:"[Ɲ~Ć©st~Ć©rdĆ”~Ć½ Ć”t] LT",lastWeek:"[L~Ć”st] dddd [Ć”t] LT",sameElse:"L"},relativeTime:{future:"Ć­~Ʊ %s",past:"%s Ć”~gĆ³",s:"Ć” ~fĆ©w ~sĆ©cĆ³~Ʊds",ss:"%d s~Ć©cĆ³Ć±~ds",m:"Ć” ~mƭƱ~ĆŗtĆ©",mm:"%d m~ƭƱĆŗ~tĆ©s",h:"Ć”~Ʊ hĆ³~Ćŗr",hh:"%d h~Ć³Ćŗrs",d:"Ć” ~dĆ”Ć½",dd:"%d d~Ć”Ć½s",M:"Ć” ~mĆ³Ć±~th",MM:"%d m~Ć³Ć±t~hs",y:"Ć” ~Ć½Ć©Ć”r",yy:"%d Ć½~Ć©Ć”rs"},dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("x-pseudo",{months:"J~ƔƱĆŗĆ”~rĆ½_F~Ć©brĆŗ~Ć”rĆ½_~MĆ”rc~h_Ɓp~rĆ­l_~MĆ”Ć½_~JĆŗƱƩ~_JĆŗl~Ć½_ƁĆŗ~gĆŗst~_SĆ©p~tĆ©mb~Ć©r_Ɠ~ctĆ³b~Ć©r_Ƒ~Ć³vĆ©m~bĆ©r_~DĆ©cĆ©~mbĆ©r".split("_"),monthsShort:"J~ƔƱ_~FĆ©b_~MĆ”r_~Ɓpr_~MĆ”Ć½_~JĆŗƱ_~JĆŗl_~ƁĆŗg_~SĆ©p_~Ɠct_~Ć‘Ć³v_~DĆ©c".split("_"),monthsParseExact:true,weekdays:"S~ĆŗƱdĆ”~Ć½_MĆ³~ƱdĆ”Ć½~_TĆŗĆ©~sdĆ”Ć½~_WĆ©d~ƱƩsd~Ć”Ć½_T~hĆŗrs~dĆ”Ć½_~FrĆ­d~Ć”Ć½_S~Ć”tĆŗr~dĆ”Ć½".split("_"),weekdaysShort:"S~ĆŗƱ_~MĆ³Ć±_~TĆŗĆ©_~WĆ©d_~ThĆŗ_~FrĆ­_~SĆ”t".split("_"),weekdaysMin:"S~Ćŗ_MĆ³~_TĆŗ_~WĆ©_T~h_Fr~_SĆ”".split("_"),weekdaysParseExact:true,longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[T~Ć³dĆ”~Ć½ Ć”t] LT",nextDay:"[T~Ć³mĆ³~rrĆ³~w Ć”t] LT",nextWeek:"dddd [Ć”t] LT",lastDay:"[Ɲ~Ć©st~Ć©rdĆ”~Ć½ Ć”t] LT",lastWeek:"[L~Ć”st] dddd [Ć”t] LT",sameElse:"L"},relativeTime:{future:"Ć­~Ʊ %s",past:"%s Ć”~gĆ³",s:"Ć” ~fĆ©w ~sĆ©cĆ³~Ʊds",ss:"%d s~Ć©cĆ³Ć±~ds",m:"Ć” ~mƭƱ~ĆŗtĆ©",mm:"%d m~ƭƱĆŗ~tĆ©s",h:"Ć”~Ʊ hĆ³~Ćŗr",hh:"%d h~Ć³Ćŗrs",d:"Ć” ~dĆ”Ć½",dd:"%d d~Ć”Ć½s",M:"Ć” ~mĆ³Ć±~th",MM:"%d m~Ć³Ć±t~hs",y:"Ć” ~Ć½Ć©Ć”r",yy:"%d Ć½~Ć©Ć”rs"},dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10,n=~~(e%100/10)===1?"th":t===1?"st":t===2?"nd":t===3?"rd":"th";return e+n},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("yo",{months:"Sįŗ¹Ģrįŗ¹Ģ_EĢ€reĢ€leĢ€_įŗørįŗ¹Ģ€naĢ€_IĢ€gbeĢ_EĢ€bibi_OĢ€kuĢ€du_Agįŗ¹mo_OĢ€guĢn_Owewe_į»ŒĢ€waĢ€raĢ€_BeĢluĢ_į»ŒĢ€pįŗ¹Ģ€Ģ€".split("_"),monthsShort:"Sįŗ¹Ģr_EĢ€rl_įŗørn_IĢ€gb_EĢ€bi_OĢ€kuĢ€_Agįŗ¹_OĢ€guĢ_Owe_į»ŒĢ€waĢ€_BeĢl_į»ŒĢ€pįŗ¹Ģ€Ģ€".split("_"),weekdays:"AĢ€iĢ€kuĢ_AjeĢ_IĢ€sįŗ¹Ģgun_į»Œjį»ĢruĢ_į»Œjį»Ģbį»_įŗøtiĢ€_AĢ€baĢmįŗ¹Ģta".split("_"),weekdaysShort:"AĢ€iĢ€k_AjeĢ_IĢ€sįŗ¹Ģ_į»Œjr_į»Œjb_įŗøtiĢ€_AĢ€baĢ".split("_"),weekdaysMin:"AĢ€iĢ€_Aj_IĢ€s_į»Œr_į»Œb_įŗøt_AĢ€b".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[OĢ€niĢ€ ni] LT",nextDay:"[į»ŒĢ€la ni] LT",nextWeek:"dddd [į»Œsįŗ¹Ģ€ toĢn'bį»] [ni] LT",lastDay:"[AĢ€na ni] LT",lastWeek:"dddd [į»Œsįŗ¹Ģ€ toĢlį»Ģ] [ni] LT",sameElse:"L"},relativeTime:{future:"niĢ %s",past:"%s kį»jaĢ",s:"iĢ€sįŗ¹juĢ aayaĢ die",ss:"aayaĢ %d",m:"iĢ€sįŗ¹juĢ kan",mm:"iĢ€sįŗ¹juĢ %d",h:"waĢkati kan",hh:"waĢkati %d",d:"į»jį»Ģ kan",dd:"į»jį»Ģ %d",M:"osuĢ€ kan",MM:"osuĢ€ %d",y:"į»duĢn kan",yy:"į»duĢn %d"},dayOfMonthOrdinalParse:/į»jį»Ģ\s\d{1,2}/,ordinal:"į»jį»Ģ %d",week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("yo",{months:"Sįŗ¹Ģrįŗ¹Ģ_EĢ€reĢ€leĢ€_įŗørįŗ¹Ģ€naĢ€_IĢ€gbeĢ_EĢ€bibi_OĢ€kuĢ€du_Agįŗ¹mo_OĢ€guĢn_Owewe_į»ŒĢ€waĢ€raĢ€_BeĢluĢ_į»ŒĢ€pįŗ¹Ģ€Ģ€".split("_"),monthsShort:"Sįŗ¹Ģr_EĢ€rl_įŗørn_IĢ€gb_EĢ€bi_OĢ€kuĢ€_Agįŗ¹_OĢ€guĢ_Owe_į»ŒĢ€waĢ€_BeĢl_į»ŒĢ€pįŗ¹Ģ€Ģ€".split("_"),weekdays:"AĢ€iĢ€kuĢ_AjeĢ_IĢ€sįŗ¹Ģgun_į»Œjį»ĢruĢ_į»Œjį»Ģbį»_įŗøtiĢ€_AĢ€baĢmįŗ¹Ģta".split("_"),weekdaysShort:"AĢ€iĢ€k_AjeĢ_IĢ€sįŗ¹Ģ_į»Œjr_į»Œjb_įŗøtiĢ€_AĢ€baĢ".split("_"),weekdaysMin:"AĢ€iĢ€_Aj_IĢ€s_į»Œr_į»Œb_įŗøt_AĢ€b".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[OĢ€niĢ€ ni] LT",nextDay:"[į»ŒĢ€la ni] LT",nextWeek:"dddd [į»Œsįŗ¹Ģ€ toĢn'bį»] [ni] LT",lastDay:"[AĢ€na ni] LT",lastWeek:"dddd [į»Œsįŗ¹Ģ€ toĢlį»Ģ] [ni] LT",sameElse:"L"},relativeTime:{future:"niĢ %s",past:"%s kį»jaĢ",s:"iĢ€sįŗ¹juĢ aayaĢ die",ss:"aayaĢ %d",m:"iĢ€sįŗ¹juĢ kan",mm:"iĢ€sįŗ¹juĢ %d",h:"waĢkati kan",hh:"waĢkati %d",d:"į»jį»Ģ kan",dd:"į»jį»Ģ %d",M:"osuĢ€ kan",MM:"osuĢ€ %d",y:"į»duĢn kan",yy:"į»duĢn %d"},dayOfMonthOrdinalParse:/į»jį»Ģ\s\d{1,2}/,ordinal:"į»jį»Ģ %d",week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("zh-cn",{months:"äø€ęœˆ_äŗŒęœˆ_äø‰ęœˆ_å››ęœˆ_äŗ”ęœˆ_å…­ęœˆ_äøƒęœˆ_å…«ęœˆ_ä¹ęœˆ_åęœˆ_十äø€ęœˆ_十äŗŒęœˆ".split("_"),monthsShort:"1꜈_2꜈_3꜈_4꜈_5꜈_6꜈_7꜈_8꜈_9꜈_10꜈_11꜈_12꜈".split("_"),weekdays:"ę˜ŸęœŸę—„_ꘟꜟäø€_ꘟꜟäŗŒ_ꘟꜟäø‰_ę˜ŸęœŸå››_ꘟꜟäŗ”_ę˜ŸęœŸå…­".split("_"),weekdaysShort:"å‘Øę—„_å‘Øäø€_å‘ØäŗŒ_å‘Øäø‰_å‘Ø四_å‘Øäŗ”_å‘Ø六".split("_"),weekdaysMin:"ę—„_äø€_äŗŒ_äø‰_四_äŗ”_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY幓M꜈Dę—„",LLL:"YYYY幓M꜈Dę—„Ahē‚¹mm分",LLLL:"YYYY幓M꜈Dę—„ddddAhē‚¹mm分",l:"YYYY/M/D",ll:"YYYY幓M꜈Dę—„",lll:"YYYY幓M꜈Dę—„ HH:mm",llll:"YYYY幓M꜈Dę—„dddd HH:mm"},meridiemParse:/凌ę™Ø|ę—©äøŠ|äøŠåˆ|äø­åˆ|äø‹åˆ|ꙚäøŠ/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="凌ę™Ø"||t==="ę—©äøŠ"||t==="äøŠåˆ")return e;else if(t==="äø‹åˆ"||t==="ꙚäøŠ")return e+12;else return e>=11?e:e+12},meridiem:function(e,t,n){var a=e*100+t;if(a<600)return"凌ę™Ø";else if(a<900)return"ę—©äøŠ";else if(a<1130)return"äøŠåˆ";else if(a<1230)return"äø­åˆ";else if(a<1800)return"äø‹åˆ";else return"ꙚäøŠ"},calendar:{sameDay:"[今天]LT",nextDay:"[ę˜Žå¤©]LT",nextWeek:function(e){if(e.week()!==this.week())return"[äø‹]dddLT";else return"[ęœ¬]dddLT"},lastDay:"[ę˜Ø天]LT",lastWeek:function(e){if(this.week()!==e.week())return"[äøŠ]dddLT";else return"[ęœ¬]dddLT"},sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(ę—„|꜈|å‘Ø)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"ę—„";case"M":return e+"꜈";case"w":case"W":return e+"å‘Ø";default:return e}},relativeTime:{future:"%s后",past:"%s前",s:"几ē§’",ss:"%d ē§’",m:"1 分钟",mm:"%d 分钟",h:"1 å°ę—¶",hh:"%d å°ę—¶",d:"1 天",dd:"%d 天",w:"1 å‘Ø",ww:"%d å‘Ø",M:"1 äøŖ꜈",MM:"%d äøŖ꜈",y:"1 幓",yy:"%d 幓"},week:{dow:1,doy:4}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("zh-cn",{months:"äø€ęœˆ_äŗŒęœˆ_äø‰ęœˆ_å››ęœˆ_äŗ”ęœˆ_å…­ęœˆ_äøƒęœˆ_å…«ęœˆ_ä¹ęœˆ_åęœˆ_十äø€ęœˆ_十äŗŒęœˆ".split("_"),monthsShort:"1꜈_2꜈_3꜈_4꜈_5꜈_6꜈_7꜈_8꜈_9꜈_10꜈_11꜈_12꜈".split("_"),weekdays:"ę˜ŸęœŸę—„_ꘟꜟäø€_ꘟꜟäŗŒ_ꘟꜟäø‰_ę˜ŸęœŸå››_ꘟꜟäŗ”_ę˜ŸęœŸå…­".split("_"),weekdaysShort:"å‘Øę—„_å‘Øäø€_å‘ØäŗŒ_å‘Øäø‰_å‘Ø四_å‘Øäŗ”_å‘Ø六".split("_"),weekdaysMin:"ę—„_äø€_äŗŒ_äø‰_四_äŗ”_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY幓M꜈Dę—„",LLL:"YYYY幓M꜈Dę—„Ahē‚¹mm分",LLLL:"YYYY幓M꜈Dę—„ddddAhē‚¹mm分",l:"YYYY/M/D",ll:"YYYY幓M꜈Dę—„",lll:"YYYY幓M꜈Dę—„ HH:mm",llll:"YYYY幓M꜈Dę—„dddd HH:mm"},meridiemParse:/凌ę™Ø|ę—©äøŠ|äøŠåˆ|äø­åˆ|äø‹åˆ|ꙚäøŠ/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="凌ę™Ø"||t==="ę—©äøŠ"||t==="äøŠåˆ")return e;else if(t==="äø‹åˆ"||t==="ꙚäøŠ")return e+12;else return e>=11?e:e+12},meridiem:function(e,t,n){var a=e*100+t;if(a<600)return"凌ę™Ø";else if(a<900)return"ę—©äøŠ";else if(a<1130)return"äøŠåˆ";else if(a<1230)return"äø­åˆ";else if(a<1800)return"äø‹åˆ";else return"ꙚäøŠ"},calendar:{sameDay:"[今天]LT",nextDay:"[ę˜Žå¤©]LT",nextWeek:function(e){if(e.week()!==this.week())return"[äø‹]dddLT";else return"[ęœ¬]dddLT"},lastDay:"[ę˜Ø天]LT",lastWeek:function(e){if(this.week()!==e.week())return"[äøŠ]dddLT";else return"[ęœ¬]dddLT"},sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(ę—„|꜈|å‘Ø)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"ę—„";case"M":return e+"꜈";case"w":case"W":return e+"å‘Ø";default:return e}},relativeTime:{future:"%s后",past:"%s前",s:"几ē§’",ss:"%d ē§’",m:"1 分钟",mm:"%d 分钟",h:"1 å°ę—¶",hh:"%d å°ę—¶",d:"1 天",dd:"%d 天",w:"1 å‘Ø",ww:"%d å‘Ø",M:"1 äøŖ꜈",MM:"%d äøŖ꜈",y:"1 幓",yy:"%d 幓"},week:{dow:1,doy:4}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("zh-hk",{months:"äø€ęœˆ_äŗŒęœˆ_äø‰ęœˆ_å››ęœˆ_äŗ”ęœˆ_å…­ęœˆ_äøƒęœˆ_å…«ęœˆ_ä¹ęœˆ_åęœˆ_十äø€ęœˆ_十äŗŒęœˆ".split("_"),monthsShort:"1꜈_2꜈_3꜈_4꜈_5꜈_6꜈_7꜈_8꜈_9꜈_10꜈_11꜈_12꜈".split("_"),weekdays:"ę˜ŸęœŸę—„_ꘟꜟäø€_ꘟꜟäŗŒ_ꘟꜟäø‰_ę˜ŸęœŸå››_ꘟꜟäŗ”_ę˜ŸęœŸå…­".split("_"),weekdaysShort:"週ꗄ_週äø€_週äŗŒ_週äø‰_週四_週äŗ”_週六".split("_"),weekdaysMin:"ę—„_äø€_äŗŒ_äø‰_四_äŗ”_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY幓M꜈Dę—„",LLL:"YYYY幓M꜈Dę—„ HH:mm",LLLL:"YYYY幓M꜈Dę—„dddd HH:mm",l:"YYYY/M/D",ll:"YYYY幓M꜈Dę—„",lll:"YYYY幓M꜈Dę—„ HH:mm",llll:"YYYY幓M꜈Dę—„dddd HH:mm"},meridiemParse:/凌ę™Ø|ę—©äøŠ|äøŠåˆ|äø­åˆ|äø‹åˆ|ꙚäøŠ/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="凌ę™Ø"||t==="ę—©äøŠ"||t==="äøŠåˆ")return e;else if(t==="äø­åˆ")return e>=11?e:e+12;else if(t==="äø‹åˆ"||t==="ꙚäøŠ")return e+12},meridiem:function(e,t,n){var a=e*100+t;if(a<600)return"凌ę™Ø";else if(a<900)return"ę—©äøŠ";else if(a<1200)return"äøŠåˆ";else if(a===1200)return"äø­åˆ";else if(a<1800)return"äø‹åˆ";else return"ꙚäøŠ"},calendar:{sameDay:"[今天]LT",nextDay:"[ę˜Žå¤©]LT",nextWeek:"[äø‹]ddddLT",lastDay:"[ę˜Ø天]LT",lastWeek:"[äøŠ]ddddLT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(ę—„|꜈|週)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"ę—„";case"M":return e+"꜈";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"å¹¾ē§’",ss:"%d ē§’",m:"1 分鐘",mm:"%d 分鐘",h:"1 å°ę™‚",hh:"%d å°ę™‚",d:"1 天",dd:"%d 天",M:"1 å€‹ęœˆ",MM:"%d å€‹ęœˆ",y:"1 幓",yy:"%d 幓"}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("zh-hk",{months:"äø€ęœˆ_äŗŒęœˆ_äø‰ęœˆ_å››ęœˆ_äŗ”ęœˆ_å…­ęœˆ_äøƒęœˆ_å…«ęœˆ_ä¹ęœˆ_åęœˆ_十äø€ęœˆ_十äŗŒęœˆ".split("_"),monthsShort:"1꜈_2꜈_3꜈_4꜈_5꜈_6꜈_7꜈_8꜈_9꜈_10꜈_11꜈_12꜈".split("_"),weekdays:"ę˜ŸęœŸę—„_ꘟꜟäø€_ꘟꜟäŗŒ_ꘟꜟäø‰_ę˜ŸęœŸå››_ꘟꜟäŗ”_ę˜ŸęœŸå…­".split("_"),weekdaysShort:"週ꗄ_週äø€_週äŗŒ_週äø‰_週四_週äŗ”_週六".split("_"),weekdaysMin:"ę—„_äø€_äŗŒ_äø‰_四_äŗ”_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY幓M꜈Dę—„",LLL:"YYYY幓M꜈Dę—„ HH:mm",LLLL:"YYYY幓M꜈Dę—„dddd HH:mm",l:"YYYY/M/D",ll:"YYYY幓M꜈Dę—„",lll:"YYYY幓M꜈Dę—„ HH:mm",llll:"YYYY幓M꜈Dę—„dddd HH:mm"},meridiemParse:/凌ę™Ø|ę—©äøŠ|äøŠåˆ|äø­åˆ|äø‹åˆ|ꙚäøŠ/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="凌ę™Ø"||t==="ę—©äøŠ"||t==="äøŠåˆ")return e;else if(t==="äø­åˆ")return e>=11?e:e+12;else if(t==="äø‹åˆ"||t==="ꙚäøŠ")return e+12},meridiem:function(e,t,n){var a=e*100+t;if(a<600)return"凌ę™Ø";else if(a<900)return"ę—©äøŠ";else if(a<1200)return"äøŠåˆ";else if(a===1200)return"äø­åˆ";else if(a<1800)return"äø‹åˆ";else return"ꙚäøŠ"},calendar:{sameDay:"[今天]LT",nextDay:"[ę˜Žå¤©]LT",nextWeek:"[äø‹]ddddLT",lastDay:"[ę˜Ø天]LT",lastWeek:"[äøŠ]ddddLT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(ę—„|꜈|週)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"ę—„";case"M":return e+"꜈";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"å¹¾ē§’",ss:"%d ē§’",m:"1 分鐘",mm:"%d 分鐘",h:"1 å°ę™‚",hh:"%d å°ę™‚",d:"1 天",dd:"%d 天",M:"1 å€‹ęœˆ",MM:"%d å€‹ęœˆ",y:"1 幓",yy:"%d 幓"}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("zh-mo",{months:"äø€ęœˆ_äŗŒęœˆ_äø‰ęœˆ_å››ęœˆ_äŗ”ęœˆ_å…­ęœˆ_äøƒęœˆ_å…«ęœˆ_ä¹ęœˆ_åęœˆ_十äø€ęœˆ_十äŗŒęœˆ".split("_"),monthsShort:"1꜈_2꜈_3꜈_4꜈_5꜈_6꜈_7꜈_8꜈_9꜈_10꜈_11꜈_12꜈".split("_"),weekdays:"ę˜ŸęœŸę—„_ꘟꜟäø€_ꘟꜟäŗŒ_ꘟꜟäø‰_ę˜ŸęœŸå››_ꘟꜟäŗ”_ę˜ŸęœŸå…­".split("_"),weekdaysShort:"週ꗄ_週äø€_週äŗŒ_週äø‰_週四_週äŗ”_週六".split("_"),weekdaysMin:"ę—„_äø€_äŗŒ_äø‰_四_äŗ”_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"YYYY幓M꜈Dę—„",LLL:"YYYY幓M꜈Dę—„ HH:mm",LLLL:"YYYY幓M꜈Dę—„dddd HH:mm",l:"D/M/YYYY",ll:"YYYY幓M꜈Dę—„",lll:"YYYY幓M꜈Dę—„ HH:mm",llll:"YYYY幓M꜈Dę—„dddd HH:mm"},meridiemParse:/凌ę™Ø|ę—©äøŠ|äøŠåˆ|äø­åˆ|äø‹åˆ|ꙚäøŠ/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="凌ę™Ø"||t==="ę—©äøŠ"||t==="äøŠåˆ")return e;else if(t==="äø­åˆ")return e>=11?e:e+12;else if(t==="äø‹åˆ"||t==="ꙚäøŠ")return e+12},meridiem:function(e,t,n){var a=e*100+t;if(a<600)return"凌ę™Ø";else if(a<900)return"ę—©äøŠ";else if(a<1130)return"äøŠåˆ";else if(a<1230)return"äø­åˆ";else if(a<1800)return"äø‹åˆ";else return"ꙚäøŠ"},calendar:{sameDay:"[今天] LT",nextDay:"[ę˜Žå¤©] LT",nextWeek:"[äø‹]dddd LT",lastDay:"[ę˜Ø天] LT",lastWeek:"[äøŠ]dddd LT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(ę—„|꜈|週)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"ę—„";case"M":return e+"꜈";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s內",past:"%s前",s:"å¹¾ē§’",ss:"%d ē§’",m:"1 分鐘",mm:"%d 分鐘",h:"1 å°ę™‚",hh:"%d å°ę™‚",d:"1 天",dd:"%d 天",M:"1 å€‹ęœˆ",MM:"%d å€‹ęœˆ",y:"1 幓",yy:"%d 幓"}})}(n(8))},function(e,t,n){!function(e){"use strict"; +var t;e.defineLocale("zh-mo",{months:"äø€ęœˆ_äŗŒęœˆ_äø‰ęœˆ_å››ęœˆ_äŗ”ęœˆ_å…­ęœˆ_äøƒęœˆ_å…«ęœˆ_ä¹ęœˆ_åęœˆ_十äø€ęœˆ_十äŗŒęœˆ".split("_"),monthsShort:"1꜈_2꜈_3꜈_4꜈_5꜈_6꜈_7꜈_8꜈_9꜈_10꜈_11꜈_12꜈".split("_"),weekdays:"ę˜ŸęœŸę—„_ꘟꜟäø€_ꘟꜟäŗŒ_ꘟꜟäø‰_ę˜ŸęœŸå››_ꘟꜟäŗ”_ę˜ŸęœŸå…­".split("_"),weekdaysShort:"週ꗄ_週äø€_週äŗŒ_週äø‰_週四_週äŗ”_週六".split("_"),weekdaysMin:"ę—„_äø€_äŗŒ_äø‰_四_äŗ”_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"YYYY幓M꜈Dę—„",LLL:"YYYY幓M꜈Dę—„ HH:mm",LLLL:"YYYY幓M꜈Dę—„dddd HH:mm",l:"D/M/YYYY",ll:"YYYY幓M꜈Dę—„",lll:"YYYY幓M꜈Dę—„ HH:mm",llll:"YYYY幓M꜈Dę—„dddd HH:mm"},meridiemParse:/凌ę™Ø|ę—©äøŠ|äøŠåˆ|äø­åˆ|äø‹åˆ|ꙚäøŠ/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="凌ę™Ø"||t==="ę—©äøŠ"||t==="äøŠåˆ")return e;else if(t==="äø­åˆ")return e>=11?e:e+12;else if(t==="äø‹åˆ"||t==="ꙚäøŠ")return e+12},meridiem:function(e,t,n){var a=e*100+t;if(a<600)return"凌ę™Ø";else if(a<900)return"ę—©äøŠ";else if(a<1130)return"äøŠåˆ";else if(a<1230)return"äø­åˆ";else if(a<1800)return"äø‹åˆ";else return"ꙚäøŠ"},calendar:{sameDay:"[今天] LT",nextDay:"[ę˜Žå¤©] LT",nextWeek:"[äø‹]dddd LT",lastDay:"[ę˜Ø天] LT",lastWeek:"[äøŠ]dddd LT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(ę—„|꜈|週)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"ę—„";case"M":return e+"꜈";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s內",past:"%s前",s:"å¹¾ē§’",ss:"%d ē§’",m:"1 分鐘",mm:"%d 分鐘",h:"1 å°ę™‚",hh:"%d å°ę™‚",d:"1 天",dd:"%d 天",M:"1 å€‹ęœˆ",MM:"%d å€‹ęœˆ",y:"1 幓",yy:"%d 幓"}})}(n(9))},function(e,t,n){!function(e){"use strict"; //! moment.js locale configuration -var t;e.defineLocale("zh-tw",{months:"äø€ęœˆ_äŗŒęœˆ_äø‰ęœˆ_å››ęœˆ_äŗ”ęœˆ_å…­ęœˆ_äøƒęœˆ_å…«ęœˆ_ä¹ęœˆ_åęœˆ_十äø€ęœˆ_十äŗŒęœˆ".split("_"),monthsShort:"1꜈_2꜈_3꜈_4꜈_5꜈_6꜈_7꜈_8꜈_9꜈_10꜈_11꜈_12꜈".split("_"),weekdays:"ę˜ŸęœŸę—„_ꘟꜟäø€_ꘟꜟäŗŒ_ꘟꜟäø‰_ę˜ŸęœŸå››_ꘟꜟäŗ”_ę˜ŸęœŸå…­".split("_"),weekdaysShort:"週ꗄ_週äø€_週äŗŒ_週äø‰_週四_週äŗ”_週六".split("_"),weekdaysMin:"ę—„_äø€_äŗŒ_äø‰_四_äŗ”_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY幓M꜈Dę—„",LLL:"YYYY幓M꜈Dę—„ HH:mm",LLLL:"YYYY幓M꜈Dę—„dddd HH:mm",l:"YYYY/M/D",ll:"YYYY幓M꜈Dę—„",lll:"YYYY幓M꜈Dę—„ HH:mm",llll:"YYYY幓M꜈Dę—„dddd HH:mm"},meridiemParse:/凌ę™Ø|ę—©äøŠ|äøŠåˆ|äø­åˆ|äø‹åˆ|ꙚäøŠ/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="凌ę™Ø"||t==="ę—©äøŠ"||t==="äøŠåˆ")return e;else if(t==="äø­åˆ")return e>=11?e:e+12;else if(t==="äø‹åˆ"||t==="ꙚäøŠ")return e+12},meridiem:function(e,t,n){var a=e*100+t;if(a<600)return"凌ę™Ø";else if(a<900)return"ę—©äøŠ";else if(a<1130)return"äøŠåˆ";else if(a<1230)return"äø­åˆ";else if(a<1800)return"äø‹åˆ";else return"ꙚäøŠ"},calendar:{sameDay:"[今天] LT",nextDay:"[ę˜Žå¤©] LT",nextWeek:"[äø‹]dddd LT",lastDay:"[ę˜Ø天] LT",lastWeek:"[äøŠ]dddd LT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(ę—„|꜈|週)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"ę—„";case"M":return e+"꜈";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"å¹¾ē§’",ss:"%d ē§’",m:"1 分鐘",mm:"%d 分鐘",h:"1 å°ę™‚",hh:"%d å°ę™‚",d:"1 天",dd:"%d 天",M:"1 å€‹ęœˆ",MM:"%d å€‹ęœˆ",y:"1 幓",yy:"%d 幓"}})}(n(8))},function(e,t,n){"use strict";t.__esModule=!0;var u=p(n(2)),a=p(n(4)),r=p(n(5)),o=p(n(6)),i=n(0),d=p(i),l=p(n(3)),c=p(n(13)),s=p(n(7)),f=n(10);function p(e){return e&&e.__esModule?e:{default:e}}h=i.Component,(0,o.default)(m,h),m.prototype.render=function(){var e,t=this.props,n=t.prefix,a=t.type,r=t.size,o=t.className,i=t.rtl,l=t.style,t=t.children,s=f.obj.pickOthers((0,u.default)({},m.propTypes),this.props),n=(0,c.default)(((e={})[n+"icon"]=!0,e[n+"icon-"+a]=!!a,e[""+n+r]=!!r&&"string"==typeof r,e[o]=!!o,e)),o=(i&&-1!==["arrow-left","arrow-right","arrow-double-left","arrow-double-right","switch","sorting","descending","ascending"].indexOf(a)&&(s.dir="rtl"),"number"==typeof r?{width:r,height:r,lineHeight:r+"px",fontSize:r}:{});return d.default.createElement("i",(0,u.default)({},s,{style:(0,u.default)({},o,l),className:n}),t)},i=n=m,n.propTypes=(0,u.default)({},s.default.propTypes,{type:l.default.string,children:l.default.node,size:l.default.oneOfType([l.default.oneOf(["xxs","xs","small","medium","large","xl","xxl","xxxl","inherit"]),l.default.number]),className:l.default.string,style:l.default.object}),n.defaultProps={prefix:"next-",size:"medium"},n._typeMark="icon";var h,o=i;function m(){return(0,a.default)(this,m),(0,r.default)(this,h.apply(this,arguments))}o.displayName="Icon",t.default=o,e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var g=s(n(2)),y=s(n(12)),a=s(n(37)),r=s(n(4)),o=s(n(5)),i=s(n(6)),v=n(0),_=s(v),l=s(n(3)),b=n(157),w=s(n(525));function s(e){return e&&e.__esModule?e:{default:e}}function u(){}function M(e){return _.default.Children.toArray(e.children)[0]||null}d=v.Component,(0,i.default)(c,d),c.prototype.normalizeNames=function(e){return"string"==typeof e?{appear:e+"-appear",appearActive:e+"-appear-active",enter:e+"-enter",enterActive:e+"-enter-active",leave:e+"-leave",leaveActive:e+"-leave-active"}:"object"===(void 0===e?"undefined":(0,a.default)(e))?{appear:e.appear,appearActive:e.appear+"-active",enter:""+e.enter,enterActive:e.enter+"-active",leave:""+e.leave,leaveActive:e.leave+"-active"}:void 0},c.prototype.render=function(){var t=this,e=this.props,n=e.animation,a=e.children,r=e.animationAppear,o=e.singleMode,i=e.component,l=e.beforeAppear,s=e.onAppear,u=e.afterAppear,d=e.beforeEnter,c=e.onEnter,f=e.afterEnter,p=e.beforeLeave,h=e.onLeave,m=e.afterLeave,e=(0,y.default)(e,["animation","children","animationAppear","singleMode","component","beforeAppear","onAppear","afterAppear","beforeEnter","onEnter","afterEnter","beforeLeave","onLeave","afterLeave"]),a=v.Children.map(a,function(e){return _.default.createElement(w.default,{key:e.key,names:t.normalizeNames(n),onAppear:l,onAppearing:s,onAppeared:u,onEnter:d,onEntering:c,onEntered:f,onExit:p,onExiting:h,onExited:m},e)});return _.default.createElement(b.TransitionGroup,(0,g.default)({appear:r,component:o?M:i},e),a)},i=n=c,n.propTypes={animation:l.default.oneOfType([l.default.string,l.default.object]),animationAppear:l.default.bool,component:l.default.any,singleMode:l.default.bool,children:l.default.oneOfType([l.default.element,l.default.arrayOf(l.default.element)]),beforeAppear:l.default.func,onAppear:l.default.func,afterAppear:l.default.func,beforeEnter:l.default.func,onEnter:l.default.func,afterEnter:l.default.func,beforeLeave:l.default.func,onLeave:l.default.func,afterLeave:l.default.func},n.defaultProps={animationAppear:!0,component:"div",singleMode:!0,beforeAppear:u,onAppear:u,afterAppear:u,beforeEnter:u,onEnter:u,afterEnter:u,beforeLeave:u,onLeave:u,afterLeave:u};var d,l=i;function c(){return(0,r.default)(this,c),(0,o.default)(this,d.apply(this,arguments))}l.displayName="Animate",t.default=l,e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0,t.default=t.EXITING=t.ENTERED=t.ENTERING=t.EXITED=t.UNMOUNTED=void 0;var a=function(e){{if(e&&e.__esModule)return e;var t,n={};if(null!=e)for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&((t=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,a):{}).get||t.set?Object.defineProperty(n,a,t):n[a]=e[a]);return n.default=e,n}}(n(3)),o=l(n(0)),i=l(n(22)),r=n(30);n(346);function l(e){return e&&e.__esModule?e:{default:e}}var s="unmounted",u=(t.UNMOUNTED=s,"exited"),d=(t.EXITED=u,"entering"),c=(t.ENTERING=d,"entered"),f=(t.ENTERED=c,"exiting"),n=(t.EXITING=f,function(r){var e;function t(e,t){var n,a=r.call(this,e,t)||this,t=t.transitionGroup,t=t&&!t.isMounting?e.enter:e.appear;return a.appearStatus=null,e.in?t?(n=u,a.appearStatus=d):n=c:n=e.unmountOnExit||e.mountOnEnter?s:u,a.state={status:n},a.nextCallback=null,a}e=r,(n=t).prototype=Object.create(e.prototype),(n.prototype.constructor=n).__proto__=e;var n=t.prototype;return n.getChildContext=function(){return{transitionGroup:null}},t.getDerivedStateFromProps=function(e,t){return e.in&&t.status===s?{status:u}:null},n.componentDidMount=function(){this.updateStatus(!0,this.appearStatus)},n.componentDidUpdate=function(e){var t=null;e!==this.props&&(e=this.state.status,this.props.in?e!==d&&e!==c&&(t=d):e!==d&&e!==c||(t=f)),this.updateStatus(!1,t)},n.componentWillUnmount=function(){this.cancelNextCallback()},n.getTimeouts=function(){var e,t,n=this.props.timeout,a=e=t=n;return null!=n&&"number"!=typeof n&&(a=n.exit,e=n.enter,t=void 0!==n.appear?n.appear:e),{exit:a,enter:e,appear:t}},n.updateStatus=function(e,t){var n;void 0===e&&(e=!1),null!==t?(this.cancelNextCallback(),n=i.default.findDOMNode(this),t===d?this.performEnter(n,e):this.performExit(n)):this.props.unmountOnExit&&this.state.status===u&&this.setState({status:s})},n.performEnter=function(e,t){var n=this,a=this.props.enter,r=this.context.transitionGroup?this.context.transitionGroup.isMounting:t,o=this.getTimeouts(),i=r?o.appear:o.enter;t||a?(this.props.onEnter(e,r),this.safeSetState({status:d},function(){n.props.onEntering(e,r),n.onTransitionEnd(e,i,function(){n.safeSetState({status:c},function(){n.props.onEntered(e,r)})})})):this.safeSetState({status:c},function(){n.props.onEntered(e)})},n.performExit=function(e){var t=this,n=this.props.exit,a=this.getTimeouts();n?(this.props.onExit(e),this.safeSetState({status:f},function(){t.props.onExiting(e),t.onTransitionEnd(e,a.exit,function(){t.safeSetState({status:u},function(){t.props.onExited(e)})})})):this.safeSetState({status:u},function(){t.props.onExited(e)})},n.cancelNextCallback=function(){null!==this.nextCallback&&(this.nextCallback.cancel(),this.nextCallback=null)},n.safeSetState=function(e,t){t=this.setNextCallback(t),this.setState(e,t)},n.setNextCallback=function(t){var n=this,a=!0;return this.nextCallback=function(e){a&&(a=!1,n.nextCallback=null,t(e))},this.nextCallback.cancel=function(){a=!1},this.nextCallback},n.onTransitionEnd=function(e,t,n){this.setNextCallback(n);n=null==t&&!this.props.addEndListener;!e||n?setTimeout(this.nextCallback,0):(this.props.addEndListener&&this.props.addEndListener(e,this.nextCallback),null!=t&&setTimeout(this.nextCallback,t))},n.render=function(){var e=this.state.status;if(e===s)return null;var t=this.props,n=t.children,t=function(e,t){if(null==e)return{};for(var n,a={},r=Object.keys(e),o=0;o 16.8.0"),null;function t(e){j.current=e,ne({})}function a(e,t){N(te,null),"function"==typeof S&&S(e,t)}function n(e){27===e.keyCode&&Y&&!P.current.size&&a("esc",e)}function r(){j.current||(t(!0),he.dom.setStyle(D.current,"display","none"),me.default.unlock(document.body,ee.current),s&&O.current&&(O.current.focus(),O.current=null),I())}var o=e.prefix,o=void 0===o?"next-":o,i=e.afterClose,I=void 0===i?ye:i,i=e.hasMask,l=void 0===i||i,i=e.autoFocus,s=void 0!==i&&i,i=e.className,R=e.title,A=e.children,H=e.footer,F=e.footerAlign,z=e.footerActions,u=e.onOk,u=void 0===u?ye:u,d=e.onCancel,W=e.okProps,V=e.cancelProps,c=e.locale,c=void 0===c?pe.default.Dialog:c,B=e.rtl,f=e.visible,p=e.closeMode,p=void 0===p?["close","esc"]:p,U=e.closeIcon,h=e.animation,h=void 0===h?{in:"fadeInUp",out:"fadeOutUp"}:h,m=e.cache,K=e.wrapperStyle,g=e.popupContainer,y=void 0===g?document.body:g,g=e.dialogRender,v=e.centered,_=e.top,_=void 0===_?v?40:100:_,b=e.bottom,b=void 0===b?40:b,w=e.width,w=void 0===w?520:w,G=e.height,M=e.isFullScreen,k=e.overflowScroll,M=void 0===k?!M:k,k=e.minMargin,S=e.onClose,q=e.style,E=(0,ie.default)(e,["prefix","afterClose","hasMask","autoFocus","className","title","children","footer","footerAlign","footerActions","onOk","onCancel","okProps","cancelProps","locale","rtl","visible","closeMode","closeIcon","animation","cache","wrapperStyle","popupContainer","dialogRender","centered","top","bottom","width","height","isFullScreen","overflowScroll","minMargin","onClose","style"]),x=("isFullScreen"in e&&he.log.deprecated("isFullScreen","overflowScroll","Dialog v2"),"minMargin"in e&&he.log.deprecated("minMargin","top/bottom","Dialog v2"),(0,le.useState)(f||!1)),$=x[0],J=x[1],x=(0,le.useState)(f),C=x[0],X=x[1],Q="string"==typeof y?function(){return document.getElementById(y)}:"function"!=typeof y?function(){return y}:y,x=(0,le.useState)(Q()),L=x[0],Z=x[1],T=(0,le.useRef)(null),D=(0,le.useRef)(null),O=(0,le.useRef)(null),ee=(0,le.useRef)(null),te=(0,le.useState)((0,he.guid)())[0],x=(0,le.useContext)(ge),N=x.setVisibleOverlayToParent,x=(0,ie.default)(x,["setVisibleOverlayToParent"]),P=(0,le.useRef)(new Map),j=(0,le.useRef)(!1),ne=(0,le.useState)()[1],Y=!1,ae=!1,re=!1;(Array.isArray(p)?p:[p]).forEach(function(e){switch(e){case"esc":Y=!0;break;case"mask":ae=!0;break;case"close":re=!0}}),(0,le.useEffect)(function(){"visible"in e&&X(f)},[f]),(0,le.useEffect)(function(){var e;C&&l&&(e={overflow:"hidden"},he.dom.hasScroll(document.body)&&he.dom.scrollbar().width&&(e.paddingRight=he.dom.getStyle(document.body,"paddingRight")+he.dom.scrollbar().width+"px"),ee.current=me.default.lock(document.body,e))},[C&&l]),(0,le.useEffect)(function(){if(C&&Y)return document.body.addEventListener("keydown",n,!1),function(){document.body.removeEventListener("keydown",n,!1)}},[C&&Y]),(0,le.useEffect)(function(){!$&&C&&J(!0)},[C]),(0,le.useEffect)(function(){L||setTimeout(function(){Z(Q())})},[L]);if((0,le.useEffect)(function(){return function(){r()}},[]),!1===$||!L)return null;if(!C&&!m&&j.current)return null;m=(0,de.default)(((p={})[o+"overlay-wrapper"]=!0,p.opened=C,p)),i=(0,de.default)(((p={})[o+"dialog-v2"]=!0,p[i]=!!i,p)),p={},k=void(v?_||b||!k?(_&&(p.marginTop=_),b&&(p.marginBottom=b)):(p.marginTop=k,p.marginBottom=k):(_&&(p.top=_),b&&(p.paddingBottom=b))),M&&(k="calc(100vh - "+(_+b)+"px)"),M={appear:300,enter:300,exit:250},_=se.default.createElement(fe.default.OverlayAnimate,{visible:C,animation:h,timeout:M,onEnter:function(){t(!1),he.dom.setStyle(D.current,"display","")},onEntered:function(){var e;s&&T.current&&T.current.bodyNode&&(0<(e=he.focus.getFocusNodeList(T.current.bodyNode)).length&&e[0]&&(O.current=document.activeElement,e[0].focus())),N(te,D.current)},onExited:r},se.default.createElement(ce.default,(0,oe.default)({},E,{style:v?(0,oe.default)({},p,q):q,v2:!0,ref:T,prefix:o,className:i,title:R,footer:H,footerAlign:F,footerActions:z,onOk:C?u:ye,onCancel:C?function(e){"function"==typeof d?d(e):a("cancelBtn",e)}:ye,okProps:W,cancelProps:V,locale:c,closeable:re,rtl:B,onClose:function(){for(var e=arguments.length,t=Array(e),n=0;n>6]+d[128|63&s]:s<55296||57344<=s?i+=d[224|s>>12]+d[128|s>>6&63]+d[128|63&s]:(l+=1,s=65536+((1023&s)<<10|1023&o.charCodeAt(l)),i+=d[240|s>>18]+d[128|s>>12&63]+d[128|s>>6&63]+d[128|63&s])}return i},isBuffer:function(e){return!(!e||"object"!=typeof e)&&!!(e.constructor&&e.constructor.isBuffer&&e.constructor.isBuffer(e))},isRegExp:function(e){return"[object RegExp]"===Object.prototype.toString.call(e)},maybeMap:function(e,t){if(m(e)){for(var n=[],a=0;athis.popupNode.offsetWidth&&p(this.popupNode,"width",l.offsetWidth+"px"),"outside"!==a||"hoz"===r&&1===n||(p(this.popupNode,"height",u.offsetHeight+"px"),this.popupNode.firstElementChild&&p(this.popupNode.firstElementChild,"overflow-y","auto")),this.popupProps);d.onOpen&&d.onOpen()}catch(e){return null}},S.prototype.handlePopupClose=function(){var e=this.props.root.popupNodes,t=e.indexOf(this.popupNode),e=(-1t?r[t+1]:r[0])}),n[a]||(o=r[0]),i.onSort(a,o)},i.keydownHandler=function(e){e.preventDefault(),e.stopPropagation(),e.keyCode===l.KEYCODE.ENTER&&i.handleClick()},i.onSort=function(e,t){var n={};n[e]=t,i.props.onSort(e,t,n)},(0,o.default)(i,e)}i.displayName="Sort",t.default=i,e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0,t.default=void 0;var r=c(n(2)),a=c(n(4)),o=c(n(5)),i=c(n(6)),l=c(n(0)),s=c(n(3)),u=c(n(13)),d=c(n(389));function c(e){return e&&e.__esModule?e:{default:e}}f=l.default.Component,(0,i.default)(p,f),p.prototype.render=function(){var e=this.props,t=e.className,n=e.record,e=e.primaryKey,a=this.context.selectedRowKeys,n=(0,u.default)(((a={selected:-1 "+this.message+" (line "+this.parsedLine+": '"+this.snippet+"')":" "+this.message},o}(Error);e.exports=n},function(e,t){var i={}.hasOwnProperty,n=function(e){var t,n=o,a=e;for(t in a)i.call(a,t)&&(n[t]=a[t]);function r(){this.constructor=n}function o(e,t,n){this.message=e,this.parsedLine=t,this.snippet=n}return r.prototype=a.prototype,n.prototype=new r,n.__super__=a.prototype,o.prototype.toString=function(){return null!=this.parsedLine&&null!=this.snippet?" "+this.message+" (line "+this.parsedLine+": '"+this.snippet+"')":" "+this.message},o}(Error);e.exports=n},function(e,t,n){"use strict";t.__esModule=!0,t.default=void 0;var y=m(n(2)),a=m(n(4)),r=m(n(5)),o=m(n(6)),i=n(0),v=m(i),l=n(22),s=m(n(3)),_=m(n(13)),u=n(10),f=m(n(50)),d=m(n(61)),b=m(n(11)),c=m(n(44)),p=m(n(675)),h=m(n(400)),w=n(177);function m(e){return e&&e.__esModule?e:{default:e}}var M=d.default.Popup,g=f.default.Item,k=f.default.Group,n=u.func.noop,S=u.func.bindCtx,E=u.func.makeChain;x=v.default.Component,(0,o.default)(C,x),C.prototype.componentDidMount=function(){var e=this;setTimeout(function(){return e.syncWidth()},0),u.events.on(window,"resize",this.handleResize)},C.prototype.componentDidUpdate=function(e,t){e.label===this.props.label&&t.value===this.state.value||this.syncWidth()},C.prototype.componentWillUnmount=function(){u.events.off(window,"resize",this.handleResize),clearTimeout(this.resizeTimeout)},C.prototype.syncWidth=function(){var e=this,t=this.props,n=t.popupStyle,t=t.popupProps;n&&"width"in n||t&&t.style&&"width"in t.style||(n=u.dom.getStyle(this.selectDOM,"width"))&&this.width!==n&&(this.width=n,this.popupRef&&this.shouldAutoWidth()&&setTimeout(function(){e.popupRef&&u.dom.setStyle(e.popupRef,"width",e.width)},0))},C.prototype.handleResize=function(){var e=this;clearTimeout(this.resizeTimeout),this.state.visible&&(this.resizeTimeout=setTimeout(function(){e.syncWidth()},200))},C.prototype.setDataSource=function(e){var t=e.dataSource,e=e.children;return i.Children.count(e)?this.dataStore.updateByDS(e,!0):Array.isArray(t)?this.dataStore.updateByDS(t,!1):[]},C.prototype.setVisible=function(e,t){this.props.disabled&&e||this.state.visible===e||("visible"in this.props||this.setState({visible:e}),this.props.onVisibleChange(e,t))},C.prototype.setFirstHightLightKeyForMenu=function(e){var t=this.state.highlightKey;this.props.autoHighlightFirstItem&&(this.dataStore.getMenuDS().length&&this.dataStore.getEnableDS().length&&(!t||e)&&(t=""+this.dataStore.getEnableDS()[0].value,this.setState({highlightKey:t}),this.props.onToggleHighlightItem(t,"autoFirstItem")),e&&!this.dataStore.getEnableDS().length&&(this.setState({highlightKey:null}),this.props.onToggleHighlightItem(null,"highlightKeyToNull")))},C.prototype.handleChange=function(e){var t;"value"in this.props||this.setState({value:e});for(var n=arguments.length,a=Array(1e.slidesToShow&&(n=e.slideWidth*e.slidesToShow*-1,o=e.slideHeight*e.slidesToShow*-1),e.slideCount%e.slidesToScroll!=0&&(t=e.slideIndex+e.slidesToScroll>e.slideCount&&e.slideCount>e.slidesToShow,(t=e.rtl?(e.slideIndex>=e.slideCount?e.slideCount-e.slideIndex:e.slideIndex)+e.slidesToScroll>e.slideCount&&e.slideCount>e.slidesToShow:t)&&(o=e.slideIndex>e.slideCount?(n=(e.slidesToShow-(e.slideIndex-e.slideCount))*e.slideWidth*-1,(e.slidesToShow-(e.slideIndex-e.slideCount))*e.slideHeight*-1):(n=e.slideCount%e.slidesToScroll*e.slideWidth*-1,e.slideCount%e.slidesToScroll*e.slideHeight*-1)))):e.slideCount%e.slidesToScroll!=0&&e.slideIndex+e.slidesToScroll>e.slideCount&&e.slideCount>e.slidesToShow&&(n=(e.slidesToShow-e.slideCount%e.slidesToScroll)*e.slideWidth),e.centerMode&&(e.infinite?n+=e.slideWidth*Math.floor(e.slidesToShow/2):n=e.slideWidth*Math.floor(e.slidesToShow/2)),a=e.vertical?e.slideIndex*e.slideHeight*-1+o:e.slideIndex*e.slideWidth*-1+n,!0===e.variableWidth&&(t=void 0,a=(r=e.slideCount<=e.slidesToShow||!1===e.infinite?i.default.findDOMNode(e.trackRef).childNodes[e.slideIndex]:(t=e.slideIndex+e.slidesToShow,i.default.findDOMNode(e.trackRef).childNodes[t]))?-1*r.offsetLeft:0,!0===e.centerMode&&(r=!1===e.infinite?i.default.findDOMNode(e.trackRef).children[e.slideIndex]:i.default.findDOMNode(e.trackRef).children[e.slideIndex+e.slidesToShow+1])&&(a=-1*r.offsetLeft+(e.listWidth-r.offsetWidth)/2)),a)}},function(e,t,n){"use strict";t.__esModule=!0;var p=u(n(2)),h=u(n(12)),o=u(n(4)),i=u(n(5)),a=u(n(6)),m=u(n(0)),r=u(n(3)),g=u(n(13)),l=u(n(7)),y=u(n(24)),s=n(10);function u(e){return e&&e.__esModule?e:{default:e}}d=m.default.Component,(0,a.default)(c,d),c.prototype.render=function(){var e=this.props,t=e.title,n=e.children,a=e.className,r=e.isExpanded,o=e.disabled,i=e.style,l=e.prefix,s=e.onClick,u=e.id,e=(0,h.default)(e,["title","children","className","isExpanded","disabled","style","prefix","onClick","id"]),a=(0,g.default)(((d={})[l+"collapse-panel"]=!0,d[l+"collapse-panel-hidden"]=!r,d[l+"collapse-panel-expanded"]=r,d[l+"collapse-panel-disabled"]=o,d[a]=a,d)),d=(0,g.default)(((d={})[l+"collapse-panel-icon"]=!0,d[l+"collapse-panel-icon-expanded"]=r,d)),c=u?u+"-heading":void 0,f=u?u+"-region":void 0;return m.default.createElement("div",(0,p.default)({className:a,style:i,id:u},e),m.default.createElement("div",{id:c,className:l+"collapse-panel-title",onClick:s,onKeyDown:this.onKeyDown,tabIndex:"0","aria-disabled":o,"aria-expanded":r,"aria-controls":f,role:"button"},m.default.createElement(y.default,{type:"arrow-right",className:d,"aria-hidden":"true"}),t),m.default.createElement("div",{className:l+"collapse-panel-content",role:"region",id:f},n))},a=n=c,n.propTypes={prefix:r.default.string,style:r.default.object,children:r.default.any,isExpanded:r.default.bool,disabled:r.default.bool,title:r.default.node,className:r.default.string,onClick:r.default.func,id:r.default.string},n.defaultProps={prefix:"next-",isExpanded:!1,onClick:s.func.noop},n.isNextPanel=!0;var d,r=a;function c(){var e,n;(0,o.default)(this,c);for(var t=arguments.length,a=Array(t),r=0;r\n com.alibaba.nacos\n nacos-client\n ${version}\n \n*/\npackage com.alibaba.nacos.example;\n\nimport java.util.Properties;\nimport java.util.concurrent.Executor;\nimport com.alibaba.nacos.api.NacosFactory;\nimport com.alibaba.nacos.api.config.ConfigService;\nimport com.alibaba.nacos.api.config.listener.Listener;\nimport com.alibaba.nacos.api.exception.NacosException;\n\n/**\n * Config service example\n *\n * @author Nacos\n *\n */\npublic class ConfigExample {\n\n\tpublic static void main(String[] args) throws NacosException, InterruptedException {\n\t\tString serverAddr = "localhost";\n\t\tString dataId = "'.concat(e.dataId,'";\n\t\tString group = "').concat(e.group,'";\n\t\tProperties properties = new Properties();\n\t\tproperties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);\n\t\tConfigService configService = NacosFactory.createConfigService(properties);\n\t\tString content = configService.getConfig(dataId, group, 5000);\n\t\tSystem.out.println(content);\n\t\tconfigService.addListener(dataId, group, new Listener() {\n\t\t\t@Override\n\t\t\tpublic void receiveConfigInfo(String configInfo) {\n\t\t\t\tSystem.out.println("recieve:" + configInfo);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Executor getExecutor() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t});\n\n\t\tboolean isPublishOk = configService.publishConfig(dataId, group, "content");\n\t\tSystem.out.println(isPublishOk);\n\n\t\tThread.sleep(3000);\n\t\tcontent = configService.getConfig(dataId, group, 5000);\n\t\tSystem.out.println(content);\n\n\t\tboolean isRemoveOk = configService.removeConfig(dataId, group);\n\t\tSystem.out.println(isRemoveOk);\n\t\tThread.sleep(3000);\n\n\t\tcontent = configService.getConfig(dataId, group, 5000);\n\t\tSystem.out.println(content);\n\t\tThread.sleep(300000);\n\n\t}\n}\n')}},{key:"getNodejsCode",value:function(e){return"TODO"}},{key:"getCppCode",value:function(e){return"TODO"}},{key:"getShellCode",value:function(e){return"TODO"}},{key:"getPythonCode",value:function(e){return"TODO"}},{key:"getCSharpCode",value:function(e){return'/*\nDemo for Basic Nacos Opreation\nApp.csproj\n\n\n \n\n*/\n\nusing Microsoft.Extensions.DependencyInjection;\nusing Nacos.V2;\nusing Nacos.V2.DependencyInjection;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nclass Program\n{\n static async Task Main(string[] args)\n {\n string serverAddr = "http://localhost:8848";\n string dataId = "'.concat(e.dataId,'";\n string group = "').concat(e.group,'";\n\n IServiceCollection services = new ServiceCollection();\n\n services.AddNacosV2Config(x =>\n {\n x.ServerAddresses = new List { serverAddr };\n x.Namespace = "cs-test";\n\n // swich to use http or rpc\n x.ConfigUseRpc = true;\n });\n\n IServiceProvider serviceProvider = services.BuildServiceProvider();\n var configSvc = serviceProvider.GetService();\n\n var content = await configSvc.GetConfig(dataId, group, 3000);\n Console.WriteLine(content);\n\n var listener = new ConfigListener();\n\n await configSvc.AddListener(dataId, group, listener);\n\n var isPublishOk = await configSvc.PublishConfig(dataId, group, "content");\n Console.WriteLine(isPublishOk);\n\n await Task.Delay(3000);\n content = await configSvc.GetConfig(dataId, group, 5000);\n Console.WriteLine(content);\n\n var isRemoveOk = await configSvc.RemoveConfig(dataId, group);\n Console.WriteLine(isRemoveOk);\n await Task.Delay(3000);\n\n content = await configSvc.GetConfig(dataId, group, 5000);\n Console.WriteLine(content);\n await Task.Delay(300000);\n }\n\n internal class ConfigListener : IListener\n {\n public void ReceiveConfigInfo(string configInfo)\n {\n Console.WriteLine("recieve:" + configInfo);\n }\n }\n}\n\n/*\nRefer to document: https://github.com/nacos-group/nacos-sdk-csharp/tree/dev/samples/MsConfigApp\nDemo for ASP.NET Core Integration\nMsConfigApp.csproj\n\n\n \n\n*/\n\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.Hosting;\nusing Serilog;\nusing Serilog.Events;\n\npublic class Program\n{\n public static void Main(string[] args)\n {\n Log.Logger = new LoggerConfiguration()\n .Enrich.FromLogContext()\n .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)\n .MinimumLevel.Override("System", LogEventLevel.Warning)\n .MinimumLevel.Debug()\n .WriteTo.Console()\n .CreateLogger();\n\n try\n {\n Log.ForContext().Information("Application starting...");\n CreateHostBuilder(args, Log.Logger).Build().Run();\n }\n catch (System.Exception ex)\n {\n Log.ForContext().Fatal(ex, "Application start-up failed!!");\n }\n finally\n {\n Log.CloseAndFlush();\n }\n }\n\n public static IHostBuilder CreateHostBuilder(string[] args, Serilog.ILogger logger) =>\n Host.CreateDefaultBuilder(args)\n .ConfigureAppConfiguration((context, builder) =>\n {\n var c = builder.Build();\n builder.AddNacosV2Configuration(c.GetSection("NacosConfig"), logAction: x => x.AddSerilog(logger));\n })\n .ConfigureWebHostDefaults(webBuilder =>\n {\n webBuilder.UseStartup().UseUrls("http://*:8787");\n })\n .UseSerilog();\n}\n ')}},{key:"openDialog",value:function(e){var t=this;this.setState({dialogvisible:!0}),this.record=e,setTimeout(function(){t.getData()})}},{key:"closeDialog",value:function(){this.setState({dialogvisible:!1})}},{key:"createCodeMirror",value:function(e,t){var n=this.refs.codepreview;n&&(n.innerHTML="",this.cm=window.CodeMirror(n,{value:t,mode:e,height:400,width:500,lineNumbers:!0,theme:"xq-light",lint:!0,tabMode:"indent",autoMatchParens:!0,textWrapping:!0,gutters:["CodeMirror-lint-markers"],extraKeys:{F1:function(e){e.setOption("fullScreen",!e.getOption("fullScreen"))},Esc:function(e){e.getOption("fullScreen")&&e.setOption("fullScreen",!1)}}}))}},{key:"changeTab",value:function(e,t){var n=this;setTimeout(function(){n[e]=!0,n.createCodeMirror("text/javascript",t)})}},{key:"render",value:function(){var e=this.props.locale,e=void 0===e?{}:e;return x.a.createElement("div",null,x.a.createElement(y.a,{title:e.sampleCode,style:{width:"80%"},visible:this.state.dialogvisible,footer:x.a.createElement("div",null),onClose:this.closeDialog.bind(this)},x.a.createElement("div",{style:{height:500}},x.a.createElement(R.a,{tip:e.loading,style:{width:"100%"},visible:this.state.loading},x.a.createElement(O.a,{shape:"text",style:{height:40,paddingBottom:10}},x.a.createElement(N,{title:"Java",key:1,onClick:this.changeTab.bind(this,"commoneditor1",this.defaultCode)}),x.a.createElement(N,{title:"Spring Boot",key:2,onClick:this.changeTab.bind(this,"commoneditor2",this.sprigboot_code)}),x.a.createElement(N,{title:"Spring Cloud",key:21,onClick:this.changeTab.bind(this,"commoneditor21",this.sprigcloud_code)}),x.a.createElement(N,{title:"Node.js",key:3,onClick:this.changeTab.bind(this,"commoneditor3",this.nodejsCode)}),x.a.createElement(N,{title:"C++",key:4,onClick:this.changeTab.bind(this,"commoneditor4",this.cppCode)}),x.a.createElement(N,{title:"Shell",key:5,onClick:this.changeTab.bind(this,"commoneditor5",this.shellCode)}),x.a.createElement(N,{title:"Python",key:6,onClick:this.changeTab.bind(this,"commoneditor6",this.pythonCode)}),x.a.createElement(N,{title:"C#",key:7,onClick:this.changeTab.bind(this,"commoneditor7",this.csharpCode)})),x.a.createElement("div",{ref:"codepreview"})))))}}]),n}(x.a.Component)).displayName="ShowCodeing",S=S))||S,S=(t(65),t(39)),S=t.n(S),H=(t(716),S.a.Row),P=S.a.Col,F=(0,n.a.config)(((S=function(e){Object(M.a)(n,e);var t=Object(k.a)(n);function n(e){return Object(_.a)(this,n),(e=t.call(this,e)).state={visible:!1,title:"",content:"",isok:!0,dataId:"",group:""},e}return Object(b.a)(n,[{key:"componentDidMount",value:function(){this.initData()}},{key:"initData",value:function(){var e=this.props.locale;this.setState({title:(void 0===e?{}:e).confManagement})}},{key:"openDialog",value:function(e){this.setState({visible:!0,title:e.title,content:e.content,isok:e.isok,dataId:e.dataId,group:e.group,message:e.message})}},{key:"closeDialog",value:function(){this.setState({visible:!1})}},{key:"render",value:function(){var e=this.props.locale,e=void 0===e?{}:e,t=x.a.createElement("div",{style:{textAlign:"right"}},x.a.createElement(d.a,{type:"primary",onClick:this.closeDialog.bind(this)},e.determine));return x.a.createElement("div",null,x.a.createElement(y.a,{visible:this.state.visible,footer:t,style:{width:555},onCancel:this.closeDialog.bind(this),onClose:this.closeDialog.bind(this),title:e.deletetitle},x.a.createElement("div",null,x.a.createElement(H,null,x.a.createElement(P,{span:"4",style:{paddingTop:16}},x.a.createElement(m.a,{type:"".concat(this.state.isok?"success":"delete","-filling"),style:{color:this.state.isok?"green":"red"},size:"xl"})),x.a.createElement(P,{span:"20"},x.a.createElement("div",null,x.a.createElement("h3",null,this.state.isok?e.deletedSuccessfully:e.deleteFailed),x.a.createElement("p",null,x.a.createElement("span",{style:{color:"#999",marginRight:5}},"Data ID"),x.a.createElement("span",{style:{color:"#c7254e"}},this.state.dataId)),x.a.createElement("p",null,x.a.createElement("span",{style:{color:"#999",marginRight:5}},"Group"),x.a.createElement("span",{style:{color:"#c7254e"}},this.state.group)),this.state.isok?"":x.a.createElement("p",{style:{color:"red"}},this.state.message)))))))}}]),n}(x.a.Component)).displayName="DeleteDialog",S=S))||S,S=(t(717),t(419)),z=t.n(S),W=(0,n.a.config)(((S=function(e){Object(M.a)(n,e);var t=Object(k.a)(n);function n(){return Object(_.a)(this,n),t.apply(this,arguments)}return Object(b.a)(n,[{key:"render",value:function(){var e=this.props,t=e.data,t=void 0===t?{}:t,n=e.height,e=e.locale,a=void 0===e?{}:e;return x.a.createElement("div",null,"notice"===t.modeType?x.a.createElement("div",{"data-spm-click":"gostr=/aliyun;locaid=notice"},x.a.createElement(z.a,{style:{marginBottom:1l?b.a.createElement(u.a,{className:"pagination",total:i.count,pageSize:l,onChange:function(e){return a.onChangePage(e)}}):null,b.a.createElement(C,{ref:this.editInstanceDialog,serviceName:n,clusterName:t,groupName:e,openLoading:function(){return a.openLoading()},closeLoading:function(){return a.closeLoading()},getInstanceList:function(){return a.getInstanceList()}})):null}}]),n}(b.a.Component)).displayName="InstanceTable",l.defaultProps={filters:new Map},l=l))||l,L=function(e,t){return e.filter(function(e){var n=e.metadata,a=!0;return t.forEach(function(e,t){if(e!==n[t])return a=!1}),a})},T=l,D=t(47),O=t(31),l=(t(175),t(73)),l=t.n(l),N=l.a.Group,P=l.a.Closeable,j=v.a.Item;var R=n.a.config(function(e){function t(){var e;a(),o&&l&&(e=new Map(Array.from(p)).set(o,l),h(e),d(""),f(""),n())}function n(){i(""),s("")}function a(){d(o?"":"error"),f(l?"":"error")}var r=Object(_.useState)(""),o=(r=Object(O.a)(r,2))[0],i=r[1],r=Object(_.useState)(""),l=(r=Object(O.a)(r,2))[0],s=r[1],r=Object(_.useState)(""),u=(r=Object(O.a)(r,2))[0],d=r[1],r=Object(_.useState)(""),c=(r=Object(O.a)(r,2))[0],f=r[1],r=Object(_.useState)(new Map),p=(r=Object(O.a)(r,2))[0],h=r[1],r=void 0===(r=e.locale)?{}:r;return Object(_.useEffect)(function(){e.setFilters(p)},[p]),b.a.createElement(m.a,{contentHeight:"auto",className:"inner-card"},b.a.createElement(v.a,{inline:!0,size:"small"},b.a.createElement(j,{label:r.title},b.a.createElement(j,null,b.a.createElement(y.a,{placeholder:"key",value:o,trim:!0,onChange:function(e){return i(e)},onPressEnter:t,state:u})),b.a.createElement(j,null,b.a.createElement(y.a,{placeholder:"value",value:l,trim:!0,onChange:function(e){return s(e)},onPressEnter:t,state:c})),b.a.createElement(j,{label:""},b.a.createElement(g.a,{type:"primary",onClick:t,style:{marginRight:10}},r.addFilter),0\n com.alibaba.nacos\n nacos-client\n ${latest.version}\n \n*/\npackage com.alibaba.nacos.example;\n\nimport java.util.Properties;\n\nimport com.alibaba.nacos.api.exception.NacosException;\nimport com.alibaba.nacos.api.naming.NamingFactory;\nimport com.alibaba.nacos.api.naming.NamingService;\nimport com.alibaba.nacos.api.naming.listener.Event;\nimport com.alibaba.nacos.api.naming.listener.EventListener;\nimport com.alibaba.nacos.api.naming.listener.NamingEvent;\n\n/**\n * @author nkorange\n */\npublic class NamingExample {\n\n public static void main(String[] args) throws NacosException {\n\n Properties properties = new Properties();\n properties.setProperty("serverAddr", System.getProperty("serverAddr"));\n properties.setProperty("namespace", System.getProperty("namespace"));\n\n NamingService naming = NamingFactory.createNamingService(properties);\n\n naming.registerInstance("'.concat(this.record.name,'", "11.11.11.11", 8888, "TEST1");\n\n naming.registerInstance("').concat(this.record.name,'", "2.2.2.2", 9999, "DEFAULT");\n\n System.out.println(naming.getAllInstances("').concat(this.record.name,'"));\n\n naming.deregisterInstance("').concat(this.record.name,'", "2.2.2.2", 9999, "DEFAULT");\n\n System.out.println(naming.getAllInstances("').concat(this.record.name,'"));\n\n naming.subscribe("').concat(this.record.name,'", new EventListener() {\n @Override\n public void onEvent(Event event) {\n System.out.println(((NamingEvent)event).getServiceName());\n System.out.println(((NamingEvent)event).getInstances());\n }\n });\n }\n}')}},{key:"getSpringCode",value:function(e){return'/* Refer to document: https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-example/nacos-spring-discovery-example\n* pom.xml\n \n com.alibaba.nacos\n nacos-spring-context\n ${latest.version}\n \n*/\n\n// Refer to document: https://github.com/nacos-group/nacos-examples/blob/master/nacos-spring-example/nacos-spring-discovery-example/src/main/java/com/alibaba/nacos/example/spring\npackage com.alibaba.nacos.example.spring;\n\nimport com.alibaba.nacos.api.annotation.NacosProperties;\nimport com.alibaba.nacos.spring.context.annotation.discovery.EnableNacosDiscovery;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\n@EnableNacosDiscovery(globalProperties = @NacosProperties(serverAddr = "127.0.0.1:8848"))\npublic class NacosConfiguration {\n\n}\n\n// Refer to document: https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-example/nacos-spring-discovery-example/src/main/java/com/alibaba/nacos/example/spring/controller\npackage com.alibaba.nacos.example.spring.controller;\n\nimport com.alibaba.nacos.api.annotation.NacosInjected;\nimport com.alibaba.nacos.api.exception.NacosException;\nimport com.alibaba.nacos.api.naming.NamingService;\nimport com.alibaba.nacos.api.naming.pojo.Instance;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport java.util.List;\n\nimport static org.springframework.web.bind.annotation.RequestMethod.GET;\n\n@Controller\n@RequestMapping("discovery")\npublic class DiscoveryController {\n\n @NacosInjected\n private NamingService namingService;\n\n @RequestMapping(value = "/get", method = GET)\n @ResponseBody\n public List get(@RequestParam String serviceName) throws NacosException {\n return namingService.getAllInstances(serviceName);\n }\n}'}},{key:"getSpringBootCode",value:function(e){return'/* Refer to document: https://github.com/nacos-group/nacos-examples/blob/master/nacos-spring-boot-example/nacos-spring-boot-discovery-example\n* pom.xml\n \n com.alibaba.boot\n nacos-discovery-spring-boot-starter\n ${latest.version}\n \n*/\n/* Refer to document: https://github.com/nacos-group/nacos-examples/blob/master/nacos-spring-boot-example/nacos-spring-boot-discovery-example/src/main/resources\n* application.properties\n nacos.discovery.server-addr=127.0.0.1:8848\n*/\n// Refer to document: https://github.com/nacos-group/nacos-examples/blob/master/nacos-spring-boot-example/nacos-spring-boot-discovery-example/src/main/java/com/alibaba/nacos/example/spring/boot/controller\n\npackage com.alibaba.nacos.example.spring.boot.controller;\n\nimport com.alibaba.nacos.api.annotation.NacosInjected;\nimport com.alibaba.nacos.api.exception.NacosException;\nimport com.alibaba.nacos.api.naming.NamingService;\nimport com.alibaba.nacos.api.naming.pojo.Instance;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport java.util.List;\n\nimport static org.springframework.web.bind.annotation.RequestMethod.GET;\n\n@Controller\n@RequestMapping("discovery")\npublic class DiscoveryController {\n\n @NacosInjected\n private NamingService namingService;\n\n @RequestMapping(value = "/get", method = GET)\n @ResponseBody\n public List get(@RequestParam String serviceName) throws NacosException {\n return namingService.getAllInstances(serviceName);\n }\n}'}},{key:"getSpringCloudCode",value:function(e){return"/* Refer to document: https://github.com/nacos-group/nacos-examples/blob/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example/\n* pom.xml\n \n org.springframework.cloud\n spring-cloud-starter-alibaba-nacos-discovery\n ${latest.version}\n \n*/\n\n// nacos-spring-cloud-provider-example\n\n/* Refer to document: https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example/nacos-spring-cloud-provider-example/src/main/resources\n* application.properties\nserver.port=18080\nspring.application.name=".concat(this.record.name,'\nspring.cloud.nacos.discovery.server-addr=127.0.0.1:8848\n*/\n\n// Refer to document: https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example/nacos-spring-cloud-provider-example/src/main/java/com/alibaba/nacos/example/spring/cloud\npackage com.alibaba.nacos.example.spring.cloud;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author xiaojing\n */\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class NacosProviderApplication {\n\n public static void main(String[] args) {\n SpringApplication.run(NacosProviderApplication.class, args);\n}\n\n @RestController\n class EchoController {\n @RequestMapping(value = "/echo/{string}", method = RequestMethod.GET)\n public String echo(@PathVariable String string) {\n return "Hello Nacos Discovery " + string;\n }\n }\n}\n\n// nacos-spring-cloud-consumer-example\n\n/* Refer to document: https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example/nacos-spring-cloud-consumer-example/src/main/resources\n* application.properties\nspring.application.name=micro-service-oauth2\nspring.cloud.nacos.discovery.server-addr=127.0.0.1:8848\n*/\n\n// Refer to document: https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example/nacos-spring-cloud-consumer-example/src/main/java/com/alibaba/nacos/example/spring/cloud\npackage com.alibaba.nacos.example.spring.cloud;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * @author xiaojing\n */\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class NacosConsumerApplication {\n\n @LoadBalanced\n @Bean\n public RestTemplate restTemplate() {\n return new RestTemplate();\n }\n\n public static void main(String[] args) {\n SpringApplication.run(NacosConsumerApplication.class, args);\n }\n\n @RestController\n public class TestController {\n\n private final RestTemplate restTemplate;\n\n @Autowired\n public TestController(RestTemplate restTemplate) {this.restTemplate = restTemplate;}\n\n @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)\n public String echo(@PathVariable String str) {\n return restTemplate.getForObject("http://service-provider/echo/" + str, String.class);\n }\n }\n}')}},{key:"getNodejsCode",value:function(e){return"TODO"}},{key:"getCppCode",value:function(e){return"TODO"}},{key:"getShellCode",value:function(e){return"TODO"}},{key:"getPythonCode",value:function(e){return"TODO"}},{key:"getCSharpCode",value:function(e){return'/* Refer to document: https://github.com/nacos-group/nacos-sdk-csharp/\nDemo for Basic Nacos Opreation\nApp.csproj\n\n\n \n\n*/\n\nusing Microsoft.Extensions.DependencyInjection;\nusing Nacos.V2;\nusing Nacos.V2.DependencyInjection;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nclass Program\n{\n static async Task Main(string[] args)\n {\n IServiceCollection services = new ServiceCollection();\n\n services.AddNacosV2Naming(x =>\n {\n x.ServerAddresses = new List { "http://localhost:8848/" };\n x.Namespace = "cs-test";\n\n // swich to use http or rpc\n x.NamingUseRpc = true;\n });\n\n IServiceProvider serviceProvider = services.BuildServiceProvider();\n var namingSvc = serviceProvider.GetService();\n\n await namingSvc.RegisterInstance("'.concat(this.record.name,'", "11.11.11.11", 8888, "TEST1");\n\n await namingSvc.RegisterInstance("').concat(this.record.name,'", "2.2.2.2", 9999, "DEFAULT");\n\n Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(await namingSvc.GetAllInstances("').concat(this.record.name,'")));\n\n await namingSvc.DeregisterInstance("').concat(this.record.name,'", "2.2.2.2", 9999, "DEFAULT");\n\n var listener = new EventListener();\n\n await namingSvc.Subscribe("').concat(this.record.name,'", listener);\n }\n\n internal class EventListener : IEventListener\n {\n public Task OnEvent(IEvent @event)\n {\n Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(@event));\n return Task.CompletedTask;\n }\n }\n}\n\n/* Refer to document: https://github.com/nacos-group/nacos-sdk-csharp/\nDemo for ASP.NET Core Integration\nApp.csproj\n\n\n \n\n*/\n\n/* Refer to document: https://github.com/nacos-group/nacos-sdk-csharp/blob/dev/samples/App1/appsettings.json\n* appsettings.json\n{\n "nacos": {\n "ServerAddresses": [ "http://localhost:8848" ],\n "DefaultTimeOut": 15000,\n "Namespace": "cs",\n "ServiceName": "App1",\n "GroupName": "DEFAULT_GROUP",\n "ClusterName": "DEFAULT",\n "Port": 0,\n "Weight": 100,\n "RegisterEnabled": true,\n "InstanceEnabled": true,\n "Ephemeral": true,\n "NamingUseRpc": true,\n "NamingLoadCacheAtStart": ""\n }\n}\n*/\n\n// Refer to document: https://github.com/nacos-group/nacos-sdk-csharp/blob/dev/samples/App1/Startup.cs\nusing Nacos.AspNetCore.V2;\n\npublic class Startup\n{\n public Startup(IConfiguration configuration)\n {\n Configuration = configuration;\n }\n\n public IConfiguration Configuration { get; }\n\n public void ConfigureServices(IServiceCollection services)\n {\n // ....\n services.AddNacosAspNet(Configuration);\n }\n\n public void Configure(IApplicationBuilder app, IWebHostEnvironment env)\n {\n // ....\n }\n}\n ')}},{key:"openDialog",value:function(e){var t=this;this.setState({dialogvisible:!0}),this.record=e,setTimeout(function(){t.getData()})}},{key:"closeDialog",value:function(){this.setState({dialogvisible:!1})}},{key:"createCodeMirror",value:function(e,t){var n=this.refs.codepreview;n&&(n.innerHTML="",this.cm=window.CodeMirror(n,{value:t,mode:e,height:400,width:500,lineNumbers:!0,theme:"xq-light",lint:!0,tabMode:"indent",autoMatchParens:!0,textWrapping:!0,gutters:["CodeMirror-lint-markers"],extraKeys:{F1:function(e){e.setOption("fullScreen",!e.getOption("fullScreen"))},Esc:function(e){e.getOption("fullScreen")&&e.setOption("fullScreen",!1)}}}),this.cm.setSize("auto","490px"))}},{key:"changeTab",value:function(e,t){var n=this;setTimeout(function(){n[e]=!0,n.createCodeMirror("text/javascript",t)})}},{key:"render",value:function(){var e=this.props.locale,e=void 0===e?{}:e;return L.a.createElement("div",null,L.a.createElement(o.a,{title:e.sampleCode,style:{width:"80%"},visible:this.state.dialogvisible,footer:L.a.createElement("div",null),onClose:this.closeDialog.bind(this)},L.a.createElement("div",{style:{height:500}},L.a.createElement(h.a,{tip:e.loading,style:{width:"100%"},visible:this.state.loading},L.a.createElement(m.a,{shape:"text",style:{height:40,paddingBottom:10}},L.a.createElement(g,{title:"Java",key:0,onClick:this.changeTab.bind(this,"commoneditor1",this.defaultCode)}),L.a.createElement(g,{title:"Spring",key:1,onClick:this.changeTab.bind(this,"commoneditor1",this.springCode)}),L.a.createElement(g,{title:"Spring Boot",key:2,onClick:this.changeTab.bind(this,"commoneditor2",this.sprigbootCode)}),L.a.createElement(g,{title:"Spring Cloud",key:21,onClick:this.changeTab.bind(this,"commoneditor21",this.sprigcloudCode)}),L.a.createElement(g,{title:"Node.js",key:3,onClick:this.changeTab.bind(this,"commoneditor3",this.nodejsCode)}),L.a.createElement(g,{title:"C++",key:4,onClick:this.changeTab.bind(this,"commoneditor4",this.cppCode)}),L.a.createElement(g,{title:"Shell",key:5,onClick:this.changeTab.bind(this,"commoneditor5",this.shellCode)}),L.a.createElement(g,{title:"Python",key:6,onClick:this.changeTab.bind(this,"commoneditor6",this.pythonCode)}),L.a.createElement(g,{title:"C#",key:7,onClick:this.changeTab.bind(this,"commoneditor7",this.csharpCode)})),L.a.createElement("div",{ref:"codepreview"})))))}}]),n}(L.a.Component)).displayName="ShowServiceCodeing",f=f))||f,P=t(68),j=(t(738),t(29)),Y=C.a.Item,I=a.a.Row,R=a.a.Col,A=x.a.Column,a=(0,n.a.config)(((f=function(e){Object(d.a)(a,e);var t=Object(c.a)(a);function a(e){var n;return Object(l.a)(this,a),(n=t.call(this,e)).getQueryLater=function(){setTimeout(function(){return n.queryServiceList()})},n.showcode=function(){setTimeout(function(){return n.queryServiceList()})},n.setNowNameSpace=function(e,t){return n.setState({nowNamespaceName:e,nowNamespaceId:t})},n.rowColor=function(e){return{className:e.healthyInstanceCount?"":"row-bg-red"}},n.editServiceDialog=L.a.createRef(),n.showcode=L.a.createRef(),n.state={loading:!1,total:0,pageSize:10,currentPage:1,dataSource:[],search:{serviceName:"",groupName:""},hasIpCount:!("false"===localStorage.getItem("hasIpCount"))},n.field=new i.a(Object(u.a)(n)),n}return Object(s.a)(a,[{key:"openLoading",value:function(){this.setState({loading:!0})}},{key:"closeLoading",value:function(){this.setState({loading:!1})}},{key:"openEditServiceDialog",value:function(){try{this.editServiceDialog.current.getInstance().show(this.state.service)}catch(e){}}},{key:"queryServiceList",value:function(){var n=this,e=this.state,t=e.currentPage,a=e.pageSize,r=e.search,o=e.withInstances,o=void 0!==o&&o,e=e.hasIpCount,e=["hasIpCount=".concat(e),"withInstances=".concat(o),"pageNo=".concat(t),"pageSize=".concat(a),"serviceNameParam=".concat(r.serviceName),"groupNameParam=".concat(r.groupName)];this.openLoading(),Object(p.b)({url:"v1/ns/catalog/services?".concat(e.join("&")),success:function(){var e=0o&&h.a.createElement(u.a,{className:"users-pagination",current:i,total:n.totalCount,pageSize:o,onChange:function(e){return t.setState({pageNo:e},function(){return t.getUsers()})}}),h.a.createElement(S,{visible:l,onOk:function(e){return Object(m.c)(e).then(function(e){return t.setState({pageNo:1},function(){return t.getUsers()}),e})},onCancel:function(){return t.colseCreateUser()}}),h.a.createElement(E.a,{visible:s,username:e,onOk:function(e){return Object(m.k)(e).then(function(e){return t.getUsers(),e})},onCancel:function(){return t.setState({passwordResetUser:void 0,passwordResetUserVisible:!1})}}))}}]),n}(h.a.Component)).displayName="UserManagement",n=o))||n)||n;t.a=r},function(e,t,n){"use strict";n(63);var a=n(46),s=n.n(a),a=(n(34),n(19)),u=n.n(a),d=n(31),a=(n(62),n(20)),c=n.n(a),a=(n(32),n(18)),f=n.n(a),i=n(14),l=n(15),p=n(17),h=n(16),a=(n(25),n(7)),a=n.n(a),r=n(0),m=n.n(r),r=n(35),g=n(45),o=n(81),y=n(48),v=(n(49),n(26)),_=n.n(v),b=n(23),v=(n(58),n(28)),w=n.n(v),v=(n(43),n(9)),M=n.n(v),k=M.a.Item,S=w.a.Option,E={labelCol:{fixedSpan:4},wrapperCol:{span:19}},x=Object(r.b)(function(e){return{namespaces:e.namespace.namespaces}},{getNamespaces:o.b,searchRoles:g.l})(v=(0,a.a.config)(((v=function(e){Object(p.a)(o,e);var r=Object(h.a)(o);function o(){var t;Object(i.a)(this,o);for(var e=arguments.length,n=new Array(e),a=0;ai&&m.a.createElement(s.a,{className:"users-pagination",current:l,total:t.totalCount,pageSize:i,onChange:function(e){return a.setState({pageNo:e},function(){return a.getPermissions()})}}),m.a.createElement(x,{visible:n,onOk:function(e){return Object(g.a)(e).then(function(e){return a.setState({pageNo:1},function(){return a.getPermissions()}),e})},onCancel:function(){return a.colseCreatePermission()}}))}}]),n}(m.a.Component)).displayName="PermissionsManagement",n=v))||n)||n);t.a=r},function(e,t,n){"use strict";n(63);var a=n(46),s=n.n(a),a=(n(34),n(19)),u=n.n(a),a=(n(62),n(20)),d=n.n(a),a=(n(32),n(18)),c=n.n(a),i=n(14),l=n(15),f=n(17),p=n(16),a=(n(25),n(7)),a=n.n(a),r=n(0),h=n.n(r),r=n(35),m=n(45),g=n(48),o=(n(58),n(28)),y=n.n(o),o=(n(42),n(11)),v=n.n(o),o=(n(49),n(26)),_=n.n(o),b=n(23),o=(n(43),n(9)),w=n.n(o),M=w.a.Item,k={labelCol:{fixedSpan:4},wrapperCol:{span:19}},S=Object(r.b)(function(e){return{users:e.authority.users}},{searchUsers:m.m})(o=(0,a.a.config)(((o=function(e){Object(f.a)(o,e);var r=Object(p.a)(o);function o(){var t;Object(i.a)(this,o);for(var e=arguments.length,n=new Array(e),a=0;ao&&h.a.createElement(s.a,{className:"users-pagination",current:i,total:t.totalCount,pageSize:o,onChange:function(e){return a.setState({pageNo:e},function(){return a.getRoles()})}}),h.a.createElement(S,{visible:l,onOk:function(e){return Object(m.b)(e).then(function(e){return a.getRoles(),e})},onCancel:function(){return a.colseCreateRole()}}))}}]),n}(h.a.Component)).displayName="RolesManagement",n=o))||n)||n);t.a=r},function(e,t,n){"use strict";n(41);function o(e){var t=void 0===(t=localStorage.token)?"{}":t,t=(Object(y.c)(t)&&JSON.parse(t)||{}).globalAdmin;return["naming"===e?void 0:v,{key:"serviceManagementVirtual",children:[{key:"serviceManagement",url:"/serviceManagement"},{key:"subscriberList",url:"/subscriberList"}]},t?_:void 0,{key:"namespace",url:"/namespace"},{key:"clusterManagementVirtual",children:[{key:"clusterManagement",url:"/clusterManagement"}]}].filter(function(e){return e})}var a=n(24),i=n.n(a),r=n(14),l=n(15),s=n(17),u=n(16),a=(n(25),n(7)),a=n.n(a),d=n(21),c=(n(79),n(50)),f=n.n(c),c=n(0),p=n.n(c),c=n(38),h=n(35),m=n(134),g=n(100),y=n(47),v={key:"configurationManagementVirtual",children:[{key:"configurationManagement",url:"/configurationManagement"},{key:"historyRollback",url:"/historyRollback"},{key:"listeningToQuery",url:"/listeningToQuery"}]},_={key:"authorityControl",children:[{key:"userList",url:"/userManagement"},{key:"roleManagement",url:"/rolesManagement"},{key:"privilegeManagement",url:"/permissionsManagement"}]},b=f.a.SubMenu,w=f.a.Item,c=(n=Object(h.b)(function(e){return Object(d.a)(Object(d.a)({},e.locale),e.base)},{getState:g.b}),h=a.a.config,Object(c.g)(a=n(a=h(((g=function(e){Object(s.a)(n,e);var t=Object(u.a)(n);function n(){return Object(r.a)(this,n),t.apply(this,arguments)}return Object(l.a)(n,[{key:"componentDidMount",value:function(){this.props.getState()}},{key:"goBack",value:function(){this.props.history.goBack()}},{key:"navTo",value:function(e){var t=this.props.location.search,t=new URLSearchParams(t);t.set("namespace",window.nownamespace),t.set("namespaceShowName",window.namespaceShowName),this.props.history.push([e,"?",t.toString()].join(""))}},{key:"isCurrentPath",value:function(e){return e===this.props.location.pathname?"current-path next-selected":void 0}},{key:"defaultOpenKeys",value:function(){for(var t=this,e=o(this.props.functionMode),n=0,a=e.length;nthis.state.pageSize&&M.a.createElement("div",{style:{marginTop:10,textAlign:"right"}},M.a.createElement(g.a,{current:this.state.pageNo,total:a,pageSize:this.state.pageSize,onChange:function(e){return t.setState({pageNo:e},function(){return t.querySubscriberList()})}}))))}}]),a}(M.a.Component)).displayName="SubscriberList",c=n))||c)||c;t.a=f},function(e,t,n){"use strict";n(51);var a=n(33),c=n.n(a),a=(n(63),n(46)),f=n.n(a),a=(n(175),n(73)),p=n.n(a),a=(n(42),n(11)),h=n.n(a),a=(n(32),n(18)),m=n.n(a),a=(n(34),n(19)),r=n.n(a),a=(n(49),n(26)),o=n.n(a),i=n(14),l=n(15),s=n(23),u=n(17),d=n(16),a=(n(25),n(7)),a=n.n(a),g=(n(403),n(115)),y=n.n(g),g=(n(62),n(20)),v=n.n(g),g=(n(65),n(39)),g=n.n(g),_=(n(43),n(9)),b=n.n(_),_=n(0),w=n.n(_),M=n(1),k=n(48),_=n(136),S=n.n(_),E=n(68),x=(n(741),b.a.Item),C=g.a.Row,L=g.a.Col,T=v.a.Column,D=y.a.Panel,g=(0,a.a.config)(((_=function(e){Object(u.a)(a,e);var t=Object(d.a)(a);function a(e){var n;return Object(i.a)(this,a),(n=t.call(this,e)).getQueryLater=function(){setTimeout(function(){return n.queryClusterStateList()})},n.setNowNameSpace=function(e,t){return n.setState({nowNamespaceName:e,nowNamespaceId:t})},n.rowColor=function(e){return{className:(e.voteFor,"")}},n.state={loading:!1,total:0,pageSize:10,currentPage:1,keyword:"",dataSource:[]},n.field=new o.a(Object(s.a)(n)),n}return Object(l.a)(a,[{key:"openLoading",value:function(){this.setState({loading:!0})}},{key:"closeLoading",value:function(){this.setState({loading:!1})}},{key:"openEditServiceDialog",value:function(){try{this.editServiceDialog.current.getInstance().show(this.state.service)}catch(e){}}},{key:"queryClusterStateList",value:function(){var n=this,e=this.state,t=e.currentPage,a=e.pageSize,r=e.keyword,e=e.withInstances,e=["withInstances=".concat(void 0!==e&&e),"pageNo=".concat(t),"pageSize=".concat(a),"keyword=".concat(r)];Object(M.b)({url:"v1/core/cluster/nodes?".concat(e.join("&")),beforeSend:function(){return n.openLoading()},success:function(){var e=0this.state.pageSize&&w.a.createElement("div",{style:{marginTop:10,textAlign:"right"}},w.a.createElement(f.a,{current:this.state.currentPage,total:this.state.total,pageSize:this.state.pageSize,onChange:function(e){return t.setState({currentPage:e},function(){return t.queryClusterStateList()})}}))))}}]),a}(w.a.Component)).displayName="ClusterNodeList",n=_))||n;t.a=g},function(e,t,F){"use strict";F.r(t),function(e){F(51);var t=F(33),a=F.n(t),t=(F(25),F(7)),r=F.n(t),o=F(14),i=F(15),l=F(17),s=F(16),n=F(21),t=F(0),u=F.n(t),t=F(22),t=F.n(t),d=F(117),c=F(411),f=F(423),p=F(35),h=F(38),m=F(80),g=(F(458),F(432)),y=F(29),v=F(433),_=F(426),b=F(434),w=F(435),M=F(427),k=F(436),S=F(437),E=F(438),x=F(439),C=F(440),L=F(424),T=F(428),D=F(425),O=F(441),N=F(442),P=F(429),j=F(430),I=F(431),R=F(421),Y=F(422),A=F(103),e=(F(744),F(745),F(746),e.hot,localStorage.getItem(y.f)||localStorage.setItem(y.f,"zh-CN"===navigator.language?"zh-CN":"en-US"),Object(d.b)(Object(n.a)(Object(n.a)({},Y.a),{},{routing:c.routerReducer}))),Y=Object(d.d)(e,Object(d.c)(Object(d.a)(f.a),window[y.i]?window[y.i]():function(e){return e})),H=[{path:"/",exact:!0,render:function(){return u.a.createElement(h.a,{to:"/welcome"})}},{path:"/welcome",component:R.a},{path:"/namespace",component:_.a},{path:"/newconfig",component:b.a},{path:"/configsync",component:w.a},{path:"/configdetail",component:M.a},{path:"/configeditor",component:k.a},{path:"/historyDetail",component:S.a},{path:"/configRollback",component:E.a},{path:"/historyRollback",component:x.a},{path:"/listeningToQuery",component:C.a},{path:"/configurationManagement",component:L.a},{path:"/serviceManagement",component:T.a},{path:"/serviceDetail",component:D.a},{path:"/subscriberList",component:O.a},{path:"/clusterManagement",component:N.a},{path:"/userManagement",component:P.a},{path:"/rolesManagement",component:I.a},{path:"/permissionsManagement",component:j.a}],e=Object(p.b)(function(e){return Object(n.a)({},e.locale)},{changeLanguage:A.a})(c=function(e){Object(l.a)(n,e);var t=Object(s.a)(n);function n(e){return Object(o.a)(this,n),(e=t.call(this,e)).state={shownotice:"none",noticecontent:"",nacosLoading:{}},e}return Object(i.a)(n,[{key:"componentDidMount",value:function(){var e=localStorage.getItem(y.f);this.props.changeLanguage(e)}},{key:"router",get:function(){return u.a.createElement(m.a,null,u.a.createElement(h.d,null,u.a.createElement(h.b,{path:"/login",component:v.a}),u.a.createElement(g.a,null,H.map(function(e){return u.a.createElement(h.b,Object.assign({key:e.path},e))}))))}},{key:"render",value:function(){var e=this.props.locale;return u.a.createElement(a.a,Object.assign({className:"nacos-loading",shape:"flower",tip:"loading...",visible:!1,fullScreen:!0},this.state.nacosLoading),u.a.createElement(r.a,{locale:e},this.router))}}]),n}(u.a.Component))||c;t.a.render(u.a.createElement(p.a,{store:Y},u.a.createElement(e,null)),document.getElementById("root"))}.call(this,F(444)(e))},function(e,t){e.exports=function(e){var t;return e.webpackPolyfill||((t=Object.create(e)).children||(t.children=[]),Object.defineProperty(t,"loaded",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,"id",{enumerable:!0,get:function(){return t.i}}),Object.defineProperty(t,"exports",{enumerable:!0}),t.webpackPolyfill=1),t}},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(I,e,t){"use strict"; +var t;e.defineLocale("zh-tw",{months:"äø€ęœˆ_äŗŒęœˆ_äø‰ęœˆ_å››ęœˆ_äŗ”ęœˆ_å…­ęœˆ_äøƒęœˆ_å…«ęœˆ_ä¹ęœˆ_åęœˆ_十äø€ęœˆ_十äŗŒęœˆ".split("_"),monthsShort:"1꜈_2꜈_3꜈_4꜈_5꜈_6꜈_7꜈_8꜈_9꜈_10꜈_11꜈_12꜈".split("_"),weekdays:"ę˜ŸęœŸę—„_ꘟꜟäø€_ꘟꜟäŗŒ_ꘟꜟäø‰_ę˜ŸęœŸå››_ꘟꜟäŗ”_ę˜ŸęœŸå…­".split("_"),weekdaysShort:"週ꗄ_週äø€_週äŗŒ_週äø‰_週四_週äŗ”_週六".split("_"),weekdaysMin:"ę—„_äø€_äŗŒ_äø‰_四_äŗ”_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY幓M꜈Dę—„",LLL:"YYYY幓M꜈Dę—„ HH:mm",LLLL:"YYYY幓M꜈Dę—„dddd HH:mm",l:"YYYY/M/D",ll:"YYYY幓M꜈Dę—„",lll:"YYYY幓M꜈Dę—„ HH:mm",llll:"YYYY幓M꜈Dę—„dddd HH:mm"},meridiemParse:/凌ę™Ø|ę—©äøŠ|äøŠåˆ|äø­åˆ|äø‹åˆ|ꙚäøŠ/,meridiemHour:function(e,t){if(e===12)e=0;if(t==="凌ę™Ø"||t==="ę—©äøŠ"||t==="äøŠåˆ")return e;else if(t==="äø­åˆ")return e>=11?e:e+12;else if(t==="äø‹åˆ"||t==="ꙚäøŠ")return e+12},meridiem:function(e,t,n){var a=e*100+t;if(a<600)return"凌ę™Ø";else if(a<900)return"ę—©äøŠ";else if(a<1130)return"äøŠåˆ";else if(a<1230)return"äø­åˆ";else if(a<1800)return"äø‹åˆ";else return"ꙚäøŠ"},calendar:{sameDay:"[今天] LT",nextDay:"[ę˜Žå¤©] LT",nextWeek:"[äø‹]dddd LT",lastDay:"[ę˜Ø天] LT",lastWeek:"[äøŠ]dddd LT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(ę—„|꜈|週)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"ę—„";case"M":return e+"꜈";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"å¹¾ē§’",ss:"%d ē§’",m:"1 分鐘",mm:"%d 分鐘",h:"1 å°ę™‚",hh:"%d å°ę™‚",d:"1 天",dd:"%d 天",M:"1 å€‹ęœˆ",MM:"%d å€‹ęœˆ",y:"1 幓",yy:"%d 幓"}})}(n(9))},function(e,t,n){"use strict";t.__esModule=!0;var u=p(n(2)),a=p(n(4)),r=p(n(5)),o=p(n(6)),i=n(0),d=p(i),l=p(n(3)),c=p(n(13)),s=p(n(8)),f=n(11);function p(e){return e&&e.__esModule?e:{default:e}}h=i.Component,(0,o.default)(m,h),m.prototype.render=function(){var e,t=this.props,n=t.prefix,a=t.type,r=t.size,o=t.className,i=t.rtl,l=t.style,t=t.children,s=f.obj.pickOthers((0,u.default)({},m.propTypes),this.props),n=(0,c.default)(((e={})[n+"icon"]=!0,e[n+"icon-"+a]=!!a,e[""+n+r]=!!r&&"string"==typeof r,e[o]=!!o,e)),o=(i&&-1!==["arrow-left","arrow-right","arrow-double-left","arrow-double-right","switch","sorting","descending","ascending"].indexOf(a)&&(s.dir="rtl"),"number"==typeof r?{width:r,height:r,lineHeight:r+"px",fontSize:r}:{});return d.default.createElement("i",(0,u.default)({},s,{style:(0,u.default)({},o,l),className:n}),t)},i=n=m,n.propTypes=(0,u.default)({},s.default.propTypes,{type:l.default.string,children:l.default.node,size:l.default.oneOfType([l.default.oneOf(["xxs","xs","small","medium","large","xl","xxl","xxxl","inherit"]),l.default.number]),className:l.default.string,style:l.default.object}),n.defaultProps={prefix:"next-",size:"medium"},n._typeMark="icon";var h,o=i;function m(){return(0,a.default)(this,m),(0,r.default)(this,h.apply(this,arguments))}o.displayName="Icon",t.default=o,e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var g=s(n(2)),y=s(n(12)),a=s(n(38)),r=s(n(4)),o=s(n(5)),i=s(n(6)),v=n(0),_=s(v),l=s(n(3)),b=n(157),w=s(n(525));function s(e){return e&&e.__esModule?e:{default:e}}function u(){}function M(e){return _.default.Children.toArray(e.children)[0]||null}d=v.Component,(0,i.default)(c,d),c.prototype.normalizeNames=function(e){return"string"==typeof e?{appear:e+"-appear",appearActive:e+"-appear-active",enter:e+"-enter",enterActive:e+"-enter-active",leave:e+"-leave",leaveActive:e+"-leave-active"}:"object"===(void 0===e?"undefined":(0,a.default)(e))?{appear:e.appear,appearActive:e.appear+"-active",enter:""+e.enter,enterActive:e.enter+"-active",leave:""+e.leave,leaveActive:e.leave+"-active"}:void 0},c.prototype.render=function(){var t=this,e=this.props,n=e.animation,a=e.children,r=e.animationAppear,o=e.singleMode,i=e.component,l=e.beforeAppear,s=e.onAppear,u=e.afterAppear,d=e.beforeEnter,c=e.onEnter,f=e.afterEnter,p=e.beforeLeave,h=e.onLeave,m=e.afterLeave,e=(0,y.default)(e,["animation","children","animationAppear","singleMode","component","beforeAppear","onAppear","afterAppear","beforeEnter","onEnter","afterEnter","beforeLeave","onLeave","afterLeave"]),a=v.Children.map(a,function(e){return _.default.createElement(w.default,{key:e.key,names:t.normalizeNames(n),onAppear:l,onAppearing:s,onAppeared:u,onEnter:d,onEntering:c,onEntered:f,onExit:p,onExiting:h,onExited:m},e)});return _.default.createElement(b.TransitionGroup,(0,g.default)({appear:r,component:o?M:i},e),a)},i=n=c,n.propTypes={animation:l.default.oneOfType([l.default.string,l.default.object]),animationAppear:l.default.bool,component:l.default.any,singleMode:l.default.bool,children:l.default.oneOfType([l.default.element,l.default.arrayOf(l.default.element)]),beforeAppear:l.default.func,onAppear:l.default.func,afterAppear:l.default.func,beforeEnter:l.default.func,onEnter:l.default.func,afterEnter:l.default.func,beforeLeave:l.default.func,onLeave:l.default.func,afterLeave:l.default.func},n.defaultProps={animationAppear:!0,component:"div",singleMode:!0,beforeAppear:u,onAppear:u,afterAppear:u,beforeEnter:u,onEnter:u,afterEnter:u,beforeLeave:u,onLeave:u,afterLeave:u};var d,l=i;function c(){return(0,r.default)(this,c),(0,o.default)(this,d.apply(this,arguments))}l.displayName="Animate",t.default=l,e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0,t.default=t.EXITING=t.ENTERED=t.ENTERING=t.EXITED=t.UNMOUNTED=void 0;var a=function(e){{if(e&&e.__esModule)return e;var t,n={};if(null!=e)for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&((t=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,a):{}).get||t.set?Object.defineProperty(n,a,t):n[a]=e[a]);return n.default=e,n}}(n(3)),o=l(n(0)),i=l(n(23)),r=n(30);n(346);function l(e){return e&&e.__esModule?e:{default:e}}var s="unmounted",u=(t.UNMOUNTED=s,"exited"),d=(t.EXITED=u,"entering"),c=(t.ENTERING=d,"entered"),f=(t.ENTERED=c,"exiting"),n=(t.EXITING=f,function(r){var e;function t(e,t){var n,a=r.call(this,e,t)||this,t=t.transitionGroup,t=t&&!t.isMounting?e.enter:e.appear;return a.appearStatus=null,e.in?t?(n=u,a.appearStatus=d):n=c:n=e.unmountOnExit||e.mountOnEnter?s:u,a.state={status:n},a.nextCallback=null,a}e=r,(n=t).prototype=Object.create(e.prototype),(n.prototype.constructor=n).__proto__=e;var n=t.prototype;return n.getChildContext=function(){return{transitionGroup:null}},t.getDerivedStateFromProps=function(e,t){return e.in&&t.status===s?{status:u}:null},n.componentDidMount=function(){this.updateStatus(!0,this.appearStatus)},n.componentDidUpdate=function(e){var t=null;e!==this.props&&(e=this.state.status,this.props.in?e!==d&&e!==c&&(t=d):e!==d&&e!==c||(t=f)),this.updateStatus(!1,t)},n.componentWillUnmount=function(){this.cancelNextCallback()},n.getTimeouts=function(){var e,t,n=this.props.timeout,a=e=t=n;return null!=n&&"number"!=typeof n&&(a=n.exit,e=n.enter,t=void 0!==n.appear?n.appear:e),{exit:a,enter:e,appear:t}},n.updateStatus=function(e,t){var n;void 0===e&&(e=!1),null!==t?(this.cancelNextCallback(),n=i.default.findDOMNode(this),t===d?this.performEnter(n,e):this.performExit(n)):this.props.unmountOnExit&&this.state.status===u&&this.setState({status:s})},n.performEnter=function(e,t){var n=this,a=this.props.enter,r=this.context.transitionGroup?this.context.transitionGroup.isMounting:t,o=this.getTimeouts(),i=r?o.appear:o.enter;t||a?(this.props.onEnter(e,r),this.safeSetState({status:d},function(){n.props.onEntering(e,r),n.onTransitionEnd(e,i,function(){n.safeSetState({status:c},function(){n.props.onEntered(e,r)})})})):this.safeSetState({status:c},function(){n.props.onEntered(e)})},n.performExit=function(e){var t=this,n=this.props.exit,a=this.getTimeouts();n?(this.props.onExit(e),this.safeSetState({status:f},function(){t.props.onExiting(e),t.onTransitionEnd(e,a.exit,function(){t.safeSetState({status:u},function(){t.props.onExited(e)})})})):this.safeSetState({status:u},function(){t.props.onExited(e)})},n.cancelNextCallback=function(){null!==this.nextCallback&&(this.nextCallback.cancel(),this.nextCallback=null)},n.safeSetState=function(e,t){t=this.setNextCallback(t),this.setState(e,t)},n.setNextCallback=function(t){var n=this,a=!0;return this.nextCallback=function(e){a&&(a=!1,n.nextCallback=null,t(e))},this.nextCallback.cancel=function(){a=!1},this.nextCallback},n.onTransitionEnd=function(e,t,n){this.setNextCallback(n);n=null==t&&!this.props.addEndListener;!e||n?setTimeout(this.nextCallback,0):(this.props.addEndListener&&this.props.addEndListener(e,this.nextCallback),null!=t&&setTimeout(this.nextCallback,t))},n.render=function(){var e=this.state.status;if(e===s)return null;var t=this.props,n=t.children,t=function(e,t){if(null==e)return{};for(var n,a={},r=Object.keys(e),o=0;o 16.8.0"),null;function t(e){j.current=e,ne({})}function a(e,t){N(te,null),"function"==typeof S&&S(e,t)}function n(e){27===e.keyCode&&Y&&!P.current.size&&a("esc",e)}function r(){j.current||(t(!0),he.dom.setStyle(D.current,"display","none"),me.default.unlock(document.body,ee.current),s&&O.current&&(O.current.focus(),O.current=null),I())}var o=e.prefix,o=void 0===o?"next-":o,i=e.afterClose,I=void 0===i?ye:i,i=e.hasMask,l=void 0===i||i,i=e.autoFocus,s=void 0!==i&&i,i=e.className,R=e.title,A=e.children,H=e.footer,F=e.footerAlign,z=e.footerActions,u=e.onOk,u=void 0===u?ye:u,d=e.onCancel,W=e.okProps,V=e.cancelProps,c=e.locale,c=void 0===c?pe.default.Dialog:c,B=e.rtl,f=e.visible,p=e.closeMode,p=void 0===p?["close","esc"]:p,U=e.closeIcon,h=e.animation,h=void 0===h?{in:"fadeInUp",out:"fadeOutUp"}:h,m=e.cache,K=e.wrapperStyle,g=e.popupContainer,y=void 0===g?document.body:g,g=e.dialogRender,v=e.centered,_=e.top,_=void 0===_?v?40:100:_,b=e.bottom,b=void 0===b?40:b,w=e.width,w=void 0===w?520:w,G=e.height,M=e.isFullScreen,k=e.overflowScroll,M=void 0===k?!M:k,k=e.minMargin,S=e.onClose,q=e.style,E=(0,ie.default)(e,["prefix","afterClose","hasMask","autoFocus","className","title","children","footer","footerAlign","footerActions","onOk","onCancel","okProps","cancelProps","locale","rtl","visible","closeMode","closeIcon","animation","cache","wrapperStyle","popupContainer","dialogRender","centered","top","bottom","width","height","isFullScreen","overflowScroll","minMargin","onClose","style"]),x=("isFullScreen"in e&&he.log.deprecated("isFullScreen","overflowScroll","Dialog v2"),"minMargin"in e&&he.log.deprecated("minMargin","top/bottom","Dialog v2"),(0,le.useState)(f||!1)),$=x[0],J=x[1],x=(0,le.useState)(f),C=x[0],X=x[1],Q="string"==typeof y?function(){return document.getElementById(y)}:"function"!=typeof y?function(){return y}:y,x=(0,le.useState)(Q()),L=x[0],Z=x[1],T=(0,le.useRef)(null),D=(0,le.useRef)(null),O=(0,le.useRef)(null),ee=(0,le.useRef)(null),te=(0,le.useState)((0,he.guid)())[0],x=(0,le.useContext)(ge),N=x.setVisibleOverlayToParent,x=(0,ie.default)(x,["setVisibleOverlayToParent"]),P=(0,le.useRef)(new Map),j=(0,le.useRef)(!1),ne=(0,le.useState)()[1],Y=!1,ae=!1,re=!1;(Array.isArray(p)?p:[p]).forEach(function(e){switch(e){case"esc":Y=!0;break;case"mask":ae=!0;break;case"close":re=!0}}),(0,le.useEffect)(function(){"visible"in e&&X(f)},[f]),(0,le.useEffect)(function(){var e;C&&l&&(e={overflow:"hidden"},he.dom.hasScroll(document.body)&&he.dom.scrollbar().width&&(e.paddingRight=he.dom.getStyle(document.body,"paddingRight")+he.dom.scrollbar().width+"px"),ee.current=me.default.lock(document.body,e))},[C&&l]),(0,le.useEffect)(function(){if(C&&Y)return document.body.addEventListener("keydown",n,!1),function(){document.body.removeEventListener("keydown",n,!1)}},[C&&Y]),(0,le.useEffect)(function(){!$&&C&&J(!0)},[C]),(0,le.useEffect)(function(){L||setTimeout(function(){Z(Q())})},[L]);if((0,le.useEffect)(function(){return function(){r()}},[]),!1===$||!L)return null;if(!C&&!m&&j.current)return null;m=(0,de.default)(((p={})[o+"overlay-wrapper"]=!0,p.opened=C,p)),i=(0,de.default)(((p={})[o+"dialog-v2"]=!0,p[i]=!!i,p)),p={},k=void(v?_||b||!k?(_&&(p.marginTop=_),b&&(p.marginBottom=b)):(p.marginTop=k,p.marginBottom=k):(_&&(p.top=_),b&&(p.paddingBottom=b))),M&&(k="calc(100vh - "+(_+b)+"px)"),M={appear:300,enter:300,exit:250},_=se.default.createElement(fe.default.OverlayAnimate,{visible:C,animation:h,timeout:M,onEnter:function(){t(!1),he.dom.setStyle(D.current,"display","")},onEntered:function(){var e;s&&T.current&&T.current.bodyNode&&(0<(e=he.focus.getFocusNodeList(T.current.bodyNode)).length&&e[0]&&(O.current=document.activeElement,e[0].focus())),N(te,D.current)},onExited:r},se.default.createElement(ce.default,(0,oe.default)({},E,{style:v?(0,oe.default)({},p,q):q,v2:!0,ref:T,prefix:o,className:i,title:R,footer:H,footerAlign:F,footerActions:z,onOk:C?u:ye,onCancel:C?function(e){"function"==typeof d?d(e):a("cancelBtn",e)}:ye,okProps:W,cancelProps:V,locale:c,closeable:re,rtl:B,onClose:function(){for(var e=arguments.length,t=Array(e),n=0;n>6]+d[128|63&s]:s<55296||57344<=s?i+=d[224|s>>12]+d[128|s>>6&63]+d[128|63&s]:(l+=1,s=65536+((1023&s)<<10|1023&o.charCodeAt(l)),i+=d[240|s>>18]+d[128|s>>12&63]+d[128|s>>6&63]+d[128|63&s])}return i},isBuffer:function(e){return!(!e||"object"!=typeof e)&&!!(e.constructor&&e.constructor.isBuffer&&e.constructor.isBuffer(e))},isRegExp:function(e){return"[object RegExp]"===Object.prototype.toString.call(e)},maybeMap:function(e,t){if(m(e)){for(var n=[],a=0;athis.popupNode.offsetWidth&&p(this.popupNode,"width",l.offsetWidth+"px"),"outside"!==a||"hoz"===r&&1===n||(p(this.popupNode,"height",u.offsetHeight+"px"),this.popupNode.firstElementChild&&p(this.popupNode.firstElementChild,"overflow-y","auto")),this.popupProps);d.onOpen&&d.onOpen()}catch(e){return null}},S.prototype.handlePopupClose=function(){var e=this.props.root.popupNodes,t=e.indexOf(this.popupNode),e=(-1t?r[t+1]:r[0])}),n[a]||(o=r[0]),i.onSort(a,o)},i.keydownHandler=function(e){e.preventDefault(),e.stopPropagation(),e.keyCode===l.KEYCODE.ENTER&&i.handleClick()},i.onSort=function(e,t){var n={};n[e]=t,i.props.onSort(e,t,n)},(0,o.default)(i,e)}i.displayName="Sort",t.default=i,e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0,t.default=void 0;var r=c(n(2)),a=c(n(4)),o=c(n(5)),i=c(n(6)),l=c(n(0)),s=c(n(3)),u=c(n(13)),d=c(n(389));function c(e){return e&&e.__esModule?e:{default:e}}f=l.default.Component,(0,i.default)(p,f),p.prototype.render=function(){var e=this.props,t=e.className,n=e.record,e=e.primaryKey,a=this.context.selectedRowKeys,n=(0,u.default)(((a={selected:-1 "+this.message+" (line "+this.parsedLine+": '"+this.snippet+"')":" "+this.message},o}(Error);e.exports=n},function(e,t){var i={}.hasOwnProperty,n=function(e){var t,n=o,a=e;for(t in a)i.call(a,t)&&(n[t]=a[t]);function r(){this.constructor=n}function o(e,t,n){this.message=e,this.parsedLine=t,this.snippet=n}return r.prototype=a.prototype,n.prototype=new r,n.__super__=a.prototype,o.prototype.toString=function(){return null!=this.parsedLine&&null!=this.snippet?" "+this.message+" (line "+this.parsedLine+": '"+this.snippet+"')":" "+this.message},o}(Error);e.exports=n},function(e,t,n){"use strict";t.__esModule=!0,t.default=void 0;var y=m(n(2)),a=m(n(4)),r=m(n(5)),o=m(n(6)),i=n(0),v=m(i),l=n(23),s=m(n(3)),_=m(n(13)),u=n(11),f=m(n(50)),d=m(n(62)),b=m(n(10)),c=m(n(44)),p=m(n(675)),h=m(n(400)),w=n(177);function m(e){return e&&e.__esModule?e:{default:e}}var M=d.default.Popup,g=f.default.Item,k=f.default.Group,n=u.func.noop,S=u.func.bindCtx,E=u.func.makeChain;x=v.default.Component,(0,o.default)(C,x),C.prototype.componentDidMount=function(){var e=this;setTimeout(function(){return e.syncWidth()},0),u.events.on(window,"resize",this.handleResize)},C.prototype.componentDidUpdate=function(e,t){e.label===this.props.label&&t.value===this.state.value||this.syncWidth()},C.prototype.componentWillUnmount=function(){u.events.off(window,"resize",this.handleResize),clearTimeout(this.resizeTimeout)},C.prototype.syncWidth=function(){var e=this,t=this.props,n=t.popupStyle,t=t.popupProps;n&&"width"in n||t&&t.style&&"width"in t.style||(n=u.dom.getStyle(this.selectDOM,"width"))&&this.width!==n&&(this.width=n,this.popupRef&&this.shouldAutoWidth()&&setTimeout(function(){e.popupRef&&u.dom.setStyle(e.popupRef,"width",e.width)},0))},C.prototype.handleResize=function(){var e=this;clearTimeout(this.resizeTimeout),this.state.visible&&(this.resizeTimeout=setTimeout(function(){e.syncWidth()},200))},C.prototype.setDataSource=function(e){var t=e.dataSource,e=e.children;return i.Children.count(e)?this.dataStore.updateByDS(e,!0):Array.isArray(t)?this.dataStore.updateByDS(t,!1):[]},C.prototype.setVisible=function(e,t){this.props.disabled&&e||this.state.visible===e||("visible"in this.props||this.setState({visible:e}),this.props.onVisibleChange(e,t))},C.prototype.setFirstHightLightKeyForMenu=function(e){var t=this.state.highlightKey;this.props.autoHighlightFirstItem&&(this.dataStore.getMenuDS().length&&this.dataStore.getEnableDS().length&&(!t||e)&&(t=""+this.dataStore.getEnableDS()[0].value,this.setState({highlightKey:t}),this.props.onToggleHighlightItem(t,"autoFirstItem")),e&&!this.dataStore.getEnableDS().length&&(this.setState({highlightKey:null}),this.props.onToggleHighlightItem(null,"highlightKeyToNull")))},C.prototype.handleChange=function(e){var t;"value"in this.props||this.setState({value:e});for(var n=arguments.length,a=Array(1e.slidesToShow&&(n=e.slideWidth*e.slidesToShow*-1,o=e.slideHeight*e.slidesToShow*-1),e.slideCount%e.slidesToScroll!=0&&(t=e.slideIndex+e.slidesToScroll>e.slideCount&&e.slideCount>e.slidesToShow,(t=e.rtl?(e.slideIndex>=e.slideCount?e.slideCount-e.slideIndex:e.slideIndex)+e.slidesToScroll>e.slideCount&&e.slideCount>e.slidesToShow:t)&&(o=e.slideIndex>e.slideCount?(n=(e.slidesToShow-(e.slideIndex-e.slideCount))*e.slideWidth*-1,(e.slidesToShow-(e.slideIndex-e.slideCount))*e.slideHeight*-1):(n=e.slideCount%e.slidesToScroll*e.slideWidth*-1,e.slideCount%e.slidesToScroll*e.slideHeight*-1)))):e.slideCount%e.slidesToScroll!=0&&e.slideIndex+e.slidesToScroll>e.slideCount&&e.slideCount>e.slidesToShow&&(n=(e.slidesToShow-e.slideCount%e.slidesToScroll)*e.slideWidth),e.centerMode&&(e.infinite?n+=e.slideWidth*Math.floor(e.slidesToShow/2):n=e.slideWidth*Math.floor(e.slidesToShow/2)),a=e.vertical?e.slideIndex*e.slideHeight*-1+o:e.slideIndex*e.slideWidth*-1+n,!0===e.variableWidth&&(t=void 0,a=(r=e.slideCount<=e.slidesToShow||!1===e.infinite?i.default.findDOMNode(e.trackRef).childNodes[e.slideIndex]:(t=e.slideIndex+e.slidesToShow,i.default.findDOMNode(e.trackRef).childNodes[t]))?-1*r.offsetLeft:0,!0===e.centerMode&&(r=!1===e.infinite?i.default.findDOMNode(e.trackRef).children[e.slideIndex]:i.default.findDOMNode(e.trackRef).children[e.slideIndex+e.slidesToShow+1])&&(a=-1*r.offsetLeft+(e.listWidth-r.offsetWidth)/2)),a)}},function(e,t,n){"use strict";t.__esModule=!0;var p=u(n(2)),h=u(n(12)),o=u(n(4)),i=u(n(5)),a=u(n(6)),m=u(n(0)),r=u(n(3)),g=u(n(13)),l=u(n(8)),y=u(n(24)),s=n(11);function u(e){return e&&e.__esModule?e:{default:e}}d=m.default.Component,(0,a.default)(c,d),c.prototype.render=function(){var e=this.props,t=e.title,n=e.children,a=e.className,r=e.isExpanded,o=e.disabled,i=e.style,l=e.prefix,s=e.onClick,u=e.id,e=(0,h.default)(e,["title","children","className","isExpanded","disabled","style","prefix","onClick","id"]),a=(0,g.default)(((d={})[l+"collapse-panel"]=!0,d[l+"collapse-panel-hidden"]=!r,d[l+"collapse-panel-expanded"]=r,d[l+"collapse-panel-disabled"]=o,d[a]=a,d)),d=(0,g.default)(((d={})[l+"collapse-panel-icon"]=!0,d[l+"collapse-panel-icon-expanded"]=r,d)),c=u?u+"-heading":void 0,f=u?u+"-region":void 0;return m.default.createElement("div",(0,p.default)({className:a,style:i,id:u},e),m.default.createElement("div",{id:c,className:l+"collapse-panel-title",onClick:s,onKeyDown:this.onKeyDown,tabIndex:"0","aria-disabled":o,"aria-expanded":r,"aria-controls":f,role:"button"},m.default.createElement(y.default,{type:"arrow-right",className:d,"aria-hidden":"true"}),t),m.default.createElement("div",{className:l+"collapse-panel-content",role:"region",id:f},n))},a=n=c,n.propTypes={prefix:r.default.string,style:r.default.object,children:r.default.any,isExpanded:r.default.bool,disabled:r.default.bool,title:r.default.node,className:r.default.string,onClick:r.default.func,id:r.default.string},n.defaultProps={prefix:"next-",isExpanded:!1,onClick:s.func.noop},n.isNextPanel=!0;var d,r=a;function c(){var e,n;(0,o.default)(this,c);for(var t=arguments.length,a=Array(t),r=0;r\n com.alibaba.nacos\n nacos-client\n ${version}\n \n*/\npackage com.alibaba.nacos.example;\n\nimport java.util.Properties;\nimport java.util.concurrent.Executor;\nimport com.alibaba.nacos.api.NacosFactory;\nimport com.alibaba.nacos.api.config.ConfigService;\nimport com.alibaba.nacos.api.config.listener.Listener;\nimport com.alibaba.nacos.api.exception.NacosException;\n\n/**\n * Config service example\n *\n * @author Nacos\n *\n */\npublic class ConfigExample {\n\n\tpublic static void main(String[] args) throws NacosException, InterruptedException {\n\t\tString serverAddr = "localhost";\n\t\tString dataId = "'.concat(e.dataId,'";\n\t\tString group = "').concat(e.group,'";\n\t\tProperties properties = new Properties();\n\t\tproperties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);\n\t\tConfigService configService = NacosFactory.createConfigService(properties);\n\t\tString content = configService.getConfig(dataId, group, 5000);\n\t\tSystem.out.println(content);\n\t\tconfigService.addListener(dataId, group, new Listener() {\n\t\t\t@Override\n\t\t\tpublic void receiveConfigInfo(String configInfo) {\n\t\t\t\tSystem.out.println("recieve:" + configInfo);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Executor getExecutor() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t});\n\n\t\tboolean isPublishOk = configService.publishConfig(dataId, group, "content");\n\t\tSystem.out.println(isPublishOk);\n\n\t\tThread.sleep(3000);\n\t\tcontent = configService.getConfig(dataId, group, 5000);\n\t\tSystem.out.println(content);\n\n\t\tboolean isRemoveOk = configService.removeConfig(dataId, group);\n\t\tSystem.out.println(isRemoveOk);\n\t\tThread.sleep(3000);\n\n\t\tcontent = configService.getConfig(dataId, group, 5000);\n\t\tSystem.out.println(content);\n\t\tThread.sleep(300000);\n\n\t}\n}\n')}},{key:"getNodejsCode",value:function(e){return"TODO"}},{key:"getCppCode",value:function(e){return"TODO"}},{key:"getShellCode",value:function(e){return"TODO"}},{key:"getPythonCode",value:function(e){return"TODO"}},{key:"getCSharpCode",value:function(e){return'/*\nDemo for Basic Nacos Opreation\nApp.csproj\n\n\n \n\n*/\n\nusing Microsoft.Extensions.DependencyInjection;\nusing Nacos.V2;\nusing Nacos.V2.DependencyInjection;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nclass Program\n{\n static async Task Main(string[] args)\n {\n string serverAddr = "http://localhost:8848";\n string dataId = "'.concat(e.dataId,'";\n string group = "').concat(e.group,'";\n\n IServiceCollection services = new ServiceCollection();\n\n services.AddNacosV2Config(x =>\n {\n x.ServerAddresses = new List { serverAddr };\n x.Namespace = "cs-test";\n\n // swich to use http or rpc\n x.ConfigUseRpc = true;\n });\n\n IServiceProvider serviceProvider = services.BuildServiceProvider();\n var configSvc = serviceProvider.GetService();\n\n var content = await configSvc.GetConfig(dataId, group, 3000);\n Console.WriteLine(content);\n\n var listener = new ConfigListener();\n\n await configSvc.AddListener(dataId, group, listener);\n\n var isPublishOk = await configSvc.PublishConfig(dataId, group, "content");\n Console.WriteLine(isPublishOk);\n\n await Task.Delay(3000);\n content = await configSvc.GetConfig(dataId, group, 5000);\n Console.WriteLine(content);\n\n var isRemoveOk = await configSvc.RemoveConfig(dataId, group);\n Console.WriteLine(isRemoveOk);\n await Task.Delay(3000);\n\n content = await configSvc.GetConfig(dataId, group, 5000);\n Console.WriteLine(content);\n await Task.Delay(300000);\n }\n\n internal class ConfigListener : IListener\n {\n public void ReceiveConfigInfo(string configInfo)\n {\n Console.WriteLine("recieve:" + configInfo);\n }\n }\n}\n\n/*\nRefer to document: https://github.com/nacos-group/nacos-sdk-csharp/tree/dev/samples/MsConfigApp\nDemo for ASP.NET Core Integration\nMsConfigApp.csproj\n\n\n \n\n*/\n\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.Hosting;\nusing Serilog;\nusing Serilog.Events;\n\npublic class Program\n{\n public static void Main(string[] args)\n {\n Log.Logger = new LoggerConfiguration()\n .Enrich.FromLogContext()\n .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)\n .MinimumLevel.Override("System", LogEventLevel.Warning)\n .MinimumLevel.Debug()\n .WriteTo.Console()\n .CreateLogger();\n\n try\n {\n Log.ForContext().Information("Application starting...");\n CreateHostBuilder(args, Log.Logger).Build().Run();\n }\n catch (System.Exception ex)\n {\n Log.ForContext().Fatal(ex, "Application start-up failed!!");\n }\n finally\n {\n Log.CloseAndFlush();\n }\n }\n\n public static IHostBuilder CreateHostBuilder(string[] args, Serilog.ILogger logger) =>\n Host.CreateDefaultBuilder(args)\n .ConfigureAppConfiguration((context, builder) =>\n {\n var c = builder.Build();\n builder.AddNacosV2Configuration(c.GetSection("NacosConfig"), logAction: x => x.AddSerilog(logger));\n })\n .ConfigureWebHostDefaults(webBuilder =>\n {\n webBuilder.UseStartup().UseUrls("http://*:8787");\n })\n .UseSerilog();\n}\n ')}},{key:"openDialog",value:function(e){var t=this;this.setState({dialogvisible:!0}),this.record=e,setTimeout(function(){t.getData()})}},{key:"closeDialog",value:function(){this.setState({dialogvisible:!1})}},{key:"createCodeMirror",value:function(e,t){var n=this.refs.codepreview;n&&(n.innerHTML="",this.cm=window.CodeMirror(n,{value:t,mode:e,height:400,width:500,lineNumbers:!0,theme:"xq-light",lint:!0,tabMode:"indent",autoMatchParens:!0,textWrapping:!0,gutters:["CodeMirror-lint-markers"],extraKeys:{F1:function(e){e.setOption("fullScreen",!e.getOption("fullScreen"))},Esc:function(e){e.getOption("fullScreen")&&e.setOption("fullScreen",!1)}}}))}},{key:"changeTab",value:function(e,t){var n=this;setTimeout(function(){n[e]=!0,n.createCodeMirror("text/javascript",t)})}},{key:"render",value:function(){var e=this.props.locale,e=void 0===e?{}:e;return x.a.createElement("div",null,x.a.createElement(y.a,{title:e.sampleCode,style:{width:"80%"},visible:this.state.dialogvisible,footer:x.a.createElement("div",null),onClose:this.closeDialog.bind(this)},x.a.createElement("div",{style:{height:500}},x.a.createElement(R.a,{tip:e.loading,style:{width:"100%"},visible:this.state.loading},x.a.createElement(O.a,{shape:"text",style:{height:40,paddingBottom:10}},x.a.createElement(N,{title:"Java",key:1,onClick:this.changeTab.bind(this,"commoneditor1",this.defaultCode)}),x.a.createElement(N,{title:"Spring Boot",key:2,onClick:this.changeTab.bind(this,"commoneditor2",this.sprigboot_code)}),x.a.createElement(N,{title:"Spring Cloud",key:21,onClick:this.changeTab.bind(this,"commoneditor21",this.sprigcloud_code)}),x.a.createElement(N,{title:"Node.js",key:3,onClick:this.changeTab.bind(this,"commoneditor3",this.nodejsCode)}),x.a.createElement(N,{title:"C++",key:4,onClick:this.changeTab.bind(this,"commoneditor4",this.cppCode)}),x.a.createElement(N,{title:"Shell",key:5,onClick:this.changeTab.bind(this,"commoneditor5",this.shellCode)}),x.a.createElement(N,{title:"Python",key:6,onClick:this.changeTab.bind(this,"commoneditor6",this.pythonCode)}),x.a.createElement(N,{title:"C#",key:7,onClick:this.changeTab.bind(this,"commoneditor7",this.csharpCode)})),x.a.createElement("div",{ref:"codepreview"})))))}}]),n}(x.a.Component)).displayName="ShowCodeing",S=S))||S,S=(t(66),t(41)),S=t.n(S),H=(t(716),S.a.Row),P=S.a.Col,F=(0,n.a.config)(((S=function(e){Object(M.a)(n,e);var t=Object(k.a)(n);function n(e){return Object(_.a)(this,n),(e=t.call(this,e)).state={visible:!1,title:"",content:"",isok:!0,dataId:"",group:""},e}return Object(b.a)(n,[{key:"componentDidMount",value:function(){this.initData()}},{key:"initData",value:function(){var e=this.props.locale;this.setState({title:(void 0===e?{}:e).confManagement})}},{key:"openDialog",value:function(e){this.setState({visible:!0,title:e.title,content:e.content,isok:e.isok,dataId:e.dataId,group:e.group,message:e.message})}},{key:"closeDialog",value:function(){this.setState({visible:!1})}},{key:"render",value:function(){var e=this.props.locale,e=void 0===e?{}:e,t=x.a.createElement("div",{style:{textAlign:"right"}},x.a.createElement(d.a,{type:"primary",onClick:this.closeDialog.bind(this)},e.determine));return x.a.createElement("div",null,x.a.createElement(y.a,{visible:this.state.visible,footer:t,style:{width:555},onCancel:this.closeDialog.bind(this),onClose:this.closeDialog.bind(this),title:e.deletetitle},x.a.createElement("div",null,x.a.createElement(H,null,x.a.createElement(P,{span:"4",style:{paddingTop:16}},x.a.createElement(m.a,{type:"".concat(this.state.isok?"success":"delete","-filling"),style:{color:this.state.isok?"green":"red"},size:"xl"})),x.a.createElement(P,{span:"20"},x.a.createElement("div",null,x.a.createElement("h3",null,this.state.isok?e.deletedSuccessfully:e.deleteFailed),x.a.createElement("p",null,x.a.createElement("span",{style:{color:"#999",marginRight:5}},"Data ID"),x.a.createElement("span",{style:{color:"#c7254e"}},this.state.dataId)),x.a.createElement("p",null,x.a.createElement("span",{style:{color:"#999",marginRight:5}},"Group"),x.a.createElement("span",{style:{color:"#c7254e"}},this.state.group)),this.state.isok?"":x.a.createElement("p",{style:{color:"red"}},this.state.message)))))))}}]),n}(x.a.Component)).displayName="DeleteDialog",S=S))||S,S=(t(717),t(419)),z=t.n(S),W=(0,n.a.config)(((S=function(e){Object(M.a)(n,e);var t=Object(k.a)(n);function n(){return Object(_.a)(this,n),t.apply(this,arguments)}return Object(b.a)(n,[{key:"render",value:function(){var e=this.props,t=e.data,t=void 0===t?{}:t,n=e.height,e=e.locale,a=void 0===e?{}:e;return x.a.createElement("div",null,"notice"===t.modeType?x.a.createElement("div",{"data-spm-click":"gostr=/aliyun;locaid=notice"},x.a.createElement(z.a,{style:{marginBottom:1l?b.a.createElement(u.a,{className:"pagination",total:i.count,pageSize:l,onChange:function(e){return a.onChangePage(e)}}):null,b.a.createElement(C,{ref:this.editInstanceDialog,serviceName:n,clusterName:t,groupName:e,openLoading:function(){return a.openLoading()},closeLoading:function(){return a.closeLoading()},getInstanceList:function(){return a.getInstanceList()}})):null}}]),n}(b.a.Component)).displayName="InstanceTable",l.defaultProps={filters:new Map},l=l))||l,L=function(e,t){return e.filter(function(e){var n=e.metadata,a=!0;return t.forEach(function(e,t){if(e!==n[t])return a=!1}),a})},T=l,D=t(47),O=t(31),l=(t(175),t(74)),l=t.n(l),N=l.a.Group,P=l.a.Closeable,j=v.a.Item;var R=n.a.config(function(e){function t(){var e;a(),o&&l&&(e=new Map(Array.from(p)).set(o,l),h(e),d(""),f(""),n())}function n(){i(""),s("")}function a(){d(o?"":"error"),f(l?"":"error")}var r=Object(_.useState)(""),o=(r=Object(O.a)(r,2))[0],i=r[1],r=Object(_.useState)(""),l=(r=Object(O.a)(r,2))[0],s=r[1],r=Object(_.useState)(""),u=(r=Object(O.a)(r,2))[0],d=r[1],r=Object(_.useState)(""),c=(r=Object(O.a)(r,2))[0],f=r[1],r=Object(_.useState)(new Map),p=(r=Object(O.a)(r,2))[0],h=r[1],r=void 0===(r=e.locale)?{}:r;return Object(_.useEffect)(function(){e.setFilters(p)},[p]),b.a.createElement(m.a,{contentHeight:"auto",className:"inner-card"},b.a.createElement(v.a,{inline:!0,size:"small"},b.a.createElement(j,{label:r.title},b.a.createElement(j,null,b.a.createElement(y.a,{placeholder:"key",value:o,trim:!0,onChange:function(e){return i(e)},onPressEnter:t,state:u})),b.a.createElement(j,null,b.a.createElement(y.a,{placeholder:"value",value:l,trim:!0,onChange:function(e){return s(e)},onPressEnter:t,state:c})),b.a.createElement(j,{label:""},b.a.createElement(g.a,{type:"primary",onClick:t,style:{marginRight:10}},r.addFilter),0\n com.alibaba.nacos\n nacos-client\n ${latest.version}\n \n*/\npackage com.alibaba.nacos.example;\n\nimport java.util.Properties;\n\nimport com.alibaba.nacos.api.exception.NacosException;\nimport com.alibaba.nacos.api.naming.NamingFactory;\nimport com.alibaba.nacos.api.naming.NamingService;\nimport com.alibaba.nacos.api.naming.listener.Event;\nimport com.alibaba.nacos.api.naming.listener.EventListener;\nimport com.alibaba.nacos.api.naming.listener.NamingEvent;\n\n/**\n * @author nkorange\n */\npublic class NamingExample {\n\n public static void main(String[] args) throws NacosException {\n\n Properties properties = new Properties();\n properties.setProperty("serverAddr", System.getProperty("serverAddr"));\n properties.setProperty("namespace", System.getProperty("namespace"));\n\n NamingService naming = NamingFactory.createNamingService(properties);\n\n naming.registerInstance("'.concat(this.record.name,'", "11.11.11.11", 8888, "TEST1");\n\n naming.registerInstance("').concat(this.record.name,'", "2.2.2.2", 9999, "DEFAULT");\n\n System.out.println(naming.getAllInstances("').concat(this.record.name,'"));\n\n naming.deregisterInstance("').concat(this.record.name,'", "2.2.2.2", 9999, "DEFAULT");\n\n System.out.println(naming.getAllInstances("').concat(this.record.name,'"));\n\n naming.subscribe("').concat(this.record.name,'", new EventListener() {\n @Override\n public void onEvent(Event event) {\n System.out.println(((NamingEvent)event).getServiceName());\n System.out.println(((NamingEvent)event).getInstances());\n }\n });\n }\n}')}},{key:"getSpringCode",value:function(e){return'/* Refer to document: https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-example/nacos-spring-discovery-example\n* pom.xml\n \n com.alibaba.nacos\n nacos-spring-context\n ${latest.version}\n \n*/\n\n// Refer to document: https://github.com/nacos-group/nacos-examples/blob/master/nacos-spring-example/nacos-spring-discovery-example/src/main/java/com/alibaba/nacos/example/spring\npackage com.alibaba.nacos.example.spring;\n\nimport com.alibaba.nacos.api.annotation.NacosProperties;\nimport com.alibaba.nacos.spring.context.annotation.discovery.EnableNacosDiscovery;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\n@EnableNacosDiscovery(globalProperties = @NacosProperties(serverAddr = "127.0.0.1:8848"))\npublic class NacosConfiguration {\n\n}\n\n// Refer to document: https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-example/nacos-spring-discovery-example/src/main/java/com/alibaba/nacos/example/spring/controller\npackage com.alibaba.nacos.example.spring.controller;\n\nimport com.alibaba.nacos.api.annotation.NacosInjected;\nimport com.alibaba.nacos.api.exception.NacosException;\nimport com.alibaba.nacos.api.naming.NamingService;\nimport com.alibaba.nacos.api.naming.pojo.Instance;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport java.util.List;\n\nimport static org.springframework.web.bind.annotation.RequestMethod.GET;\n\n@Controller\n@RequestMapping("discovery")\npublic class DiscoveryController {\n\n @NacosInjected\n private NamingService namingService;\n\n @RequestMapping(value = "/get", method = GET)\n @ResponseBody\n public List get(@RequestParam String serviceName) throws NacosException {\n return namingService.getAllInstances(serviceName);\n }\n}'}},{key:"getSpringBootCode",value:function(e){return'/* Refer to document: https://github.com/nacos-group/nacos-examples/blob/master/nacos-spring-boot-example/nacos-spring-boot-discovery-example\n* pom.xml\n \n com.alibaba.boot\n nacos-discovery-spring-boot-starter\n ${latest.version}\n \n*/\n/* Refer to document: https://github.com/nacos-group/nacos-examples/blob/master/nacos-spring-boot-example/nacos-spring-boot-discovery-example/src/main/resources\n* application.properties\n nacos.discovery.server-addr=127.0.0.1:8848\n*/\n// Refer to document: https://github.com/nacos-group/nacos-examples/blob/master/nacos-spring-boot-example/nacos-spring-boot-discovery-example/src/main/java/com/alibaba/nacos/example/spring/boot/controller\n\npackage com.alibaba.nacos.example.spring.boot.controller;\n\nimport com.alibaba.nacos.api.annotation.NacosInjected;\nimport com.alibaba.nacos.api.exception.NacosException;\nimport com.alibaba.nacos.api.naming.NamingService;\nimport com.alibaba.nacos.api.naming.pojo.Instance;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport java.util.List;\n\nimport static org.springframework.web.bind.annotation.RequestMethod.GET;\n\n@Controller\n@RequestMapping("discovery")\npublic class DiscoveryController {\n\n @NacosInjected\n private NamingService namingService;\n\n @RequestMapping(value = "/get", method = GET)\n @ResponseBody\n public List get(@RequestParam String serviceName) throws NacosException {\n return namingService.getAllInstances(serviceName);\n }\n}'}},{key:"getSpringCloudCode",value:function(e){return"/* Refer to document: https://github.com/nacos-group/nacos-examples/blob/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example/\n* pom.xml\n \n org.springframework.cloud\n spring-cloud-starter-alibaba-nacos-discovery\n ${latest.version}\n \n*/\n\n// nacos-spring-cloud-provider-example\n\n/* Refer to document: https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example/nacos-spring-cloud-provider-example/src/main/resources\n* application.properties\nserver.port=18080\nspring.application.name=".concat(this.record.name,'\nspring.cloud.nacos.discovery.server-addr=127.0.0.1:8848\n*/\n\n// Refer to document: https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example/nacos-spring-cloud-provider-example/src/main/java/com/alibaba/nacos/example/spring/cloud\npackage com.alibaba.nacos.example.spring.cloud;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author xiaojing\n */\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class NacosProviderApplication {\n\n public static void main(String[] args) {\n SpringApplication.run(NacosProviderApplication.class, args);\n}\n\n @RestController\n class EchoController {\n @RequestMapping(value = "/echo/{string}", method = RequestMethod.GET)\n public String echo(@PathVariable String string) {\n return "Hello Nacos Discovery " + string;\n }\n }\n}\n\n// nacos-spring-cloud-consumer-example\n\n/* Refer to document: https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example/nacos-spring-cloud-consumer-example/src/main/resources\n* application.properties\nspring.application.name=micro-service-oauth2\nspring.cloud.nacos.discovery.server-addr=127.0.0.1:8848\n*/\n\n// Refer to document: https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example/nacos-spring-cloud-consumer-example/src/main/java/com/alibaba/nacos/example/spring/cloud\npackage com.alibaba.nacos.example.spring.cloud;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * @author xiaojing\n */\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class NacosConsumerApplication {\n\n @LoadBalanced\n @Bean\n public RestTemplate restTemplate() {\n return new RestTemplate();\n }\n\n public static void main(String[] args) {\n SpringApplication.run(NacosConsumerApplication.class, args);\n }\n\n @RestController\n public class TestController {\n\n private final RestTemplate restTemplate;\n\n @Autowired\n public TestController(RestTemplate restTemplate) {this.restTemplate = restTemplate;}\n\n @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)\n public String echo(@PathVariable String str) {\n return restTemplate.getForObject("http://service-provider/echo/" + str, String.class);\n }\n }\n}')}},{key:"getNodejsCode",value:function(e){return"TODO"}},{key:"getCppCode",value:function(e){return"TODO"}},{key:"getShellCode",value:function(e){return"TODO"}},{key:"getPythonCode",value:function(e){return"TODO"}},{key:"getCSharpCode",value:function(e){return'/* Refer to document: https://github.com/nacos-group/nacos-sdk-csharp/\nDemo for Basic Nacos Opreation\nApp.csproj\n\n\n \n\n*/\n\nusing Microsoft.Extensions.DependencyInjection;\nusing Nacos.V2;\nusing Nacos.V2.DependencyInjection;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nclass Program\n{\n static async Task Main(string[] args)\n {\n IServiceCollection services = new ServiceCollection();\n\n services.AddNacosV2Naming(x =>\n {\n x.ServerAddresses = new List { "http://localhost:8848/" };\n x.Namespace = "cs-test";\n\n // swich to use http or rpc\n x.NamingUseRpc = true;\n });\n\n IServiceProvider serviceProvider = services.BuildServiceProvider();\n var namingSvc = serviceProvider.GetService();\n\n await namingSvc.RegisterInstance("'.concat(this.record.name,'", "11.11.11.11", 8888, "TEST1");\n\n await namingSvc.RegisterInstance("').concat(this.record.name,'", "2.2.2.2", 9999, "DEFAULT");\n\n Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(await namingSvc.GetAllInstances("').concat(this.record.name,'")));\n\n await namingSvc.DeregisterInstance("').concat(this.record.name,'", "2.2.2.2", 9999, "DEFAULT");\n\n var listener = new EventListener();\n\n await namingSvc.Subscribe("').concat(this.record.name,'", listener);\n }\n\n internal class EventListener : IEventListener\n {\n public Task OnEvent(IEvent @event)\n {\n Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(@event));\n return Task.CompletedTask;\n }\n }\n}\n\n/* Refer to document: https://github.com/nacos-group/nacos-sdk-csharp/\nDemo for ASP.NET Core Integration\nApp.csproj\n\n\n \n\n*/\n\n/* Refer to document: https://github.com/nacos-group/nacos-sdk-csharp/blob/dev/samples/App1/appsettings.json\n* appsettings.json\n{\n "nacos": {\n "ServerAddresses": [ "http://localhost:8848" ],\n "DefaultTimeOut": 15000,\n "Namespace": "cs",\n "ServiceName": "App1",\n "GroupName": "DEFAULT_GROUP",\n "ClusterName": "DEFAULT",\n "Port": 0,\n "Weight": 100,\n "RegisterEnabled": true,\n "InstanceEnabled": true,\n "Ephemeral": true,\n "NamingUseRpc": true,\n "NamingLoadCacheAtStart": ""\n }\n}\n*/\n\n// Refer to document: https://github.com/nacos-group/nacos-sdk-csharp/blob/dev/samples/App1/Startup.cs\nusing Nacos.AspNetCore.V2;\n\npublic class Startup\n{\n public Startup(IConfiguration configuration)\n {\n Configuration = configuration;\n }\n\n public IConfiguration Configuration { get; }\n\n public void ConfigureServices(IServiceCollection services)\n {\n // ....\n services.AddNacosAspNet(Configuration);\n }\n\n public void Configure(IApplicationBuilder app, IWebHostEnvironment env)\n {\n // ....\n }\n}\n ')}},{key:"openDialog",value:function(e){var t=this;this.setState({dialogvisible:!0}),this.record=e,setTimeout(function(){t.getData()})}},{key:"closeDialog",value:function(){this.setState({dialogvisible:!1})}},{key:"createCodeMirror",value:function(e,t){var n=this.refs.codepreview;n&&(n.innerHTML="",this.cm=window.CodeMirror(n,{value:t,mode:e,height:400,width:500,lineNumbers:!0,theme:"xq-light",lint:!0,tabMode:"indent",autoMatchParens:!0,textWrapping:!0,gutters:["CodeMirror-lint-markers"],extraKeys:{F1:function(e){e.setOption("fullScreen",!e.getOption("fullScreen"))},Esc:function(e){e.getOption("fullScreen")&&e.setOption("fullScreen",!1)}}}),this.cm.setSize("auto","490px"))}},{key:"changeTab",value:function(e,t){var n=this;setTimeout(function(){n[e]=!0,n.createCodeMirror("text/javascript",t)})}},{key:"render",value:function(){var e=this.props.locale,e=void 0===e?{}:e;return L.a.createElement("div",null,L.a.createElement(o.a,{title:e.sampleCode,style:{width:"80%"},visible:this.state.dialogvisible,footer:L.a.createElement("div",null),onClose:this.closeDialog.bind(this)},L.a.createElement("div",{style:{height:500}},L.a.createElement(h.a,{tip:e.loading,style:{width:"100%"},visible:this.state.loading},L.a.createElement(m.a,{shape:"text",style:{height:40,paddingBottom:10}},L.a.createElement(g,{title:"Java",key:0,onClick:this.changeTab.bind(this,"commoneditor1",this.defaultCode)}),L.a.createElement(g,{title:"Spring",key:1,onClick:this.changeTab.bind(this,"commoneditor1",this.springCode)}),L.a.createElement(g,{title:"Spring Boot",key:2,onClick:this.changeTab.bind(this,"commoneditor2",this.sprigbootCode)}),L.a.createElement(g,{title:"Spring Cloud",key:21,onClick:this.changeTab.bind(this,"commoneditor21",this.sprigcloudCode)}),L.a.createElement(g,{title:"Node.js",key:3,onClick:this.changeTab.bind(this,"commoneditor3",this.nodejsCode)}),L.a.createElement(g,{title:"C++",key:4,onClick:this.changeTab.bind(this,"commoneditor4",this.cppCode)}),L.a.createElement(g,{title:"Shell",key:5,onClick:this.changeTab.bind(this,"commoneditor5",this.shellCode)}),L.a.createElement(g,{title:"Python",key:6,onClick:this.changeTab.bind(this,"commoneditor6",this.pythonCode)}),L.a.createElement(g,{title:"C#",key:7,onClick:this.changeTab.bind(this,"commoneditor7",this.csharpCode)})),L.a.createElement("div",{ref:"codepreview"})))))}}]),n}(L.a.Component)).displayName="ShowServiceCodeing",f=f))||f,P=t(69),j=(t(738),t(29)),Y=C.a.Item,I=a.a.Row,R=a.a.Col,A=x.a.Column,a=(0,n.a.config)(((f=function(e){Object(d.a)(a,e);var t=Object(c.a)(a);function a(e){var n;return Object(l.a)(this,a),(n=t.call(this,e)).getQueryLater=function(){setTimeout(function(){return n.queryServiceList()})},n.showcode=function(){setTimeout(function(){return n.queryServiceList()})},n.setNowNameSpace=function(e,t){return n.setState({nowNamespaceName:e,nowNamespaceId:t})},n.rowColor=function(e){return{className:e.healthyInstanceCount?"":"row-bg-red"}},n.editServiceDialog=L.a.createRef(),n.showcode=L.a.createRef(),n.state={loading:!1,total:0,pageSize:10,currentPage:1,dataSource:[],search:{serviceName:"",groupName:""},hasIpCount:!("false"===localStorage.getItem("hasIpCount"))},n.field=new i.a(Object(u.a)(n)),n}return Object(s.a)(a,[{key:"openLoading",value:function(){this.setState({loading:!0})}},{key:"closeLoading",value:function(){this.setState({loading:!1})}},{key:"openEditServiceDialog",value:function(){try{this.editServiceDialog.current.getInstance().show(this.state.service)}catch(e){}}},{key:"queryServiceList",value:function(){var n=this,e=this.state,t=e.currentPage,a=e.pageSize,r=e.search,o=e.withInstances,o=void 0!==o&&o,e=e.hasIpCount,e=["hasIpCount=".concat(e),"withInstances=".concat(o),"pageNo=".concat(t),"pageSize=".concat(a),"serviceNameParam=".concat(r.serviceName),"groupNameParam=".concat(r.groupName)];this.openLoading(),Object(p.b)({url:"v1/ns/catalog/services?".concat(e.join("&")),success:function(){var e=0o&&v.a.createElement(u.a,{className:"users-pagination",current:i,total:n.totalCount,pageSize:o,onChange:function(e){return t.setState({pageNo:e},function(){return t.getUsers()})}}),v.a.createElement(E,{visible:l,onOk:function(e){return Object(_.c)(e).then(function(e){return t.setState({pageNo:1},function(){return t.getUsers()}),e})},onCancel:function(){return t.colseCreateUser()}}),v.a.createElement(x.a,{visible:s,username:e,onOk:function(e){return Object(_.k)(e).then(function(e){return t.getUsers(),e})},onCancel:function(){return t.setState({passwordResetUser:void 0,passwordResetUserVisible:!1})}}))}}]),n}(v.a.Component)).displayName="UserManagement",n=o))||n)||n;t.a=r},function(e,t,n){"use strict";n(64);var a=n(46),s=n.n(a),a=(n(34),n(19)),u=n.n(a),d=n(31),a=(n(63),n(20)),c=n.n(a),a=(n(32),n(18)),f=n.n(a),a=(n(87),n(53)),p=n.n(a),a=(n(39),n(7)),h=n.n(a),a=(n(35),n(10)),m=n.n(a),i=n(14),l=n(15),g=n(22),y=n(17),v=n(16),a=(n(25),n(8)),a=n.n(a),r=n(0),_=n.n(r),r=n(36),b=n(45),o=n(82),w=n(48),M=(n(49),n(26)),k=n.n(M),M=(n(59),n(28)),S=n.n(M),E=h.a.Item,x=S.a.Option,C={labelCol:{fixedSpan:4},wrapperCol:{span:19}},L=Object(r.b)(function(e){return{namespaces:e.namespace.namespaces}},{getNamespaces:o.b,searchRoles:b.l})(M=(0,a.a.config)(((M=function(e){Object(y.a)(o,e);var r=Object(v.a)(o);function o(){var t;Object(i.a)(this,o);for(var e=arguments.length,n=new Array(e),a=0;ai&&_.a.createElement(s.a,{className:"users-pagination",current:l,total:t.totalCount,pageSize:i,onChange:function(e){return a.setState({pageNo:e},function(){return a.getPermissions()})}}),_.a.createElement(L,{visible:n,onOk:function(e){return Object(b.a)(e).then(function(e){return a.setState({pageNo:1},function(){return a.getPermissions()}),e})},onCancel:function(){return a.colseCreatePermission()}}))}}]),n}(_.a.Component)).displayName="PermissionsManagement",n=M))||n)||n);t.a=r},function(e,t,n){"use strict";n(64);var a=n(46),s=n.n(a),a=(n(34),n(19)),u=n.n(a),a=(n(63),n(20)),d=n.n(a),a=(n(32),n(18)),c=n.n(a),a=(n(87),n(53)),f=n.n(a),a=(n(39),n(7)),p=n.n(a),a=(n(35),n(10)),h=n.n(a),i=n(14),l=n(15),m=n(22),g=n(17),y=n(16),a=(n(25),n(8)),a=n.n(a),r=n(0),v=n.n(r),r=n(36),_=n(45),b=n(48),o=(n(59),n(28)),w=n.n(o),o=(n(49),n(26)),M=n.n(o),k=p.a.Item,S={labelCol:{fixedSpan:4},wrapperCol:{span:19}},E=Object(r.b)(function(e){return{users:e.authority.users}},{searchUsers:_.m})(o=(0,a.a.config)(((o=function(e){Object(g.a)(o,e);var r=Object(y.a)(o);function o(){var t;Object(i.a)(this,o);for(var e=arguments.length,n=new Array(e),a=0;ao&&v.a.createElement(s.a,{className:"users-pagination",current:i,total:t.totalCount,pageSize:o,onChange:function(e){return a.setState({pageNo:e},function(){return a.getRoles()})}}),v.a.createElement(E,{visible:l,onOk:function(e){return Object(_.b)(e).then(function(e){return a.getRoles(),e})},onCancel:function(){return a.colseCreateRole()}}))}}]),n}(v.a.Component)).displayName="RolesManagement",n=o))||n)||n);t.a=r},function(e,t,n){"use strict";n(43);function o(e){var t=void 0===(t=localStorage.token)?"{}":t,t=(Object(y.c)(t)&&JSON.parse(t)||{}).globalAdmin;return["naming"===e?void 0:v,{key:"serviceManagementVirtual",children:[{key:"serviceManagement",url:"/serviceManagement"},{key:"subscriberList",url:"/subscriberList"}]},t?_:void 0,{key:"namespace",url:"/namespace"},{key:"clusterManagementVirtual",children:[{key:"clusterManagement",url:"/clusterManagement"}]}].filter(function(e){return e})}var a=n(24),i=n.n(a),r=n(14),l=n(15),s=n(17),u=n(16),a=(n(25),n(8)),a=n.n(a),d=n(21),c=(n(80),n(50)),f=n.n(c),c=n(0),p=n.n(c),c=n(40),h=n(36),m=n(134),g=n(101),y=n(47),v={key:"configurationManagementVirtual",children:[{key:"configurationManagement",url:"/configurationManagement"},{key:"historyRollback",url:"/historyRollback"},{key:"listeningToQuery",url:"/listeningToQuery"}]},_={key:"authorityControl",children:[{key:"userList",url:"/userManagement"},{key:"roleManagement",url:"/rolesManagement"},{key:"privilegeManagement",url:"/permissionsManagement"}]},b=f.a.SubMenu,w=f.a.Item,c=(n=Object(h.b)(function(e){return Object(d.a)(Object(d.a)({},e.locale),e.base)},{getState:g.b}),h=a.a.config,Object(c.g)(a=n(a=h(((g=function(e){Object(s.a)(n,e);var t=Object(u.a)(n);function n(){return Object(r.a)(this,n),t.apply(this,arguments)}return Object(l.a)(n,[{key:"componentDidMount",value:function(){this.props.getState()}},{key:"goBack",value:function(){this.props.history.goBack()}},{key:"navTo",value:function(e){var t=this.props.location.search,t=new URLSearchParams(t);t.set("namespace",window.nownamespace),t.set("namespaceShowName",window.namespaceShowName),this.props.history.push([e,"?",t.toString()].join(""))}},{key:"isCurrentPath",value:function(e){return e===this.props.location.pathname?"current-path next-selected":void 0}},{key:"defaultOpenKeys",value:function(){for(var t=this,e=o(this.props.functionMode),n=0,a=e.length;nthis.state.pageSize&&M.a.createElement("div",{style:{marginTop:10,textAlign:"right"}},M.a.createElement(g.a,{current:this.state.pageNo,total:a,pageSize:this.state.pageSize,onChange:function(e){return t.setState({pageNo:e},function(){return t.querySubscriberList()})}}))))}}]),a}(M.a.Component)).displayName="SubscriberList",c=n))||c)||c;t.a=f},function(e,t,n){"use strict";n(51);var a=n(33),c=n.n(a),a=(n(64),n(46)),f=n.n(a),a=(n(175),n(74)),p=n.n(a),a=(n(35),n(10)),h=n.n(a),a=(n(32),n(18)),m=n.n(a),a=(n(34),n(19)),r=n.n(a),a=(n(49),n(26)),o=n.n(a),i=n(14),l=n(15),s=n(22),u=n(17),d=n(16),a=(n(25),n(8)),a=n.n(a),g=(n(403),n(116)),y=n.n(g),g=(n(63),n(20)),v=n.n(g),g=(n(66),n(41)),g=n.n(g),_=(n(39),n(7)),b=n.n(_),_=n(0),w=n.n(_),M=n(1),k=n(48),_=n(136),S=n.n(_),E=n(69),x=(n(741),b.a.Item),C=g.a.Row,L=g.a.Col,T=v.a.Column,D=y.a.Panel,g=(0,a.a.config)(((_=function(e){Object(u.a)(a,e);var t=Object(d.a)(a);function a(e){var n;return Object(i.a)(this,a),(n=t.call(this,e)).getQueryLater=function(){setTimeout(function(){return n.queryClusterStateList()})},n.setNowNameSpace=function(e,t){return n.setState({nowNamespaceName:e,nowNamespaceId:t})},n.rowColor=function(e){return{className:(e.voteFor,"")}},n.state={loading:!1,total:0,pageSize:10,currentPage:1,keyword:"",dataSource:[]},n.field=new o.a(Object(s.a)(n)),n}return Object(l.a)(a,[{key:"openLoading",value:function(){this.setState({loading:!0})}},{key:"closeLoading",value:function(){this.setState({loading:!1})}},{key:"openEditServiceDialog",value:function(){try{this.editServiceDialog.current.getInstance().show(this.state.service)}catch(e){}}},{key:"queryClusterStateList",value:function(){var n=this,e=this.state,t=e.currentPage,a=e.pageSize,r=e.keyword,e=e.withInstances,e=["withInstances=".concat(void 0!==e&&e),"pageNo=".concat(t),"pageSize=".concat(a),"keyword=".concat(r)];Object(M.b)({url:"v1/core/cluster/nodes?".concat(e.join("&")),beforeSend:function(){return n.openLoading()},success:function(){var e=0this.state.pageSize&&w.a.createElement("div",{style:{marginTop:10,textAlign:"right"}},w.a.createElement(f.a,{current:this.state.currentPage,total:this.state.total,pageSize:this.state.pageSize,onChange:function(e){return t.setState({currentPage:e},function(){return t.queryClusterStateList()})}}))))}}]),a}(w.a.Component)).displayName="ClusterNodeList",n=_))||n;t.a=g},function(e,t,F){"use strict";F.r(t),function(e){F(51);var t=F(33),a=F.n(t),t=(F(25),F(8)),r=F.n(t),o=F(14),i=F(15),l=F(17),s=F(16),n=F(21),t=F(0),u=F.n(t),t=F(23),t=F.n(t),d=F(118),c=F(411),f=F(423),p=F(36),h=F(40),m=F(81),g=(F(458),F(432)),y=F(29),v=F(433),_=F(426),b=F(434),w=F(435),M=F(427),k=F(436),S=F(437),E=F(438),x=F(439),C=F(440),L=F(424),T=F(428),D=F(425),O=F(441),N=F(442),P=F(429),j=F(430),I=F(431),R=F(421),Y=F(422),A=F(104),e=(F(744),F(745),F(746),e.hot,localStorage.getItem(y.f)||localStorage.setItem(y.f,"zh-CN"===navigator.language?"zh-CN":"en-US"),Object(d.b)(Object(n.a)(Object(n.a)({},Y.a),{},{routing:c.routerReducer}))),Y=Object(d.d)(e,Object(d.c)(Object(d.a)(f.a),window[y.i]?window[y.i]():function(e){return e})),H=[{path:"/",exact:!0,render:function(){return u.a.createElement(h.a,{to:"/welcome"})}},{path:"/welcome",component:R.a},{path:"/namespace",component:_.a},{path:"/newconfig",component:b.a},{path:"/configsync",component:w.a},{path:"/configdetail",component:M.a},{path:"/configeditor",component:k.a},{path:"/historyDetail",component:S.a},{path:"/configRollback",component:E.a},{path:"/historyRollback",component:x.a},{path:"/listeningToQuery",component:C.a},{path:"/configurationManagement",component:L.a},{path:"/serviceManagement",component:T.a},{path:"/serviceDetail",component:D.a},{path:"/subscriberList",component:O.a},{path:"/clusterManagement",component:N.a},{path:"/userManagement",component:P.a},{path:"/rolesManagement",component:I.a},{path:"/permissionsManagement",component:j.a}],e=Object(p.b)(function(e){return Object(n.a)({},e.locale)},{changeLanguage:A.a})(c=function(e){Object(l.a)(n,e);var t=Object(s.a)(n);function n(e){return Object(o.a)(this,n),(e=t.call(this,e)).state={shownotice:"none",noticecontent:"",nacosLoading:{}},e}return Object(i.a)(n,[{key:"componentDidMount",value:function(){var e=localStorage.getItem(y.f);this.props.changeLanguage(e)}},{key:"router",get:function(){return u.a.createElement(m.a,null,u.a.createElement(h.d,null,u.a.createElement(h.b,{path:"/login",component:v.a}),u.a.createElement(g.a,null,H.map(function(e){return u.a.createElement(h.b,Object.assign({key:e.path},e))}))))}},{key:"render",value:function(){var e=this.props.locale;return u.a.createElement(a.a,Object.assign({className:"nacos-loading",shape:"flower",tip:"loading...",visible:!1,fullScreen:!0},this.state.nacosLoading),u.a.createElement(r.a,{locale:e},this.router))}}]),n}(u.a.Component))||c;t.a.render(u.a.createElement(p.a,{store:Y},u.a.createElement(e,null)),document.getElementById("root"))}.call(this,F(444)(e))},function(e,t){e.exports=function(e){var t;return e.webpackPolyfill||((t=Object.create(e)).children||(t.children=[]),Object.defineProperty(t,"loaded",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,"id",{enumerable:!0,get:function(){return t.i}}),Object.defineProperty(t,"exports",{enumerable:!0}),t.webpackPolyfill=1),t}},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(I,e,t){"use strict"; /** @license React v16.14.0 * react.production.min.js * @@ -326,5 +326,5 @@ var t;e.defineLocale("zh-tw",{months:"äø€ęœˆ_äŗŒęœˆ_äø‰ęœˆ_å››ęœˆ_äŗ”ęœˆ_å…­ęœˆ_ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var a=60103,r=60106,o=60107,i=60108,l=60114,s=60109,u=60110,d=60112,c=60113,f=60120,p=60115,h=60116,m=60121,g=60122,y=60117,v=60129,_=60131;function b(e){if("object"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case a:switch(e=e.type){case o:case l:case i:case c:case f:return e;default:switch(e=e&&e.$$typeof){case u:case d:case h:case p:case s:return e;default:return t}}case r:return t}}}"function"==typeof Symbol&&Symbol.for&&(a=(w=Symbol.for)("react.element"),r=w("react.portal"),o=w("react.fragment"),i=w("react.strict_mode"),l=w("react.profiler"),s=w("react.provider"),u=w("react.context"),d=w("react.forward_ref"),c=w("react.suspense"),f=w("react.suspense_list"),p=w("react.memo"),h=w("react.lazy"),m=w("react.block"),g=w("react.server.block"),y=w("react.fundamental"),v=w("react.debug_trace_mode"),_=w("react.legacy_hidden"));var w=s,M=a,k=d,S=o,E=h,x=p,C=r,L=l,T=i,D=c;t.ContextConsumer=u,t.ContextProvider=w,t.Element=M,t.ForwardRef=k,t.Fragment=S,t.Lazy=E,t.Memo=x,t.Portal=C,t.Profiler=L,t.StrictMode=T,t.Suspense=D,t.isAsyncMode=function(){return!1},t.isConcurrentMode=function(){return!1},t.isContextConsumer=function(e){return b(e)===u},t.isContextProvider=function(e){return b(e)===s},t.isElement=function(e){return"object"==typeof e&&null!==e&&e.$$typeof===a},t.isForwardRef=function(e){return b(e)===d},t.isFragment=function(e){return b(e)===o},t.isLazy=function(e){return b(e)===h},t.isMemo=function(e){return b(e)===p},t.isPortal=function(e){return b(e)===r},t.isProfiler=function(e){return b(e)===l},t.isStrictMode=function(e){return b(e)===i},t.isSuspense=function(e){return b(e)===c},t.isValidElementType=function(e){return"string"==typeof e||"function"==typeof e||e===o||e===l||e===v||e===i||e===c||e===f||e===_||"object"==typeof e&&null!==e&&(e.$$typeof===h||e.$$typeof===p||e.$$typeof===s||e.$$typeof===u||e.$$typeof===d||e.$$typeof===y||e.$$typeof===m||e[0]===g)},t.typeOf=b},function(e,t,n){"use strict";var a,r=n(1),o=void 0;window.edasprefix="acm",window.globalConfig={isParentEdas:function(){return window.parent&&-1!==window.parent.location.host.indexOf("edas")}},r.b.middleWare(function(){var e=0=e.length?{value:void 0,done:!0}:(e=a(e,t),this._i+=e.length,{value:e,done:!1})})},function(e,t,n){var o=n(144),i=n(143);e.exports=function(r){return function(e,t){var n,e=String(i(e)),t=o(t),a=e.length;return t<0||a<=t?r?"":void 0:(n=e.charCodeAt(t))<55296||56319=e.length?(this._t=void 0,r(1)):r(0,"keys"==t?n:"values"==t?e[n]:[n,e[n]])},"values"),o.Arguments=o.Array,a("keys"),a("values"),a("entries")},function(e,t){e.exports=function(){}},function(e,t){e.exports=function(e,t){return{value:t,done:!!e}}},function(e,t,n){e.exports={default:n(481),__esModule:!0}},function(e,t,n){n(482),n(487),n(488),n(489),e.exports=n(76).Symbol},function(I,R,e){"use strict";function a(e){var t=L[e]=_(M[E]);return t._k=e,t}function n(e,t){m(e);for(var n,a=V(t=g(t)),r=0,o=a.length;rr;)s(L,t=n[r++])||t==x||t==H||a.push(t);return a}function i(e){for(var t,n=e===D,a=Q(n?T:g(e)),r=[],o=0;a.length>o;)!s(L,t=a[o++])||n&&!s(D,t)||r.push(L[t]);return r}var l=e(75),s=e(85),u=e(77),d=e(90),A=e(197),H=e(483).KEY,c=e(107),f=e(146),p=e(152),F=e(121),h=e(94),z=e(153),W=e(154),V=e(484),B=e(485),m=e(106),U=e(92),K=e(149),g=e(93),y=e(142),v=e(118),_=e(151),G=e(486),q=e(199),b=e(148),$=e(84),J=e(119),X=q.f,w=$.f,Q=G.f,M=l.Symbol,k=l.JSON,S=k&&k.stringify,E="prototype",x=h("_hidden"),Z=h("toPrimitive"),ee={}.propertyIsEnumerable,C=f("symbol-registry"),L=f("symbols"),T=f("op-symbols"),D=Object[E],f="function"==typeof M&&!!b.f,O=l.QObject,N=!O||!O[E]||!O[E].findChild,P=u&&c(function(){return 7!=_(w({},"a",{get:function(){return w(this,"a",{value:7}).a}})).a})?function(e,t,n){var a=X(D,t);a&&delete D[t],w(e,t,n),a&&e!==D&&w(D,t,a)}:w,j=f&&"symbol"==typeof M.iterator?function(e){return"symbol"==typeof e}:function(e){return e instanceof M},Y=function(e,t,n){return e===D&&Y(T,t,n),m(e),t=y(t,!0),m(n),s(L,t)?(n.enumerable?(s(e,x)&&e[x][t]&&(e[x][t]=!1),n=_(n,{enumerable:v(0,!1)})):(s(e,x)||w(e,x,v(1,{})),e[x][t]=!0),P(e,t,n)):w(e,t,n)};f||(A((M=function(){if(this instanceof M)throw TypeError("Symbol is not a constructor!");var t=F(0ne;)h(te[ne++]);for(var ae=J(h.store),re=0;ae.length>re;)W(ae[re++]);d(d.S+d.F*!f,"Symbol",{for:function(e){return s(C,e+="")?C[e]:C[e]=M(e)},keyFor:function(e){if(!j(e))throw TypeError(e+" is not a symbol!");for(var t in C)if(C[t]===e)return t},useSetter:function(){N=!0},useSimple:function(){N=!1}}),d(d.S+d.F*!f,"Object",{create:function(e,t){return void 0===t?_(e):n(_(e),t)},defineProperty:Y,defineProperties:n,getOwnPropertyDescriptor:r,getOwnPropertyNames:o,getOwnPropertySymbols:i});O=c(function(){b.f(1)});d(d.S+d.F*O,"Object",{getOwnPropertySymbols:function(e){return b.f(K(e))}}),k&&d(d.S+d.F*(!f||c(function(){var e=M();return"[null]"!=S([e])||"{}"!=S({a:e})||"{}"!=S(Object(e))})),"JSON",{stringify:function(e){for(var t,n,a=[e],r=1;ri;)o.call(e,a=r[i++])&&t.push(a);return t}},function(e,t,n){var a=n(195);e.exports=Array.isArray||function(e){return"Array"==a(e)}},function(e,t,n){var a=n(93),r=n(198).f,o={}.toString,i="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];e.exports.f=function(e){if(!i||"[object Window]"!=o.call(e))return r(a(e));try{return r(e)}catch(e){return i.slice()}}},function(e,t){},function(e,t,n){n(154)("asyncIterator")},function(e,t,n){n(154)("observable")},function(e,t,n){e.exports={default:n(491),__esModule:!0}},function(e,t,n){n(492),e.exports=n(76).Object.setPrototypeOf},function(e,t,n){var a=n(90);a(a.S,"Object",{setPrototypeOf:n(493).set})},function(e,t,r){function o(e,t){if(a(e),!n(t)&&null!==t)throw TypeError(t+": can't set as prototype!")}var n=r(92),a=r(106);e.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(e,n,a){try{(a=r(190)(Function.call,r(199).f(Object.prototype,"__proto__").set,2))(e,[]),n=!(e instanceof Array)}catch(e){n=!0}return function(e,t){return o(e,t),n?e.__proto__=t:a(e,t),e}}({},!1):void 0),check:o}},function(e,t,n){e.exports={default:n(495),__esModule:!0}},function(e,t,n){n(496);var a=n(76).Object;e.exports=function(e,t){return a.create(e,t)}},function(e,t,n){var a=n(90);a(a.S,"Object",{create:n(151)})},function(e,t,n){"use strict";var i=n(498);function a(){}function r(){}r.resetWarningCache=a,e.exports=function(){function e(e,t,n,a,r,o){if(o!==i)throw o=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types"),o.name="Invariant Violation",o}function t(){return e}var n={array:e.isRequired=e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:r,resetWarningCache:a};return n.PropTypes=n}},function(e,t,n){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},function(e,t,n){"use strict";function s(e,t,n,a){e.removeEventListener&&e.removeEventListener(t,n,a||!1)}function a(e,t,n,a){return e.addEventListener&&e.addEventListener(t,n,a||!1),{off:function(){return s(e,t,n,a)}}}t.__esModule=!0,t.on=a,t.once=function(r,o,i,l){return a(r,o,function e(){for(var t=arguments.length,n=Array(t),a=0;a68?1900:2e3)},r=function(t){return function(e){this[t]=+e}},o=[/[+-]\d\d:?(\d\d)?|Z/,function(e){(this.zone||(this.zone={})).offset=function(e){if(!e)return 0;if("Z"===e)return 0;var t=e.match(/([+-]|\d\d)/g),n=60*t[1]+(+t[2]||0);return 0===n?0:"+"===t[0]?-n:n}(e)}],i=function(e){var t=h[e];return t&&(t.indexOf?t:t.s.concat(t.f))},l=function(e,t){var n,a=h.meridiem;if(a){for(var r=1;r<=24;r+=1)if(e.indexOf(a(r,0,t))>-1){n=r>12;break}}else n=e===(t?"pm":"PM");return n},f={A:[n,function(e){this.afternoon=l(e,!1)}],a:[n,function(e){this.afternoon=l(e,!0)}],S:[/\d/,function(e){this.milliseconds=100*+e}],SS:[e,function(e){this.milliseconds=10*+e}],SSS:[/\d{3}/,function(e){this.milliseconds=+e}],s:[t,r("seconds")],ss:[t,r("seconds")],m:[t,r("minutes")],mm:[t,r("minutes")],H:[t,r("hours")],h:[t,r("hours")],HH:[t,r("hours")],hh:[t,r("hours")],D:[t,r("day")],DD:[e,r("day")],Do:[n,function(e){var t=h.ordinal,n=e.match(/\d+/);if(this.day=n[0],t)for(var a=1;a<=31;a+=1)t(a).replace(/\[|\]/g,"")===e&&(this.day=a)}],M:[t,r("month")],MM:[e,r("month")],MMM:[n,function(e){var t=i("months"),n=(i("monthsShort")||t.map(function(e){return e.slice(0,3)})).indexOf(e)+1;if(n<1)throw new Error;this.month=n%12||n}],MMMM:[n,function(e){var t=i("months").indexOf(e)+1;if(t<1)throw new Error;this.month=t%12||t}],Y:[/[+-]?\d+/,r("year")],YY:[e,function(e){this.year=a(e)}],YYYY:[/\d{4}/,r("year")],Z:o,ZZ:o};function b(e){var t,r;t=e,r=h&&h.formats;for(var u=(e=t.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g,function(e,t,n){var a=n&&n.toUpperCase();return t||r[n]||s[n]||r[a].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g,function(e,t,n){return t||n.slice(1)})})).match(c),d=u.length,n=0;n-1)return new Date(("X"===t?1e3:1)*e);var a=b(t)(e),r=a.year,o=a.month,i=a.day,l=a.hours,s=a.minutes,u=a.seconds,d=a.milliseconds,c=a.zone,f=new Date,p=i||(r||o?1:f.getDate()),h=r||f.getFullYear(),m=0;r&&!o||(m=o>0?o-1:f.getMonth());var g=l||0,y=s||0,v=u||0,_=d||0;return c?new Date(Date.UTC(h,m,p,g,y,v,_+60*c.offset*1e3)):n?new Date(Date.UTC(h,m,p,g,y,v,_)):new Date(h,m,p,g,y,v,_)}catch(e){return new Date("")}}(t,r,n),this.init(),s&&!0!==s&&(this.$L=this.locale(s).$L),l&&t!=this.format(r)&&(this.$d=new Date("")),h={}}else if(r instanceof Array)for(var u=r.length,d=1;d<=u;d+=1){a[1]=r[d-1];var c=f.apply(this,a);if(c.isValid()){this.$d=c.$d,this.$L=c.$L,this.init();break}d===u&&(this.$d=new Date(""))}else p.call(this,e)}}}()},function(e,t,n){e.exports=function(){"use strict";return function(e,t,a){a.updateLocale=function(e,t){var n=a.Ls[e];if(n)return(t?Object.keys(t):[]).forEach(function(e){n[e]=t[e]}),n}}}()},function(e,t,n){e.exports=function(e,t,n){function a(e,t,n,a,r){var e=e.name?e:e.$locale(),t=l(e[t]),n=l(e[n]),o=t||n.map(function(e){return e.slice(0,a)});if(!r)return o;var i=e.weekStart;return o.map(function(e,t){return o[(t+(i||0))%7]})}function r(){return n.Ls[n.locale()]}function o(e,t){return e.formats[t]||e.formats[t.toUpperCase()].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g,function(e,t,n){return t||n.slice(1)})}var t=t.prototype,l=function(e){return e&&(e.indexOf?e:e.s)};t.localeData=function(){return function(){var t=this;return{months:function(e){return e?e.format("MMMM"):a(t,"months")},monthsShort:function(e){return e?e.format("MMM"):a(t,"monthsShort","months",3)},firstDayOfWeek:function(){return t.$locale().weekStart||0},weekdays:function(e){return e?e.format("dddd"):a(t,"weekdays")},weekdaysMin:function(e){return e?e.format("dd"):a(t,"weekdaysMin","weekdays",2)},weekdaysShort:function(e){return e?e.format("ddd"):a(t,"weekdaysShort","weekdays",3)},longDateFormat:function(e){return o(t.$locale(),e)},meridiem:this.$locale().meridiem,ordinal:this.$locale().ordinal}}.bind(this)()},n.localeData=function(){var t=r();return{firstDayOfWeek:function(){return t.weekStart||0},weekdays:function(){return n.weekdays()},weekdaysShort:function(){return n.weekdaysShort()},weekdaysMin:function(){return n.weekdaysMin()},months:function(){return n.months()},monthsShort:function(){return n.monthsShort()},longDateFormat:function(e){return o(t,e)},meridiem:t.meridiem,ordinal:t.ordinal}},n.months=function(){return a(r(),"months")},n.monthsShort=function(){return a(r(),"monthsShort","months",3)},n.weekdays=function(e){return a(r(),"weekdays",null,null,e)},n.weekdaysShort=function(e){return a(r(),"weekdaysShort","weekdays",3,e)},n.weekdaysMin=function(e){return a(r(),"weekdaysMin","weekdays",2,e)}}},function(e,t,n){e.exports=function(){"use strict";var i="month",l="quarter";return function(e,t){var n=t.prototype;n.quarter=function(e){return this.$utils().u(e)?Math.ceil((this.month()+1)/3):this.month(this.month()%3+3*(e-1))};var a=n.add;n.add=function(e,t){return e=Number(e),this.$utils().p(t)===l?this.add(3*e,i):a.bind(this)(e,t)};var o=n.startOf;n.startOf=function(e,t){var n=this.$utils(),a=!!n.u(t)||t;if(n.p(e)===l){var r=this.quarter()-1;return a?this.month(3*r).startOf(i).startOf("day"):this.month(3*r+2).endOf(i).endOf("day")}return o.bind(this)(e,t)}}}()},function(e,t,n){e.exports=function(){"use strict";return function(e,t,n){var a=t.prototype,o=a.format;n.en.ordinal=function(e){var t=["th","st","nd","rd"],n=e%100;return"["+e+(t[(n-20)%10]||t[n]||t[0])+"]"},a.format=function(e){var t=this,n=this.$locale();if(!this.isValid())return o.bind(this)(e);var a=this.$utils(),r=(e||"YYYY-MM-DDTHH:mm:ssZ").replace(/\[([^\]]+)]|Q|wo|ww|w|WW|W|zzz|z|gggg|GGGG|Do|X|x|k{1,2}|S/g,function(e){switch(e){case"Q":return Math.ceil((t.$M+1)/3);case"Do":return n.ordinal(t.$D);case"gggg":return t.weekYear();case"GGGG":return t.isoWeekYear();case"wo":return n.ordinal(t.week(),"W");case"w":case"ww":return a.s(t.week(),"w"===e?1:2,"0");case"W":case"WW":return a.s(t.isoWeek(),"W"===e?1:2,"0");case"k":case"kk":return a.s(String(0===t.$H?24:t.$H),"k"===e?1:2,"0");case"X":return Math.floor(t.$d.getTime()/1e3);case"x":return t.$d.getTime();case"z":return"["+t.offsetName()+"]";case"zzz":return"["+t.offsetName("long")+"]";default:return e}});return o.bind(this)(r)}}}()},function(e,t,n){e.exports=function(){"use strict";var l="week",s="year";return function(e,t,i){var n=t.prototype;n.week=function(e){if(void 0===e&&(e=null),null!==e)return this.add(7*(e-this.week()),"day");var t=this.$locale().yearStart||1;if(11===this.month()&&this.date()>25){var n=i(this).startOf(s).add(1,s).date(t),a=i(this).endOf(l);if(n.isBefore(a))return 1}var r=i(this).startOf(s).date(t).startOf(l).subtract(1,"millisecond"),o=this.diff(r,l,!0);return o<0?i(this).startOf("week").week():Math.ceil(o)},n.weeks=function(e){return void 0===e&&(e=null),this.week(e)}}}()},function(e,t,n){e.exports=function(e){"use strict";function t(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var n=t(e),a={name:"zh-cn",weekdays:"ę˜ŸęœŸę—„_ꘟꜟäø€_ꘟꜟäŗŒ_ꘟꜟäø‰_ę˜ŸęœŸå››_ꘟꜟäŗ”_ę˜ŸęœŸå…­".split("_"),weekdaysShort:"å‘Øę—„_å‘Øäø€_å‘ØäŗŒ_å‘Øäø‰_å‘Ø四_å‘Øäŗ”_å‘Ø六".split("_"),weekdaysMin:"ę—„_äø€_äŗŒ_äø‰_四_äŗ”_六".split("_"),months:"äø€ęœˆ_äŗŒęœˆ_äø‰ęœˆ_å››ęœˆ_äŗ”ęœˆ_å…­ęœˆ_äøƒęœˆ_å…«ęœˆ_ä¹ęœˆ_åęœˆ_十äø€ęœˆ_十äŗŒęœˆ".split("_"),monthsShort:"1꜈_2꜈_3꜈_4꜈_5꜈_6꜈_7꜈_8꜈_9꜈_10꜈_11꜈_12꜈".split("_"),ordinal:function(e,t){return"W"===t?e+"å‘Ø":e+"ę—„"},weekStart:1,yearStart:4,formats:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY幓M꜈Dę—„",LLL:"YYYY幓M꜈Dę—„Ahē‚¹mm分",LLLL:"YYYY幓M꜈Dę—„ddddAhē‚¹mm分",l:"YYYY/M/D",ll:"YYYY幓M꜈Dę—„",lll:"YYYY幓M꜈Dę—„ HH:mm",llll:"YYYY幓M꜈Dę—„dddd HH:mm"},relativeTime:{future:"%s内",past:"%s前",s:"几ē§’",m:"1 分钟",mm:"%d 分钟",h:"1 å°ę—¶",hh:"%d å°ę—¶",d:"1 天",dd:"%d 天",M:"1 äøŖ꜈",MM:"%d äøŖ꜈",y:"1 幓",yy:"%d 幓"},meridiem:function(e,t){var n=100*e+t;return n<600?"凌ę™Ø":n<900?"ę—©äøŠ":n<1100?"äøŠåˆ":n<1300?"äø­åˆ":n<1800?"äø‹åˆ":"ꙚäøŠ"}};return n.default.locale(a,null,!0),a}(n(205))},function(e,t,n){"use strict";t.__esModule=!0,t.flex=t.transition=t.animation=void 0;var r=n(201),o=n(95);function a(e){if(!r.hasDOM)return!1;var n=document.createElement("div"),a=!1;return(0,o.each)(e,function(e,t){if(void 0!==n.style[t])return!(a={end:e})}),a}t.animation=a({WebkitAnimation:"webkitAnimationEnd",OAnimation:"oAnimationEnd",animation:"animationend"}),t.transition=a({WebkitTransition:"webkitTransitionEnd",OTransition:"oTransitionEnd",transition:"transitionend"}),t.flex=function(e){if(!r.hasDOM)return!1;var n=document.createElement("div"),a=!1;return(0,o.each)(e,function(e,t){return(0,o.each)(e,function(e){try{n.style[t]=e,a=a||n.style[t]===e}catch(e){}return!a}),!a}),a}({display:["flex","-webkit-flex","-moz-flex","-ms-flexbox"]})},function(e,t,n){"use strict";t.__esModule=!0,t.getFocusNodeList=i,t.saveLastFocusNode=function(){l=document.activeElement},t.clearLastFocusNode=function(){l=null},t.backLastFocusNode=function(){if(l)try{l.focus()}catch(e){}},t.limitTabRange=function(e,t){{var n,a;t.keyCode===r.default.TAB&&(e=i(e),n=e.length-1,-1<(a=e.indexOf(document.activeElement))&&(a=a+(t.shiftKey?-1:1),e[a=n<(a=a<0?n:a)?0:a].focus(),t.preventDefault()))}};var t=n(206),r=(t=t)&&t.__esModule?t:{default:t},a=n(95);function o(e){var t=e.nodeName.toLowerCase(),n=parseInt(e.getAttribute("tabindex"),10),n=!isNaN(n)&&-1a.height)&&(r[1]=-t.top-("t"===e?t.height:0))),r},this._getParentScrollOffset=function(e){var t=0,n=0;return e&&e.offsetParent&&e.offsetParent!==document.body&&(isNaN(e.offsetParent.scrollTop)||(t+=e.offsetParent.scrollTop),isNaN(e.offsetParent.scrollLeft)||(n+=e.offsetParent.scrollLeft)),{top:t,left:n}}};var h=a;function m(e){(0,o.default)(this,m),r.call(this),this.pinElement=e.pinElement,this.baseElement=e.baseElement,this.pinFollowBaseElementWhenFixed=e.pinFollowBaseElementWhenFixed,this.container=function(e){var t=e.container,e=e.baseElement;if(void 0===("undefined"==typeof document?"undefined":(0,i.default)(document)))return t;for(var n=(n=(0,l.default)(t,e))||document.body;"static"===y.dom.getStyle(n,"position");){if(!n||n===document.body)return document.body;n=n.parentNode}return n}(e),this.autoFit=e.autoFit||!1,this.align=e.align||"tl tl",this.offset=e.offset||[0,0],this.needAdjust=e.needAdjust||!1,this.isRtl=e.isRtl||!1}t.default=h,e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var w=a(n(2)),M=a(n(12)),k=n(0),S=a(k),E=a(n(13)),x=a(n(183)),C=a(n(78)),L=n(10);function a(e){return e&&e.__esModule?e:{default:e}}t.default=function(e){if(!k.useState||!k.useRef||!k.useEffect)return L.log.warning("need react version > 16.8.0"),null;var t=e.prefix,t=void 0===t?"next-":t,n=e.animation,a=void 0===n?{in:"expandInDown",out:"expandOutUp"}:n,r=e.visible,n=e.hasMask,o=e.align,i=e.points,o=void 0===i?o?o.split(" "):void 0:i,l=e.onPosition,i=e.children,s=e.className,u=e.style,d=e.wrapperClassName,c=e.beforeOpen,f=e.onOpen,p=e.afterOpen,h=e.beforeClose,m=e.onClose,g=e.afterClose,e=(0,M.default)(e,["prefix","animation","visible","hasMask","align","points","onPosition","children","className","style","wrapperClassName","beforeOpen","onOpen","afterOpen","beforeClose","onClose","afterClose"]),y=(0,k.useState)(!0),v=y[0],_=y[1],b=(0,k.useRef)(null),y=S.default.createElement(C.default.OverlayAnimate,{visible:r,animation:a,onEnter:function(){_(!1),"function"==typeof c&&c(b.current)},onEntering:function(){"function"==typeof f&&f(b.current)},onEntered:function(){"function"==typeof p&&p(b.current)},onExit:function(){"function"==typeof h&&h(b.current)},onExiting:function(){"function"==typeof m&&m(b.current)},onExited:function(){_(!0),"function"==typeof g&&g(b.current)},timeout:300,style:u},i?(0,k.cloneElement)(i,{className:(0,E.default)([t+"overlay-inner",s,i&&i.props&&i.props.className])}):S.default.createElement("span",null)),s=(0,E.default)(((u={})[t+"overlay-wrapper v2"]=!0,u[d]=d,u.opened=r,u));return S.default.createElement(x.default,(0,w.default)({},e,{visible:r,isAnimationEnd:v,hasMask:n,wrapperClassName:s,maskClassName:t+"overlay-backdrop",maskRender:function(e){return S.default.createElement(C.default.OverlayAnimate,{visible:r,animation:!!a&&{in:"fadeIn",out:"fadeOut"},timeout:300,unmountOnExit:!0},e)},points:o,onPosition:function(e){(0,w.default)(e,{align:e.config.points}),"function"==typeof l&&l(e)},ref:b}),y)},e.exports=t.default},function(n,e){function a(e,t){return n.exports=a=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},n.exports.__esModule=!0,n.exports.default=n.exports,a(e,t)}n.exports=a,n.exports.__esModule=!0,n.exports.default=n.exports},function(e,t,n){"use strict";t.__esModule=!0;var a,d=g(n(12)),c=g(n(2)),r=g(n(4)),o=g(n(5)),i=g(n(6)),s=n(0),f=g(s),p=n(22),l=n(30),u=g(n(3)),h=n(10),m=g(n(348));function g(e){return e&&e.__esModule?e:{default:e}}var y,n=h.func.noop,v=h.func.makeChain,_=h.func.bindCtx,u=(y=s.Component,(0,i.default)(b,y),b.getDerivedStateFromProps=function(e,t){return"visible"in e?(0,c.default)({},t,{visible:e.visible}):null},b.prototype.componentWillUnmount=function(){var t=this;["_timer","_hideTimer","_showTimer"].forEach(function(e){t[e]&&clearTimeout(t[e])})},b.prototype.handleVisibleChange=function(e,t,n){"visible"in this.props||this.setState({visible:e}),this.props.onVisibleChange(e,t,n)},b.prototype.handleTriggerClick=function(e){this.state.visible&&!this.props.canCloseByTrigger||this.handleVisibleChange(!this.state.visible,"fromTrigger",e)},b.prototype.handleTriggerKeyDown=function(e){var t=this.props.triggerClickKeycode;(Array.isArray(t)?t:[t]).includes(e.keyCode)&&(e.preventDefault(),this.handleTriggerClick(e))},b.prototype.handleTriggerMouseEnter=function(e){var t=this;this._mouseNotFirstOnMask=!1,this._hideTimer&&(clearTimeout(this._hideTimer),this._hideTimer=null),this._showTimer&&(clearTimeout(this._showTimer),this._showTimer=null),this.state.visible||(this._showTimer=setTimeout(function(){t.handleVisibleChange(!0,"fromTrigger",e)},this.props.delay))},b.prototype.handleTriggerMouseLeave=function(e,t){var n=this;this._showTimer&&(clearTimeout(this._showTimer),this._showTimer=null),this.state.visible&&(this._hideTimer=setTimeout(function(){n.handleVisibleChange(!1,t||"fromTrigger",e)},this.props.delay))},b.prototype.handleTriggerFocus=function(e){this.handleVisibleChange(!0,"fromTrigger",e)},b.prototype.handleTriggerBlur=function(e){this._isForwardContent||this.handleVisibleChange(!1,"fromTrigger",e),this._isForwardContent=!1},b.prototype.handleContentMouseDown=function(){this._isForwardContent=!0},b.prototype.handleContentMouseEnter=function(){clearTimeout(this._hideTimer)},b.prototype.handleContentMouseLeave=function(e){this.handleTriggerMouseLeave(e,"fromContent")},b.prototype.handleMaskMouseEnter=function(){this._mouseNotFirstOnMask||(clearTimeout(this._hideTimer),this._hideTimer=null,this._mouseNotFirstOnMask=!1)},b.prototype.handleMaskMouseLeave=function(){this._mouseNotFirstOnMask=!0},b.prototype.handleRequestClose=function(e,t){this.handleVisibleChange(!1,e,t)},b.prototype.renderTrigger=function(){var e,t,n,a,r,o,i,l=this,s=this.props,u=s.trigger,s=s.disabled,d={key:"trigger","aria-haspopup":!0,"aria-expanded":this.state.visible};return this.state.visible||(d["aria-describedby"]=void 0),s||(s=this.props.triggerType,s=Array.isArray(s)?s:[s],e=u&&u.props||{},t=e.onClick,n=e.onKeyDown,a=e.onMouseEnter,r=e.onMouseLeave,o=e.onFocus,i=e.onBlur,s.forEach(function(e){switch(e){case"click":d.onClick=v(l.handleTriggerClick,t),d.onKeyDown=v(l.handleTriggerKeyDown,n);break;case"hover":d.onMouseEnter=v(l.handleTriggerMouseEnter,a),d.onMouseLeave=v(l.handleTriggerMouseLeave,r);break;case"focus":d.onFocus=v(l.handleTriggerFocus,o),d.onBlur=v(l.handleTriggerBlur,i)}})),u&&f.default.cloneElement(u,d)},b.prototype.renderContent=function(){var t=this,e=this.props,n=e.children,e=e.triggerType,e=Array.isArray(e)?e:[e],n=s.Children.only(n),a=n.props,r=a.onMouseDown,o=a.onMouseEnter,i=a.onMouseLeave,l={key:"portal"};return e.forEach(function(e){switch(e){case"focus":l.onMouseDown=v(t.handleContentMouseDown,r);break;case"hover":l.onMouseEnter=v(t.handleContentMouseEnter,o),l.onMouseLeave=v(t.handleContentMouseLeave,i)}}),f.default.cloneElement(n,l)},b.prototype.renderPortal=function(){function e(){return(0,p.findDOMNode)(t)}var t=this,n=this.props,a=n.target,r=n.safeNode,o=n.followTrigger,i=n.triggerType,l=n.hasMask,s=n.wrapperStyle,n=(0,d.default)(n,["target","safeNode","followTrigger","triggerType","hasMask","wrapperStyle"]),u=this.props.container,r=Array.isArray(r)?[].concat(r):[r],s=(r.unshift(e),s||{});return o&&(u=function(e){return e&&e.parentNode||e},s.position="relative"),"hover"===i&&l&&(n.onMaskMouseEnter=this.handleMaskMouseEnter,n.onMaskMouseLeave=this.handleMaskMouseLeave),f.default.createElement(m.default,(0,c.default)({},n,{key:"overlay",ref:function(e){return t.overlay=e},visible:this.state.visible,target:a||e,container:u,safeNode:r,wrapperStyle:s,triggerType:i,hasMask:l,onRequestClose:this.handleRequestClose}),this.props.children&&this.renderContent())},b.prototype.render=function(){return[this.renderTrigger(),this.renderPortal()]},a=i=b,i.propTypes={children:u.default.node,trigger:u.default.element,triggerType:u.default.oneOfType([u.default.string,u.default.array]),triggerClickKeycode:u.default.oneOfType([u.default.number,u.default.array]),visible:u.default.bool,defaultVisible:u.default.bool,onVisibleChange:u.default.func,disabled:u.default.bool,autoFit:u.default.bool,delay:u.default.number,canCloseByTrigger:u.default.bool,target:u.default.any,safeNode:u.default.any,followTrigger:u.default.bool,container:u.default.any,hasMask:u.default.bool,wrapperStyle:u.default.object,rtl:u.default.bool,v2:u.default.bool,placement:u.default.string,placementOffset:u.default.number},i.defaultProps={triggerType:"hover",triggerClickKeycode:[h.KEYCODE.SPACE,h.KEYCODE.ENTER],defaultVisible:!1,onVisibleChange:n,disabled:!1,autoFit:!1,delay:200,canCloseByTrigger:!0,followTrigger:!1,container:function(){return document.body},rtl:!1},a);function b(e){(0,r.default)(this,b);var t=(0,o.default)(this,y.call(this,e));return t.state={visible:void 0===e.visible?e.defaultVisible:e.visible},_(t,["handleTriggerClick","handleTriggerKeyDown","handleTriggerMouseEnter","handleTriggerMouseLeave","handleTriggerFocus","handleTriggerBlur","handleContentMouseEnter","handleContentMouseLeave","handleContentMouseDown","handleRequestClose","handleMaskMouseEnter","handleMaskMouseLeave"]),t}u.displayName="Popup",t.default=(0,l.polyfill)(u),e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var T=a(n(2)),D=a(n(12)),O=n(0),N=a(O),P=a(n(13)),j=a(n(183)),Y=a(n(78)),I=n(10);function a(e){return e&&e.__esModule?e:{default:e}}t.default=function(r){if(!O.useState||!O.useRef||!O.useEffect)return I.log.warning("need react version > 16.8.0"),null;var e=r.prefix,e=void 0===e?"next-":e,t=r.animation,n=void 0===t?{in:"expandInDown",out:"expandOutUp"}:t,t=r.defaultVisible,a=r.onVisibleChange,o=void 0===a?function(){}:a,a=r.trigger,i=r.triggerType,i=void 0===i?"hover":i,l=r.overlay,s=r.onPosition,u=r.children,d=r.className,c=r.style,f=r.wrapperClassName,p=r.triggerClickKeycode,h=r.align,m=r.beforeOpen,g=r.onOpen,y=r.afterOpen,v=r.beforeClose,_=r.onClose,b=r.afterClose,w=(0,D.default)(r,["prefix","animation","defaultVisible","onVisibleChange","trigger","triggerType","overlay","onPosition","children","className","style","wrapperClassName","triggerClickKeycode","align","beforeOpen","onOpen","afterOpen","beforeClose","onClose","afterClose"]),t=(0,O.useState)(t),M=t[0],k=t[1],t=(0,O.useState)(n),S=t[0],E=t[1],t=(0,O.useState)(!0),x=t[0],C=t[1],L=(0,O.useRef)(null),t=((0,O.useEffect)(function(){"visible"in r&&k(r.visible)},[r.visible]),(0,O.useEffect)(function(){"animation"in r&&S!==n&&E(n)},[n]),l?u:a),a=l||u,a=N.default.createElement(Y.default.OverlayAnimate,{visible:M,animation:S,timeout:200,onEnter:function(){C(!1),"function"==typeof m&&m(L.current)},onEntering:function(){"function"==typeof g&&g(L.current)},onEntered:function(){"function"==typeof y&&y(L.current)},onExit:function(){"function"==typeof v&&v(L.current)},onExiting:function(){"function"==typeof _&&_(L.current)},onExited:function(){C(!0),"function"==typeof b&&b(L.current)},style:c},a?(0,O.cloneElement)(a,{className:(0,P.default)([e+"overlay-inner",d,a&&a.props&&a.props.className])}):N.default.createElement("span",null)),u=(0,P.default)(((l={})[e+"overlay-wrapper v2"]=!0,l[f]=f,l.opened=M,l)),c={};h&&(c.points=h.split(" "));return N.default.createElement(j.default.Popup,(0,T.default)({},w,c,{wrapperClassName:u,overlay:a,visible:M,isAnimationEnd:x,triggerType:i,onVisibleChange:function(e){for(var t=arguments.length,n=Array(1a&&y.shift(),i.default.render(d.default.createElement(l.default,l.default.getContext(),d.default.createElement(m,{dataSource:y})),g),{key:n,close:function(){r.timer&&clearTimeout(r.timer);var e=y.indexOf(r);-1 16.8.0")}},e.exports=t.default},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){"use strict";n(546)},function(e,t,n){},function(e,t,n){},function(e,t,n){"use strict";t.__esModule=!0,t.default=void 0;var p=s(n(2)),r=s(n(4)),o=s(n(5)),a=s(n(6)),h=s(n(37)),m=s(n(0)),i=s(n(3)),g=s(n(13)),y=n(10),l=s(n(26)),v=s(n(355));function s(e){return e&&e.__esModule?e:{default:e}}function _(e,r){var o=r.size,i=r.device,l=r.labelAlign,s=r.labelTextAlign,u=r.labelCol,d=r.wrapperCol,c=r.responsive,f=r.colon;return m.default.Children.map(e,function(e){return y.obj.isReactFragment(e)?_(e.props.children,r):e&&-1<["function","object"].indexOf((0,h.default)(e.type))&&"form_item"===e.type._typeMark?(t={labelCol:e.props.labelCol||u,wrapperCol:e.props.wrapperCol||d,labelAlign:e.props.labelAlign||("phone"===i?"top":l),labelTextAlign:e.props.labelTextAlign||s,colon:"colon"in e.props?e.props.colon:f,size:e.props.size||o,responsive:c},m.default.cloneElement(e,(n=t,a={},Object.keys(n).forEach(function(e){void 0!==n[e]&&(a[e]=n[e])}),a))):e;var t,n,a})}u=m.default.Component,(0,a.default)(b,u),b.prototype.getChildContext=function(){return{_formField:this.props.field||this._formField,_formSize:this.props.size,_formDisabled:this.props.disabled,_formPreview:this.props.isPreview,_formFullWidth:this.props.fullWidth,_formLabelForErrorMessage:this.props.useLabelForErrorMessage}},b.prototype.componentDidUpdate=function(e){var t=this.props;this._formField&&("value"in t&&t.value!==e.value&&this._formField.setValues(t.value),"error"in t&&t.error!==e.error&&this._formField.setValues(t.error))},b.prototype.render=function(){var e=this.props,t=e.className,n=e.inline,a=e.size,r=(e.device,e.labelAlign,e.labelTextAlign,e.onSubmit),o=e.children,i=(e.labelCol,e.wrapperCol,e.style),l=e.prefix,s=e.rtl,u=e.isPreview,d=e.component,c=e.responsive,f=e.gap,n=(e.colon,(0,g.default)(((e={})[l+"form"]=!0,e[l+"inline"]=n,e[""+l+a]=a,e[l+"form-responsive-grid"]=c,e[l+"form-preview"]=u,e[t]=!!t,e))),a=_(o,this.props);return m.default.createElement(d,(0,p.default)({role:"grid"},y.obj.pickOthers(b.propTypes,this.props),{className:n,style:i,dir:s?"rtl":void 0,onSubmit:r}),c?m.default.createElement(v.default,{gap:f},a):a)},a=n=b,n.propTypes={prefix:i.default.string,inline:i.default.bool,size:i.default.oneOf(["large","medium","small"]),fullWidth:i.default.bool,labelAlign:i.default.oneOf(["top","left","inset"]),labelTextAlign:i.default.oneOf(["left","right"]),field:i.default.any,saveField:i.default.func,labelCol:i.default.object,wrapperCol:i.default.object,onSubmit:i.default.func,children:i.default.any,className:i.default.string,style:i.default.object,value:i.default.object,onChange:i.default.func,component:i.default.oneOfType([i.default.string,i.default.func]),fieldOptions:i.default.object,rtl:i.default.bool,device:i.default.oneOf(["phone","tablet","desktop"]),responsive:i.default.bool,isPreview:i.default.bool,useLabelForErrorMessage:i.default.bool,colon:i.default.bool,disabled:i.default.bool,gap:i.default.oneOfType([i.default.arrayOf(i.default.number),i.default.number])},n.defaultProps={prefix:"next-",onSubmit:function(e){e.preventDefault()},size:"medium",labelAlign:"left",onChange:y.func.noop,component:"form",saveField:y.func.noop,device:"desktop",colon:!1,disabled:!1},n.childContextTypes={_formField:i.default.object,_formSize:i.default.string,_formDisabled:i.default.bool,_formPreview:i.default.bool,_formFullWidth:i.default.bool,_formLabelForErrorMessage:i.default.bool};var u,n=a;function b(e){(0,r.default)(this,b);var t,n,a=(0,o.default)(this,u.call(this,e));return a.onChange=function(e,t){a.props.onChange(a._formField.getValues(),{name:e,value:t,field:a._formField})},a._formField=null,!1!==e.field&&(t=(0,p.default)({},e.fieldOptions,{onChange:a.onChange}),e.field?(a._formField=e.field,n=a._formField.options.onChange,t.onChange=y.func.makeChain(n,a.onChange),a._formField.setOptions&&a._formField.setOptions(t)):("value"in e&&(t.values=e.value),a._formField=new l.default(a,t)),e.locale&&e.locale.Validate&&a._formField.setOptions({messages:e.locale.Validate}),e.saveField(a._formField)),a}n.displayName="Form",t.default=n,e.exports=t.default},function(e,t,n){"use strict";var a=n(86),m=(Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,a(n(160))),i=a(n(551)),r=a(n(162)),o=a(n(161)),b=a(n(163)),w=a(n(72)),l=a(n(352)),s=a(n(353)),g=a(n(557)),M=n(566),u={state:"",valueName:"value",trigger:"onChange",inputValues:[]},a=function(){function a(e){var t=this,n=1e.length)&&(t=e.length);for(var n=0,a=new Array(t);n=a.length)return n;var o=a[r],n=e(t&&t[o],n,a,r+1);return t?Array.isArray(t)?((a=[].concat(t))[o]=n,a):(0,s.default)({},t,(0,i.default)({},o,n)):((r=isNaN(o)?{}:[])[o]=n,r)};t=function(){};void 0!==e&&e.env,n.warning=t}.call(this,r(354))},function(e,t,n){"use strict";t.__esModule=!0,t.cloneAndAddKey=function(e){{var t;if(e&&(0,a.isValidElement)(e))return t=e.key||"error",(0,a.cloneElement)(e,{key:t})}return e},t.scrollToFirstError=function(e){var t=e.errorsGroup,n=e.options,a=e.instance;if(t&&n.scrollToFirstError){var r,o=void 0,i=void 0;for(r in t)if(t.hasOwnProperty(r)){var l=u.default.findDOMNode(a[r]);if(!l)return;var s=l.offsetTop;(void 0===i||s), use instead of.'),S.default.cloneElement(e,{className:t,size:c||C(r)})):(0,k.isValidElement)(e)?e:S.default.createElement("span",{className:a+"btn-helper"},e)}),t=d,_=(0,b.default)({},x.obj.pickOthers(Object.keys(L.propTypes),e),{type:o,disabled:p,onClick:h,className:(0,E.default)(n)});return"button"!==t&&(delete _.type,_.disabled&&(delete _.onClick,_.href&&delete _.href)),S.default.createElement(t,(0,b.default)({},_,{dir:g?"rtl":void 0,onMouseUp:this.onMouseUp,ref:this.buttonRefHandler}),l,u)},a=n=L,n.propTypes=(0,b.default)({},l.default.propTypes,{prefix:r.default.string,rtl:r.default.bool,type:r.default.oneOf(["primary","secondary","normal"]),size:r.default.oneOf(["small","medium","large"]),icons:r.default.shape({loading:r.default.node}),iconSize:r.default.oneOfType([r.default.oneOf(["xxs","xs","small","medium","large","xl","xxl","xxxl","inherit"]),r.default.number]),htmlType:r.default.oneOf(["submit","reset","button"]),component:r.default.oneOf(["button","a","div","span"]),loading:r.default.bool,ghost:r.default.oneOf([!0,!1,"light","dark"]),text:r.default.bool,warning:r.default.bool,disabled:r.default.bool,onClick:r.default.func,className:r.default.string,onMouseUp:r.default.func,children:r.default.node}),n.defaultProps={prefix:"next-",type:"normal",size:"medium",icons:{},htmlType:"button",component:"button",loading:!1,ghost:!1,text:!1,warning:!1,disabled:!1,onClick:function(){}};var u,l=a;function L(){var e,t;(0,o.default)(this,L);for(var n=arguments.length,a=Array(n),r=0;ra[r])return!0;if(n[r] 0, or `null`');if(W(i,"numericSeparator")&&"boolean"!=typeof i.numericSeparator)throw new TypeError('option "numericSeparator", if provided, must be `true` or `false`');var t=i.numericSeparator;if(void 0===n)return"undefined";if(null===n)return"null";if("boolean"==typeof n)return n?"true":"false";if("string"==typeof n)return function e(t,n){if(t.length>n.maxStringLength)return a=t.length-n.maxStringLength,a="... "+a+" more character"+(1"}if(H(n)){if(0===n.length)return"[]";var v=$(n,h);return l&&!function(e){for(var t=0;t "+h(e,n))}),G("Map",_.call(n),u,l)):function(e){if(b&&e&&"object"==typeof e)try{b.call(e);try{_.call(e)}catch(e){return 1}return e instanceof Set}catch(e){}return}(n)?(d=[],X.call(n,function(e){d.push(h(e,n))}),G("Set",b.call(n),d,l)):function(e){if(w&&e&&"object"==typeof e)try{w.call(e,w);try{M.call(e,M)}catch(e){return 1}return e instanceof WeakMap}catch(e){}return}(n)?K("WeakMap"):function(e){if(M&&e&&"object"==typeof e)try{M.call(e,M);try{w.call(e,w)}catch(e){return 1}return e instanceof WeakSet}catch(e){}return}(n)?K("WeakSet"):function(e){if(k&&e&&"object"==typeof e)try{return k.call(e),1}catch(e){}return}(n)?K("WeakRef"):"[object Number]"!==V(c=n)||N&&"object"==typeof c&&N in c?function(e){if(e&&"object"==typeof e&&T)try{return T.call(e),1}catch(e){}return}(n)?U(h(T.call(n))):"[object Boolean]"!==V(t=n)||N&&"object"==typeof t&&N in t?"[object String]"!==V(e=n)||N&&"object"==typeof e&&N in e?("[object Date]"!==V(t=n)||N&&"object"==typeof t&&N in t)&&!F(n)?(e=$(n,h),t=j?j(n)===Object.prototype:n instanceof Object||n.constructor===Object,f=n instanceof Object?"":"null prototype",p=!t&&N&&Object(n)===n&&N in n?S.call(V(n),8,-1):f?"Object":"",t=(!t&&"function"==typeof n.constructor&&n.constructor.name?n.constructor.name+" ":"")+(p||f?"["+L.call(C.call([],p||[],f||[]),": ")+"] ":""),0===e.length?t+"{}":l?t+"{"+q(e,l)+"}":t+"{ "+L.call(e,", ")+" }"):String(n):U(h(String(n))):U(Q.call(n)):U(h(Number(n)))};var s=Object.prototype.hasOwnProperty||function(e){return e in this};function W(e,t){return s.call(e,t)}function V(e){return i.call(e)}function B(e,t){if(e.indexOf)return e.indexOf(t);for(var n=0,a=e.length;n, as child."),a.push(t),e.props.children&&(t.children=n(e.props.children)))}),a}(e.children):t},N.prototype.fetchInfoFromBinaryChildren=function(e){function r(e,t){return t=t||0,e.forEach(function(e){e.children?t=r(e.children,t):t+=1}),t}var a=!1,o=[],i=[],e=(function t(){var e=0r.tRight&&(e=r.tRight,r.changedPageX=r.tRight-r.startLeft),e-r.cellLefto.clientHeight,o.scrollWidth,o.clientWidth,o={},e||(o[r]=0,o[a]=0),+i&&(o.marginBottom=-i,o.paddingBottom=i,e&&(o[a]=i)),h.dom.setStyle(this.headerNode,o)),n&&!this.props.lockType&&this.headerNode&&(r=this.headerNode.querySelector("."+t+"table-header-fixer"),e=h.dom.getStyle(this.headerNode,"height"),a=h.dom.getStyle(this.headerNode,"paddingBottom"),h.dom.setStyle(r,{width:i,height:e-a}))},o.prototype.render=function(){var e=this.props,t=e.components,n=e.className,a=e.prefix,r=e.fixedHeader,o=e.lockType,i=e.dataSource,e=(e.maxBodyHeight,(0,u.default)(e,["components","className","prefix","fixedHeader","lockType","dataSource","maxBodyHeight"]));return r&&((t=(0,s.default)({},t)).Header||(t.Header=m.default),t.Body||(t.Body=g.default),t.Wrapper||(t.Wrapper=y.default),n=(0,p.default)(((r={})[a+"table-fixed"]=!0,r[a+"table-wrap-empty"]=!i.length,r[n]=n,r))),c.default.createElement(l,(0,s.default)({},e,{dataSource:i,lockType:o,components:t,className:n,prefix:a}))},o}(c.default.Component),n.FixedHeader=m.default,n.FixedBody=g.default,n.FixedWrapper=y.default,n.propTypes=(0,s.default)({hasHeader:r.default.bool,fixedHeader:r.default.bool,maxBodyHeight:r.default.oneOfType([r.default.number,r.default.string])},l.propTypes),n.defaultProps=(0,s.default)({},l.defaultProps,{hasHeader:!0,fixedHeader:!1,maxBodyHeight:200,components:{},refs:{},prefix:"next-"}),n.childContextTypes={fixedHeader:r.default.bool,getNode:r.default.func,onFixedScrollSync:r.default.func,getTableInstanceForFixed:r.default.func,maxBodyHeight:r.default.oneOfType([r.default.number,r.default.string])};var t,n=t;return n.displayName="FixedTable",(0,o.statics)(n,l),n};var c=l(n(0)),r=l(n(3)),f=n(22),p=l(n(13)),h=n(10),m=l(n(128)),g=l(n(390)),y=l(n(129)),o=n(66);function l(e){return e&&e.__esModule?e:{default:e}}e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var i=o(n(12)),f=o(n(2)),r=o(n(4)),l=o(n(5)),s=o(n(6)),u=(t.default=function(o){e=t=function(n){function a(e,t){(0,r.default)(this,a);var c=(0,l.default)(this,n.call(this,e,t));return c.addSelection=function(e){var t=c.props,n=t.prefix,a=t.rowSelection,t=t.size,a=a.columnProps&&a.columnProps()||{};e.find(function(e){return"selection"===e.key})||e.unshift((0,f.default)({key:"selection",title:c.renderSelectionHeader.bind(c),cell:c.renderSelectionBody.bind(c),width:"small"===t?34:50,className:n+"table-selection "+n+"table-prerow",__normalized:!0},a))},c.renderSelectionHeader=function(){var e=c.selectAllRow,t={},n=c.props,a=n.rowSelection,r=n.primaryKey,o=n.dataSource,i=n.entireDataSource,n=n.locale,l=c.state.selectedRowKeys,s=a.mode||"multiple",u=!!l.length,d=!1,i=(c.flatDataSource(i||o).filter(function(e,t){return!a.getProps||!(a.getProps(e,t)||{}).disabled}).map(function(e){return e[r]}).forEach(function(e){-1===l.indexOf(e)?u=!1:d=!0}),t.onClick=b(function(e){e.stopPropagation()},t.onClick),a.titleProps&&a.titleProps()||{});return u&&(d=!1),["multiple"===s?p.default.createElement(h.default,(0,f.default)({key:"_total",indeterminate:d,"aria-label":n.selectAll,checked:u,onChange:e},t,i)):null,a.titleAddons&&a.titleAddons()]},c.renderSelectionBody=function(e,t,n){var a=c.props,r=a.rowSelection,a=a.primaryKey,o=c.state.selectedRowKeys,i=r.mode||"multiple",o=-1s.length&&(u=o),w(u.filter(function(e){return-1=Math.max(a-y,0)&&oc.clientHeight;this.isLock()?(e=this.bodyLeftNode,t=this.bodyRightNode,n=this.getWrapperNode("right"),a=f?d:0,c=c.offsetHeight-d,f||(r[s]=0,r[u]=0),+d?(r.marginBottom=-d,r.paddingBottom=d):(r.marginBottom=-20,r.paddingBottom=20),c={"max-height":c},o||+d||(c[u]=0),+d&&(c[u]=-d),e&&g.dom.setStyle(e,c),t&&g.dom.setStyle(t,c),n&&+d&&g.dom.setStyle(n,i?"left":"right",a+"px")):(r.marginBottom=-d,r.paddingBottom=d,r[u]=0,f||(r[s]=0)),l&&g.dom.setStyle(l,r)},a.prototype.adjustHeaderSize=function(){var o=this;this.isLock()&&this.tableInc.groupChildren.forEach(function(e,t){var n=o.tableInc.groupChildren[t].length-1,n=o.getHeaderCellNode(t,n),a=o.getHeaderCellNode(t,0),r=o.getHeaderCellNode(t,0,"right"),t=o.getHeaderCellNode(t,0,"left");n&&r&&(n=n.offsetHeight,g.dom.setStyle(r,"height",n),setTimeout(function(){var e=o.tableRightInc.affixRef;return e&&e.getInstance()&&e.getInstance().updatePosition()})),a&&t&&(r=a.offsetHeight,g.dom.setStyle(t,"height",r),setTimeout(function(){var e=o.tableLeftInc.affixRef;return e&&e.getInstance()&&e.getInstance().updatePosition()}))})},a.prototype.adjustRowHeight=function(){var n=this;this.isLock()&&this.tableInc.props.dataSource.forEach(function(e,t){t=""+("object"===(void 0===e?"undefined":(0,r.default)(e))&&"__rowIndex"in e?e.__rowIndex:t)+(e.__expanded?"_expanded":"");n.setRowHeight(t,"left"),n.setRowHeight(t,"right")})},a.prototype.setRowHeight=function(e,t){var t=this.getRowNode(e,t),e=this.getRowNode(e),e=(M?e&&e.offsetHeight:e&&parseFloat(getComputedStyle(e).height))||"auto",n=(M?t&&t.offsetHeight:t&&parseFloat(getComputedStyle(t).height))||"auto";t&&e!==n&&g.dom.setStyle(t,"height",e)},a.prototype.getWrapperNode=function(e){e=e?e.charAt(0).toUpperCase()+e.substr(1):"";try{return(0,u.findDOMNode)(this["lock"+e+"El"])}catch(e){return null}},a.prototype.getRowNode=function(e,t){t=this["table"+(t=t?t.charAt(0).toUpperCase()+t.substr(1):"")+"Inc"];try{return(0,u.findDOMNode)(t.getRowRef(e))}catch(e){return null}},a.prototype.getHeaderCellNode=function(e,t,n){n=this["table"+(n=n?n.charAt(0).toUpperCase()+n.substr(1):"")+"Inc"];try{return(0,u.findDOMNode)(n.getHeaderCellRef(e,t))}catch(e){return null}},a.prototype.getCellNode=function(e,t,n){n=this["table"+(n=n?n.charAt(0).toUpperCase()+n.substr(1):"")+"Inc"];try{return(0,u.findDOMNode)(n.getCellRef(e,t))}catch(e){return null}},a.prototype.render=function(){var e,t=this.props,n=(t.children,t.columns,t.prefix),a=t.components,r=t.className,o=t.dataSource,i=t.tableWidth,t=(0,f.default)(t,["children","columns","prefix","components","className","dataSource","tableWidth"]),l=this.normalizeChildrenState(this.props),s=l.lockLeftChildren,u=l.lockRightChildren,l=l.children,d={left:this.getFlatenChildrenLength(s),right:this.getFlatenChildrenLength(u),origin:this.getFlatenChildrenLength(l)};return this._notNeedAdjustLockLeft&&(s=[]),this._notNeedAdjustLockRight&&(u=[]),this.lockLeftChildren=s,this.lockRightChildren=u,this.isOriginLock()?((a=(0,p.default)({},a)).Body=a.Body||v.default,a.Header=a.Header||_.default,a.Wrapper=a.Wrapper||b.default,a.Row=a.Row||y.default,r=(0,m.default)(((e={})[n+"table-lock"]=!0,e[n+"table-wrap-empty"]=!o.length,e[r]=r,e)),e=[h.default.createElement(c,(0,p.default)({},t,{dataSource:o,key:"lock-left",columns:s,className:n+"table-lock-left",lengths:d,prefix:n,lockType:"left",components:a,ref:this.saveLockLeftRef,loading:!1,"aria-hidden":!0})),h.default.createElement(c,(0,p.default)({},t,{dataSource:o,key:"lock-right",columns:u,className:n+"table-lock-right",lengths:d,prefix:n,lockType:"right",components:a,ref:this.saveLockRightRef,loading:!1,"aria-hidden":!0}))],h.default.createElement(c,(0,p.default)({},t,{tableWidth:i,dataSource:o,columns:l,prefix:n,lengths:d,wrapperContent:e,components:a,className:r}))):h.default.createElement(c,this.props)},a}(h.default.Component),t.LockRow=y.default,t.LockBody=v.default,t.LockHeader=_.default,t.propTypes=(0,p.default)({scrollToCol:a.default.number,scrollToRow:a.default.number},c.propTypes),t.defaultProps=(0,p.default)({},c.defaultProps),t.childContextTypes={getTableInstance:a.default.func,getLockNode:a.default.func,onLockBodyScroll:a.default.func,onRowMouseEnter:a.default.func,onRowMouseLeave:a.default.func};var e,t=e;return t.displayName="LockTable",(0,w.statics)(t,c),t},n(0)),h=c(s),u=n(22),a=c(n(3)),m=c(n(13)),d=c(n(172)),g=n(10),y=c(n(174)),v=c(n(391)),_=c(n(392)),b=c(n(129)),w=n(66);function c(e){return e&&e.__esModule?e:{default:e}}var M=g.env.ieVersion;function k(e){return function n(e){return e.map(function(e){var t=(0,p.default)({},e);return e.children&&(e.children=n(e.children)),t})}(e)}e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var s=l(n(12)),u=l(n(2)),r=l(n(4)),o=l(n(5)),i=l(n(6)),d=(t.default=function(l){e=t=function(n){function a(e,t){(0,r.default)(this,a);var d=(0,o.default)(this,n.call(this,e));return d.state={},d.updateOffsetArr=function(){var e=d.splitChildren||{},t=e.lockLeftChildren,n=e.lockRightChildren,e=e.originChildren,a=d.getFlatenChildren(t).length,r=d.getFlatenChildren(n).length,e=a+r+d.getFlatenChildren(e).length,a=0r.top-e.offset?(t?(s.position="absolute",s.top=a-(r.top-e.offset),u="relative"):(s.position="fixed",s.top=e.offset+n.top),d._setAffixStyle(s,!0),d._setContainerStyle(l)):e.bottom&&a![^\\|>]*)\\s+)?(?\\||>)(?\\+|\\-|\\d+|\\+\\d+|\\-\\d+|\\d+\\+|\\d+\\-)?(? +#.*)?$"),R.prototype.PATTERN_FOLDED_SCALAR_END=new p("(?\\||>)(?\\+|\\-|\\d+|\\+\\d+|\\-\\d+|\\d+\\+|\\d+\\-)?(? +#.*)?$"),R.prototype.PATTERN_SEQUENCE_ITEM=new p("^\\-((?\\s+)(?.+?))?\\s*$"),R.prototype.PATTERN_ANCHOR_VALUE=new p("^&(?[^ ]+) *(?.*)"),R.prototype.PATTERN_COMPACT_NOTATION=new p("^(?"+j.REGEX_QUOTED_STRING+"|[^ '\"\\{\\[].*?) *\\:(\\s+(?.+?))?\\s*$"),R.prototype.PATTERN_MAPPING_ITEM=new p("^(?"+j.REGEX_QUOTED_STRING+"|[^ '\"\\[\\{].*?) *\\:(\\s+(?.+?))?\\s*$"),R.prototype.PATTERN_DECIMAL=new p("\\d+"),R.prototype.PATTERN_INDENT_SPACES=new p("^ +"),R.prototype.PATTERN_TRAILING_LINES=new p("(\n*)$"),R.prototype.PATTERN_YAML_HEADER=new p("^\\%YAML[: ][\\d\\.]+.*\n","m"),R.prototype.PATTERN_LEADING_COMMENTS=new p("^(\\#.*?\n)+","m"),R.prototype.PATTERN_DOCUMENT_MARKER_START=new p("^\\-\\-\\-.*?\n","m"),R.prototype.PATTERN_DOCUMENT_MARKER_END=new p("^\\.\\.\\.\\s*$","m"),R.prototype.PATTERN_FOLDED_SCALAR_BY_INDENTATION={},R.prototype.CONTEXT_NONE=0,R.prototype.CONTEXT_SEQUENCE=1,R.prototype.CONTEXT_MAPPING=2,R.prototype.parse=function(e,t,n){var a,r,o,i,l,s,u,d,c,f,p,h,m,g,y,v,_,b,w,M,k,S,E,x,C,L,T,D,O,N,P;for(null==t&&(t=!1),null==n&&(n=null),this.currentLineNb=-1,this.currentLine="",this.lines=this.cleanup(e).split("\n"),i=null,o=this.CONTEXT_NONE,r=!1;this.moveToNextLine();)if(!this.isCurrentLineEmpty()){if("\t"===this.currentLine[0])throw new Y("A YAML file cannot contain tabs as indentation.",this.getRealCurrentLineNb()+1,this.currentLine);if(d=k=!1,P=this.PATTERN_SEQUENCE_ITEM.exec(this.currentLine)){if(this.CONTEXT_MAPPING===o)throw new Y("You cannot define a sequence item when in a mapping");o=this.CONTEXT_SEQUENCE,null==i&&(i=[]),null!=P.value&&(M=this.PATTERN_ANCHOR_VALUE.exec(P.value))&&(d=M.ref,P.value=M.value),null==P.value||""===I.trim(P.value," ")||0===I.ltrim(P.value," ").indexOf("#")?this.currentLineNb=this.lines.length-1)&&(this.currentLine=this.lines[++this.currentLineNb],!0)},R.prototype.moveToPreviousLine=function(){this.currentLine=this.lines[--this.currentLineNb]},R.prototype.parseValue=function(t,e,n){var a,r,o,i;if(0===t.indexOf("*")){if(t=-1!==(r=t.indexOf("#"))?t.substr(1,r-2):t.slice(1),void 0===this.refs[t])throw new Y('Reference "'+t+'" does not exist.',this.currentLine);return this.refs[t]}if(r=this.PATTERN_FOLDED_SCALAR_ALL.exec(t))return i=null!=(i=r.modifiers)?i:"",o=Math.abs(parseInt(i)),isNaN(o)&&(o=0),i=this.parseFoldedScalar(r.separator,this.PATTERN_DECIMAL.replace(i,""),o),null!=r.type?(j.configure(e,n),j.parseScalar(r.type+" "+i)):i;if("["!==(o=t.charAt(0))&&"{"!==o&&'"'!==o&&"'"!==o)return this.isNextLineIndented()&&(t+="\n"+this.getNextEmbedBlock()),j.parse(t,e,n);for(;;)try{return j.parse(t,e,n)}catch(e){if(!((a=e)instanceof l&&this.moveToNextLine()))throw a.parsedLine=this.getRealCurrentLineNb()+1,a.snippet=this.currentLine,a;t+="\n"+I.trim(this.currentLine," ")}},R.prototype.parseFoldedScalar=function(e,t,n){var a,r,o,i,l,s,u,d,c,f;if(null==t&&(t=""),null==n&&(n=0),!(u=this.moveToNextLine()))return"";for(a=this.isCurrentLineBlank(),f="";u&&a;)(u=this.moveToNextLine())&&(f+="\n",a=this.isCurrentLineBlank());if(0<(n=0===n&&(l=this.PATTERN_INDENT_SPACES.exec(this.currentLine))?l[0].length:n))for(null==(d=this.PATTERN_FOLDED_SCALAR_BY_INDENTATION[n])&&(d=new p("^ {"+n+"}(.*)$"),R.prototype.PATTERN_FOLDED_SCALAR_BY_INDENTATION[n]=d);u&&(a||(l=d.exec(this.currentLine)));)f+=a?this.currentLine.slice(n):l[1],(u=this.moveToNextLine())&&(f+="\n",a=this.isCurrentLineBlank());else u&&(f+="\n");if(u&&this.moveToPreviousLine(),">"===e){for(s="",r=0,o=(c=f.split("\n")).length;rn&&(e=!0),this.moveToPreviousLine(),e)},R.prototype.isCurrentLineEmpty=function(){var e=I.trim(this.currentLine," ");return 0===e.length||"#"===e.charAt(0)},R.prototype.isCurrentLineBlank=function(){return""===I.trim(this.currentLine," ")},R.prototype.isCurrentLineComment=function(){return"#"===I.ltrim(this.currentLine," ").charAt(0)},R.prototype.cleanup=function(e){var t,n,a,r,o,i,l,s,u,d,c;for(-1!==e.indexOf("\r")&&(e=e.split("\r\n").join("\n").split("\r").join("\n")),e=(u=this.PATTERN_YAML_HEADER.replaceAll(e,""))[0],u=u[1],this.offset+=u,c=(u=this.PATTERN_LEADING_COMMENTS.replaceAll(e,"",1))[0],1===u[1]&&(this.offset+=I.subStrCount(e,"\n")-I.subStrCount(c,"\n"),e=c),c=(u=this.PATTERN_DOCUMENT_MARKER_START.replaceAll(e,"",1))[0],1===u[1]&&(this.offset+=I.subStrCount(e,"\n")-I.subStrCount(c,"\n"),e=c,e=this.PATTERN_DOCUMENT_MARKER_END.replace(e,"")),d=-1,a=0,o=(s=e.split("\n")).length;a=!%@`]"),r.requiresDoubleQuoting=function(e){return this.PATTERN_CHARACTERS_TO_ESCAPE.test(e)},r.escapeWithDoubleQuotes=function(e){var t;return'"'+this.PATTERN_MAPPING_ESCAPEES.replace(e,(t=this,function(e){return t.MAPPING_ESCAPEES_TO_ESCAPED[e]}))+'"'},r.requiresSingleQuoting=function(e){return this.PATTERN_SINGLE_QUOTING.test(e)},r.escapeWithSingleQuotes=function(e){return"'"+e.replace(/'/g,"''")+"'"},e.exports=r},function(e,t){var i={}.hasOwnProperty,n=function(e){var t,n=o,a=e;for(t in a)i.call(a,t)&&(n[t]=a[t]);function r(){this.constructor=n}function o(e,t,n){this.message=e,this.parsedLine=t,this.snippet=n}return r.prototype=a.prototype,n.prototype=new r,n.__super__=a.prototype,o.prototype.toString=function(){return null!=this.parsedLine&&null!=this.snippet?" "+this.message+" (line "+this.parsedLine+": '"+this.snippet+"')":" "+this.message},o}(Error);e.exports=n},function(e,t,n){var f,p;function a(){}p=n(111),f=n(396),a.indentation=4,a.prototype.dump=function(e,t,n,a,r){var o,i,l,s,u,d,c;if(null==t&&(t=0),null==a&&(a=!1),null==r&&(r=null),s="",u=(n=null==n?0:n)?p.strRepeat(" ",n):"",t<=0||"object"!=typeof e||e instanceof Date||p.isEmpty(e))s+=u+f.dump(e,a,r);else if(e instanceof Array)for(o=0,l=e.length;ou&&!c&&(o=o.slice(0,u),e=C.default.createElement(m.default,{key:"_count",type:"primary",size:p,animation:!1},d(a,t))),0=D.KEYCODE.LEFT&&t<=D.KEYCODE.DOWN&&e.preventDefault(),e=void 0,t===D.KEYCODE.RIGHT||t===D.KEYCODE.DOWN?(e=o.getNextActiveKey(!0),o.handleTriggerEvent(o.props.triggerType,e)):t!==D.KEYCODE.LEFT&&t!==D.KEYCODE.UP||(e=o.getNextActiveKey(!1),o.handleTriggerEvent(o.props.triggerType,e)))},o.state={activeKey:o.getDefaultActiveKey(e)},o}l.displayName="Tab",t.default=(0,s.polyfill)(l),e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var h=f(n(2)),a=f(n(4)),r=f(n(5)),o=f(n(6)),m=f(n(0)),i=n(22),l=f(n(3)),g=f(n(13)),s=f(n(24)),u=f(n(61)),d=f(n(50)),y=f(n(78)),v=n(10),c=n(402);function f(e){return e&&e.__esModule?e:{default:e}}var p,_={float:"right",zIndex:1},b={float:"left",zIndex:1},w={dropdown:"arrow-down",prev:"arrow-left",next:"arrow-right"},M=u.default.Popup,u=(p=m.default.Component,(0,o.default)(k,p),k.prototype.componentDidMount=function(){this.props.animation||this.initialSettings(),v.events.on(window,"resize",this.onWindowResized)},k.prototype.componentDidUpdate=function(e){var t=this;clearTimeout(this.scrollTimer),this.scrollTimer=setTimeout(function(){t.scrollToActiveTab()},410),clearTimeout(this.slideTimer),this.slideTimer=setTimeout(function(){t.setSlideBtn()},410),"dropdown"!==this.props.excessMode||(0,c.tabsArrayShallowEqual)(this.props.tabs,e.tabs)||this.getDropdownItems(this.props)},k.prototype.componentWillUnmount=function(){v.events.off(window,"resize",this.onWindowResized)},k.prototype.initialSettings=function(){this.setSlideBtn(),this.getDropdownItems(this.props)},k.prototype.setOffset=function(e){var t=!(1n&&(t.current=n),this.setState(t),this.props.onPageSizeChange(e)},I.prototype.renderPageTotal=function(){var e=this.props,t=e.prefix,n=e.total,e=e.totalRender,a=this.state,r=a.currentPageSize,a=a.current;return N.default.createElement("div",{className:t+"pagination-total"},e(n,[(a-1)*r+1,a*r]))},I.prototype.renderPageItem=function(e){var t=this.props,n=t.prefix,a=t.size,r=t.link,o=t.pageNumberRender,i=t.total,l=t.pageSize,t=t.locale,s=this.state.current,i=Y(i,l),l=parseInt(e,10)===s,a={size:a,className:(0,P.default)(((s={})[n+"pagination-item"]=!0,s[n+"current"]=l,s)),onClick:l?m:this.onPageItemClick.bind(this,e)};return r&&(a.component="a",a.href=r.replace("{page}",e)),N.default.createElement(c.default,(0,O.default)({"aria-label":j.str.template(t.total,{current:e,total:i})},a,{key:e}),o(e))},I.prototype.renderPageFirst=function(e){var t=this.props,n=t.prefix,a=t.size,r=t.shape,t=t.locale,a={disabled:e<=1,size:a,className:(0,P.default)(((a={})[n+"pagination-item"]=!0,a[n+"prev"]=!0,a)),onClick:this.onPageItemClick.bind(this,e-1)},n=N.default.createElement(d.default,{type:"arrow-left",className:n+"pagination-icon-prev"});return N.default.createElement(c.default,(0,O.default)({},a,{"aria-label":j.str.template(t.labelPrev,{current:e})}),n,"arrow-only"===r||"arrow-prev-only"===r||"no-border"===r?"":t.prev)},I.prototype.renderPageLast=function(e,t){var n=this.props,a=n.prefix,r=n.size,o=n.shape,n=n.locale,r={disabled:t<=e,size:r,className:(0,P.default)(((t={})[a+"pagination-item"]=!0,t[a+"next"]=!0,t)),onClick:this.onPageItemClick.bind(this,e+1)},t=N.default.createElement(d.default,{type:"arrow-right",className:a+"pagination-icon-next"});return N.default.createElement(c.default,(0,O.default)({},r,{"aria-label":j.str.template(n.labelNext,{current:e})}),"arrow-only"===o||"no-border"===o?"":n.next,t)},I.prototype.renderPageEllipsis=function(e){var t=this.props.prefix;return N.default.createElement(d.default,{className:t+"pagination-ellipsis "+t+"pagination-icon-ellipsis",type:"ellipsis",key:"ellipsis-"+e})},I.prototype.renderPageJump=function(){var t=this,e=this.props,n=e.prefix,a=e.size,e=e.locale,r=this.state.inputValue;return[N.default.createElement("span",{className:n+"pagination-jump-text"},e.goTo),N.default.createElement(f.default,{className:n+"pagination-jump-input",type:"text","aria-label":e.inputAriaLabel,size:a,value:r,onChange:this.onInputChange.bind(this),onKeyDown:function(e){e.keyCode===j.KEYCODE.ENTER&&t.handleJump(e)}}),N.default.createElement("span",{className:n+"pagination-jump-text"},e.page),N.default.createElement(c.default,{className:n+"pagination-jump-go",size:a,onClick:this.handleJump},e.go)]},I.prototype.renderPageDisplay=function(e,t){var n=this.props,a=n.prefix,n=n.pageNumberRender;return N.default.createElement("span",{className:a+"pagination-display"},N.default.createElement("em",null,n(e)),"/",n(t))},I.prototype.renderPageList=function(e,t){var n=this.props,a=n.prefix,n=n.pageShowCount,r=[];if(t<=n)for(var o=1;o<=t;o++)r.push(this.renderPageItem(o));else{var n=n-3,i=parseInt(n/2,10),l=void 0,s=void 0;r.push(this.renderPageItem(1)),s=e+i,(l=e-i)<=1&&(s=(l=2)+n),2=e.length&&-1=this.props.children.length?(this.update(this.props),this.changeSlide({message:"index",index:this.props.children.length-this.props.slidesToShow,currentSlide:this.state.currentSlide})):s.obj.shallowEqual(e,this.props)||this.update(this.props),this.adaptHeight()},p.prototype.componentWillUnmount=function(){this.animationEndCallback&&clearTimeout(this.animationEndCallback),s.events.off(window,"resize",this.onWindowResized),this.state.autoPlayTimer&&clearInterval(this.state.autoPlayTimer)},p.prototype.onWindowResized=function(){this.update(this.props),this.setState({animating:!1}),clearTimeout(this.animationEndCallback),delete this.animationEndCallback},p.prototype.slickGoTo=function(e){"number"==typeof e&&this.changeSlide({message:"index",index:e,currentSlide:this.state.currentSlide})},p.prototype.onEnterArrow=function(e){this.arrowHoverHandler(e)},p.prototype.onLeaveArrow=function(){this.arrowHoverHandler()},p.prototype._instanceRefHandler=function(e,t){this[e]=t},p.prototype.render=function(){var e=this.props,t=e.prefix,n=e.animation,a=e.arrows,r=e.arrowSize,o=e.arrowPosition,i=e.arrowDirection,l=e.dots,s=e.dotsClass,u=e.cssEase,d=e.speed,c=e.infinite,f=e.centerMode,p=e.centerPadding,h=e.lazyLoad,m=e.dotsDirection,g=e.rtl,y=e.slidesToShow,v=e.slidesToScroll,_=e.variableWidth,b=e.vertical,w=e.verticalSwiping,M=e.focusOnSelect,k=e.children,S=e.dotsRender,e=e.triggerType,E=this.state,x=E.currentSlide,C=E.lazyLoadedList,L=E.slideCount,T=E.slideWidth,D=E.slideHeight,O=E.trackStyle,N=E.listHeight,E=E.dragging,u={prefix:t,animation:n,cssEase:u,speed:d,infinite:c,centerMode:f,focusOnSelect:M?this.selectHandler:null,currentSlide:x,lazyLoad:h,lazyLoadedList:C,rtl:g,slideWidth:T,slideHeight:D,slidesToShow:y,slidesToScroll:v,slideCount:L,trackStyle:O,variableWidth:_,vertical:b,verticalSwiping:w,triggerType:e},d=void 0,h=(!0===l&&yt.startX?1:-1),!0===this.props.verticalSwiping&&(t.swipeLength=Math.round(Math.sqrt(Math.pow(t.curY-t.startY,2))),a=t.curY>t.startY?1:-1),r=this.state.currentSlide,l=Math.ceil(this.state.slideCount/this.props.slidesToScroll),o=this.swipeDirection(this.state.touchObject),i=t.swipeLength,!1===this.props.infinite&&(0===r&&"right"===o||l<=r+1&&"left"===o)&&(i=t.swipeLength*this.props.edgeFriction,!1===this.state.edgeDragged&&this.props.edgeEvent&&(this.props.edgeEvent(o),this.setState({edgeDragged:!0}))),!1===this.state.swiped&&this.props.swipeEvent&&(this.props.swipeEvent(o),this.setState({swiped:!0})),this.setState({touchObject:t,swipeLeft:l=n+i*a,trackStyle:(0,u.getTrackCSS)((0,s.default)({left:l},this.props,this.state))}),Math.abs(t.curX-t.startX)<.8*Math.abs(t.curY-t.startY)||4t[t.length-1])e=t[t.length-1];else for(var a in t){if(e-1*n.state.swipeLeft)return t=e,!1}else if(e.offsetLeft-a+(n.getWidth(e)||0)/2>-1*n.state.swipeLeft)return t=e,!1;return!0}),Math.abs(t.dataset.index-this.state.currentSlide)||1):this.props.slidesToScroll},swipeEnd:function(e){if(this.state.dragging){var t=this.state.touchObject,n=this.state.listWidth/this.props.touchThreshold,a=this.swipeDirection(t);if(this.props.verticalSwiping&&(n=this.state.listHeight/this.props.touchThreshold),this.setState({dragging:!1,edgeDragged:!1,swiped:!1,swipeLeft:null,touchObject:{}}),t.swipeLength)if(t.swipeLength>n){e.preventDefault();var r=void 0,o=void 0;switch(a){case"left":case"down":o=this.state.currentSlide+this.getSlideCount(),r=this.props.swipeToSlide?this.checkNavigable(o):o,this.setState({currentDirection:0});break;case"right":case"up":o=this.state.currentSlide-this.getSlideCount(),r=this.props.swipeToSlide?this.checkNavigable(o):o,this.setState({currentDirection:1});break;default:r=this.state.currentSlide}this.slideHandler(r)}else{t=(0,u.getTrackLeft)((0,s.default)({slideIndex:this.state.currentSlide,trackRef:this.track},this.props,this.state));this.setState({trackStyle:(0,u.getTrackAnimateCSS)((0,s.default)({left:t},this.props,this.state))})}}else this.props.swipe&&e.preventDefault()},onInnerSliderEnter:function(){this.props.autoplay&&this.props.pauseOnHover&&this.pause()},onInnerSliderLeave:function(){this.props.autoplay&&this.props.pauseOnHover&&this.autoPlay()}},e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var g=a(n(2)),d=a(n(0)),c=a(n(22)),y=n(405);function a(e){return e&&e.__esModule?e:{default:e}}t.default={initialize:function(t){var n=this,e=c.default.findDOMNode(this.list),a=d.default.Children.count(t.children),r=this.getWidth(e)||0,o=this.getWidth(c.default.findDOMNode(this.track))||0,i=void 0,e=(i=t.vertical?r:(r-(t.centerMode&&2*parseInt(t.centerPadding)))/t.slidesToShow,this.getHeight(e.querySelector('[data-index="0"]'))||0),l=e*t.slidesToShow,s=t.slidesToShow||1,u="activeIndex"in t?t.activeIndex:t.defaultActiveIndex,s=t.rtl?a-1-(s-1)-u:u;this.setState({slideCount:a,slideWidth:i,listWidth:r,trackWidth:o,currentSlide:s,slideHeight:e,listHeight:l},function(){var e=(0,y.getTrackLeft)((0,g.default)({slideIndex:n.state.currentSlide,trackRef:n.track},t,n.state)),e=(0,y.getTrackCSS)((0,g.default)({left:e},t,n.state));n.setState({trackStyle:e}),n.autoPlay()})},update:function(e){this.initialize(e)},getWidth:function(e){return"clientWidth"in e?e.clientWidth:e&&e.getBoundingClientRect().width},getHeight:function(e){return"clientHeight"in e?e.clientHeight:e&&e.getBoundingClientRect().height},adaptHeight:function(){var e,t;this.props.adaptiveHeight&&(t='[data-index="'+this.state.currentSlide+'"]',this.list&&(t=(e=c.default.findDOMNode(this.list)).querySelector(t).offsetHeight,e.style.height=t+"px"))},canGoNext:function(e){var t=!0;return e.infinite||(e.centerMode?e.currentSlide>=e.slideCount-1&&(t=!1):(e.slideCount<=e.slidesToShow||e.currentSlide>=e.slideCount-e.slidesToShow)&&(t=!1)),t},slideHandler:function(e){var t=this,n=this.props.rtl,a=void 0,r=void 0,o=void 0;if(!this.props.waitForAnimate||!this.state.animating){if("fade"===this.props.animation)return r=this.state.currentSlide,!1===this.props.infinite&&(e<0||e>=this.state.slideCount)?void 0:(a=e<0?e+this.state.slideCount:e>=this.state.slideCount?e-this.state.slideCount:e,this.props.lazyLoad&&this.state.lazyLoadedList.indexOf(a)<0&&this.setState({lazyLoadedList:this.state.lazyLoadedList.concat(a)}),o=function(){t.setState({animating:!1}),t.props.onChange(a),delete t.animationEndCallback},this.props.onBeforeChange(this.state.currentSlide,a),this.setState({animating:!0,currentSlide:a},function(){this.animationEndCallback=setTimeout(o,this.props.speed+20)}),void this.autoPlay());a=e,n?a<0?!1===this.props.infinite?r=0:this.state.slideCount%this.props.slidesToScroll!=0?a+this.props.slidesToScroll<=0?(r=this.state.slideCount+a,a=this.state.slideCount-this.props.slidesToScroll):r=a=0:r=this.state.slideCount+a:r=a>=this.state.slideCount?!1===this.props.infinite?this.state.slideCount-this.props.slidesToShow:this.state.slideCount%this.props.slidesToScroll!=0?0:a-this.state.slideCount:a:r=a<0?!1===this.props.infinite?0:this.state.slideCount%this.props.slidesToScroll!=0?this.state.slideCount-this.state.slideCount%this.props.slidesToScroll:this.state.slideCount+a:a>=this.state.slideCount?!1===this.props.infinite?this.state.slideCount-this.props.slidesToShow:this.state.slideCount%this.props.slidesToScroll!=0?0:a-this.state.slideCount:a;var i,e=(0,y.getTrackLeft)((0,g.default)({slideIndex:a,trackRef:this.track},this.props,this.state)),l=(0,y.getTrackLeft)((0,g.default)({slideIndex:r,trackRef:this.track},this.props,this.state));if(!1===this.props.infinite&&(e=l),this.props.lazyLoad){for(var s=!0,u=[],d=this.state.slideCount,c=a<0?d+a:r,f=c;f=s.activeIndex?"visible":"hidden",d.transition="opacity "+s.speed+"ms "+s.cssEase,d.WebkitTransition="opacity "+s.speed+"ms "+s.cssEase,s.vertical?d.top=-s.activeIndex*s.slideHeight:d.left=-s.activeIndex*s.slideWidth),s.vertical&&(d.width="100%"),d),u=(d=(0,v.default)({activeIndex:e},c),a=d.prefix,u=r=i=void 0,o=(u=d.rtl?d.slideCount-1-d.activeIndex:d.activeIndex)<0||u>=d.slideCount,d.centerMode?(n=Math.floor(d.slidesToShow/2),r=(u-d.currentSlide)%d.slideCount==0,u>d.currentSlide-n-1&&u<=d.currentSlide+n&&(i=!0)):i=d.currentSlide<=u&&u=u,u=(0,k.default)(((v={})[o+"upload-list-item"]=!0,v[o+"hidden"]=u,v)),v=this.props.children||i.card.addPhoto,c=r?S.func.prevent:c,_=S.obj.pickOthers(C.propTypes,this.props),b=S.obj.pickOthers(E.default.propTypes,_);if(h&&"function"==typeof m)return e=(0,k.default)(((e={})[o+"form-preview"]=!0,e[l]=!!l,e)),M.default.createElement("div",{style:s,className:e},m(this.state.value,this.props));return M.default.createElement(E.default,(0,w.default)({className:l,style:s,listType:"card",closable:!0,locale:i,value:this.state.value,onRemove:c,onCancel:f,onPreview:d,itemRender:g,isPreview:h,uploader:this.uploaderRef,reUpload:y,showDownload:n},_),M.default.createElement(x.default,(0,w.default)({},b,{shape:"card",prefix:o,disabled:r,action:a,timeout:p,isPreview:h,value:this.state.value,onProgress:this.onProgress,onChange:this.onChange,ref:function(e){return t.saveRef(e)},className:u}),v))},d=n=C,n.displayName="Card",n.propTypes={prefix:l.default.string,locale:l.default.object,children:l.default.object,value:l.default.oneOfType([l.default.array,l.default.object]),defaultValue:l.default.oneOfType([l.default.array,l.default.object]),onPreview:l.default.func,onChange:l.default.func,onRemove:l.default.func,onCancel:l.default.func,itemRender:l.default.func,reUpload:l.default.bool,showDownload:l.default.bool,onProgress:l.default.func,isPreview:l.default.bool,renderPreview:l.default.func},n.defaultProps={prefix:"next-",locale:u.default.Upload,showDownload:!0,onChange:S.func.noop,onPreview:S.func.noop,onProgress:S.func.noop},a=function(){var n=this;this.onProgress=function(e,t){n.setState({value:e}),n.props.onProgress(e,t)},this.onChange=function(e,t){"value"in n.props||n.setState({value:e}),n.props.onChange(e,t)}};var f,i=d;function C(e){(0,r.default)(this,C);var t=(0,o.default)(this,f.call(this,e)),n=(a.call(t),void 0),n="value"in e?e.value:e.defaultValue;return t.state={value:Array.isArray(n)?n:[],uploaderRef:t.uploaderRef},t}t.default=(0,s.polyfill)(i),e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var u=m(n(2)),d=m(n(12)),o=m(n(4)),i=m(n(5)),a=m(n(6)),c=m(n(0)),r=m(n(3)),f=m(n(13)),p=m(n(24)),l=n(10),s=m(n(44)),h=m(n(180));function m(e){return e&&e.__esModule?e:{default:e}}g=c.default.Component,(0,a.default)(y,g),y.prototype.abort=function(e){this.uploaderRef.abort(e)},y.prototype.startUpload=function(){this.uploaderRef.startUpload()},y.prototype.render=function(){var e=this.props,t=e.className,n=e.style,a=e.shape,r=e.locale,o=e.prefix,i=e.listType,e=(0,d.default)(e,["className","style","shape","locale","prefix","listType"]),l=o+"upload-drag",t=(0,f.default)(((s={})[l]=!0,s[l+"-over"]=this.state.dragOver,s[t]=!!t,s)),s=this.props.children||c.default.createElement("div",{className:t},c.default.createElement("p",{className:l+"-icon"},c.default.createElement(p.default,{size:"large",className:l+"-upload-icon"})),c.default.createElement("p",{className:l+"-text"},r.drag.text),c.default.createElement("p",{className:l+"-hint"},r.drag.hint));return c.default.createElement(h.default,(0,u.default)({},e,{prefix:o,shape:a,listType:i,dragable:!0,style:n,onDragOver:this.onDragOver,onDragLeave:this.onDragLeave,onDrop:this.onDrop,ref:this.saveUploaderRef}),s)},a=n=y,n.propTypes={prefix:r.default.string,locale:r.default.object,shape:r.default.string,onDragOver:r.default.func,onDragLeave:r.default.func,onDrop:r.default.func,limit:r.default.number,className:r.default.string,style:r.default.object,defaultValue:r.default.array,children:r.default.node,listType:r.default.string,timeout:r.default.number},n.defaultProps={prefix:"next-",onDragOver:l.func.noop,onDragLeave:l.func.noop,onDrop:l.func.noop,locale:s.default.Upload};var g,r=a;function y(){var e,t;(0,o.default)(this,y);for(var n=arguments.length,a=Array(n),r=0;r=e.length?{value:void 0,done:!0}:(e=a(e,t),this._i+=e.length,{value:e,done:!1})})},function(e,t,n){var o=n(144),i=n(143);e.exports=function(r){return function(e,t){var n,e=String(i(e)),t=o(t),a=e.length;return t<0||a<=t?r?"":void 0:(n=e.charCodeAt(t))<55296||56319=e.length?(this._t=void 0,r(1)):r(0,"keys"==t?n:"values"==t?e[n]:[n,e[n]])},"values"),o.Arguments=o.Array,a("keys"),a("values"),a("entries")},function(e,t){e.exports=function(){}},function(e,t){e.exports=function(e,t){return{value:t,done:!!e}}},function(e,t,n){e.exports={default:n(481),__esModule:!0}},function(e,t,n){n(482),n(487),n(488),n(489),e.exports=n(77).Symbol},function(I,R,e){"use strict";function a(e){var t=L[e]=_(M[E]);return t._k=e,t}function n(e,t){m(e);for(var n,a=V(t=g(t)),r=0,o=a.length;rr;)s(L,t=n[r++])||t==x||t==H||a.push(t);return a}function i(e){for(var t,n=e===D,a=Q(n?T:g(e)),r=[],o=0;a.length>o;)!s(L,t=a[o++])||n&&!s(D,t)||r.push(L[t]);return r}var l=e(76),s=e(85),u=e(78),d=e(91),A=e(197),H=e(483).KEY,c=e(108),f=e(146),p=e(152),F=e(122),h=e(95),z=e(153),W=e(154),V=e(484),B=e(485),m=e(107),U=e(93),K=e(149),g=e(94),y=e(142),v=e(119),_=e(151),G=e(486),q=e(199),b=e(148),$=e(84),J=e(120),X=q.f,w=$.f,Q=G.f,M=l.Symbol,k=l.JSON,S=k&&k.stringify,E="prototype",x=h("_hidden"),Z=h("toPrimitive"),ee={}.propertyIsEnumerable,C=f("symbol-registry"),L=f("symbols"),T=f("op-symbols"),D=Object[E],f="function"==typeof M&&!!b.f,O=l.QObject,N=!O||!O[E]||!O[E].findChild,P=u&&c(function(){return 7!=_(w({},"a",{get:function(){return w(this,"a",{value:7}).a}})).a})?function(e,t,n){var a=X(D,t);a&&delete D[t],w(e,t,n),a&&e!==D&&w(D,t,a)}:w,j=f&&"symbol"==typeof M.iterator?function(e){return"symbol"==typeof e}:function(e){return e instanceof M},Y=function(e,t,n){return e===D&&Y(T,t,n),m(e),t=y(t,!0),m(n),s(L,t)?(n.enumerable?(s(e,x)&&e[x][t]&&(e[x][t]=!1),n=_(n,{enumerable:v(0,!1)})):(s(e,x)||w(e,x,v(1,{})),e[x][t]=!0),P(e,t,n)):w(e,t,n)};f||(A((M=function(){if(this instanceof M)throw TypeError("Symbol is not a constructor!");var t=F(0ne;)h(te[ne++]);for(var ae=J(h.store),re=0;ae.length>re;)W(ae[re++]);d(d.S+d.F*!f,"Symbol",{for:function(e){return s(C,e+="")?C[e]:C[e]=M(e)},keyFor:function(e){if(!j(e))throw TypeError(e+" is not a symbol!");for(var t in C)if(C[t]===e)return t},useSetter:function(){N=!0},useSimple:function(){N=!1}}),d(d.S+d.F*!f,"Object",{create:function(e,t){return void 0===t?_(e):n(_(e),t)},defineProperty:Y,defineProperties:n,getOwnPropertyDescriptor:r,getOwnPropertyNames:o,getOwnPropertySymbols:i});O=c(function(){b.f(1)});d(d.S+d.F*O,"Object",{getOwnPropertySymbols:function(e){return b.f(K(e))}}),k&&d(d.S+d.F*(!f||c(function(){var e=M();return"[null]"!=S([e])||"{}"!=S({a:e})||"{}"!=S(Object(e))})),"JSON",{stringify:function(e){for(var t,n,a=[e],r=1;ri;)o.call(e,a=r[i++])&&t.push(a);return t}},function(e,t,n){var a=n(195);e.exports=Array.isArray||function(e){return"Array"==a(e)}},function(e,t,n){var a=n(94),r=n(198).f,o={}.toString,i="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];e.exports.f=function(e){if(!i||"[object Window]"!=o.call(e))return r(a(e));try{return r(e)}catch(e){return i.slice()}}},function(e,t){},function(e,t,n){n(154)("asyncIterator")},function(e,t,n){n(154)("observable")},function(e,t,n){e.exports={default:n(491),__esModule:!0}},function(e,t,n){n(492),e.exports=n(77).Object.setPrototypeOf},function(e,t,n){var a=n(91);a(a.S,"Object",{setPrototypeOf:n(493).set})},function(e,t,r){function o(e,t){if(a(e),!n(t)&&null!==t)throw TypeError(t+": can't set as prototype!")}var n=r(93),a=r(107);e.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(e,n,a){try{(a=r(190)(Function.call,r(199).f(Object.prototype,"__proto__").set,2))(e,[]),n=!(e instanceof Array)}catch(e){n=!0}return function(e,t){return o(e,t),n?e.__proto__=t:a(e,t),e}}({},!1):void 0),check:o}},function(e,t,n){e.exports={default:n(495),__esModule:!0}},function(e,t,n){n(496);var a=n(77).Object;e.exports=function(e,t){return a.create(e,t)}},function(e,t,n){var a=n(91);a(a.S,"Object",{create:n(151)})},function(e,t,n){"use strict";var i=n(498);function a(){}function r(){}r.resetWarningCache=a,e.exports=function(){function e(e,t,n,a,r,o){if(o!==i)throw o=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types"),o.name="Invariant Violation",o}function t(){return e}var n={array:e.isRequired=e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:r,resetWarningCache:a};return n.PropTypes=n}},function(e,t,n){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},function(e,t,n){"use strict";function s(e,t,n,a){e.removeEventListener&&e.removeEventListener(t,n,a||!1)}function a(e,t,n,a){return e.addEventListener&&e.addEventListener(t,n,a||!1),{off:function(){return s(e,t,n,a)}}}t.__esModule=!0,t.on=a,t.once=function(r,o,i,l){return a(r,o,function e(){for(var t=arguments.length,n=Array(t),a=0;a68?1900:2e3)},r=function(t){return function(e){this[t]=+e}},o=[/[+-]\d\d:?(\d\d)?|Z/,function(e){(this.zone||(this.zone={})).offset=function(e){if(!e)return 0;if("Z"===e)return 0;var t=e.match(/([+-]|\d\d)/g),n=60*t[1]+(+t[2]||0);return 0===n?0:"+"===t[0]?-n:n}(e)}],i=function(e){var t=h[e];return t&&(t.indexOf?t:t.s.concat(t.f))},l=function(e,t){var n,a=h.meridiem;if(a){for(var r=1;r<=24;r+=1)if(e.indexOf(a(r,0,t))>-1){n=r>12;break}}else n=e===(t?"pm":"PM");return n},f={A:[n,function(e){this.afternoon=l(e,!1)}],a:[n,function(e){this.afternoon=l(e,!0)}],S:[/\d/,function(e){this.milliseconds=100*+e}],SS:[e,function(e){this.milliseconds=10*+e}],SSS:[/\d{3}/,function(e){this.milliseconds=+e}],s:[t,r("seconds")],ss:[t,r("seconds")],m:[t,r("minutes")],mm:[t,r("minutes")],H:[t,r("hours")],h:[t,r("hours")],HH:[t,r("hours")],hh:[t,r("hours")],D:[t,r("day")],DD:[e,r("day")],Do:[n,function(e){var t=h.ordinal,n=e.match(/\d+/);if(this.day=n[0],t)for(var a=1;a<=31;a+=1)t(a).replace(/\[|\]/g,"")===e&&(this.day=a)}],M:[t,r("month")],MM:[e,r("month")],MMM:[n,function(e){var t=i("months"),n=(i("monthsShort")||t.map(function(e){return e.slice(0,3)})).indexOf(e)+1;if(n<1)throw new Error;this.month=n%12||n}],MMMM:[n,function(e){var t=i("months").indexOf(e)+1;if(t<1)throw new Error;this.month=t%12||t}],Y:[/[+-]?\d+/,r("year")],YY:[e,function(e){this.year=a(e)}],YYYY:[/\d{4}/,r("year")],Z:o,ZZ:o};function b(e){var t,r;t=e,r=h&&h.formats;for(var u=(e=t.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g,function(e,t,n){var a=n&&n.toUpperCase();return t||r[n]||s[n]||r[a].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g,function(e,t,n){return t||n.slice(1)})})).match(c),d=u.length,n=0;n-1)return new Date(("X"===t?1e3:1)*e);var a=b(t)(e),r=a.year,o=a.month,i=a.day,l=a.hours,s=a.minutes,u=a.seconds,d=a.milliseconds,c=a.zone,f=new Date,p=i||(r||o?1:f.getDate()),h=r||f.getFullYear(),m=0;r&&!o||(m=o>0?o-1:f.getMonth());var g=l||0,y=s||0,v=u||0,_=d||0;return c?new Date(Date.UTC(h,m,p,g,y,v,_+60*c.offset*1e3)):n?new Date(Date.UTC(h,m,p,g,y,v,_)):new Date(h,m,p,g,y,v,_)}catch(e){return new Date("")}}(t,r,n),this.init(),s&&!0!==s&&(this.$L=this.locale(s).$L),l&&t!=this.format(r)&&(this.$d=new Date("")),h={}}else if(r instanceof Array)for(var u=r.length,d=1;d<=u;d+=1){a[1]=r[d-1];var c=f.apply(this,a);if(c.isValid()){this.$d=c.$d,this.$L=c.$L,this.init();break}d===u&&(this.$d=new Date(""))}else p.call(this,e)}}}()},function(e,t,n){e.exports=function(){"use strict";return function(e,t,a){a.updateLocale=function(e,t){var n=a.Ls[e];if(n)return(t?Object.keys(t):[]).forEach(function(e){n[e]=t[e]}),n}}}()},function(e,t,n){e.exports=function(e,t,n){function a(e,t,n,a,r){var e=e.name?e:e.$locale(),t=l(e[t]),n=l(e[n]),o=t||n.map(function(e){return e.slice(0,a)});if(!r)return o;var i=e.weekStart;return o.map(function(e,t){return o[(t+(i||0))%7]})}function r(){return n.Ls[n.locale()]}function o(e,t){return e.formats[t]||e.formats[t.toUpperCase()].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g,function(e,t,n){return t||n.slice(1)})}var t=t.prototype,l=function(e){return e&&(e.indexOf?e:e.s)};t.localeData=function(){return function(){var t=this;return{months:function(e){return e?e.format("MMMM"):a(t,"months")},monthsShort:function(e){return e?e.format("MMM"):a(t,"monthsShort","months",3)},firstDayOfWeek:function(){return t.$locale().weekStart||0},weekdays:function(e){return e?e.format("dddd"):a(t,"weekdays")},weekdaysMin:function(e){return e?e.format("dd"):a(t,"weekdaysMin","weekdays",2)},weekdaysShort:function(e){return e?e.format("ddd"):a(t,"weekdaysShort","weekdays",3)},longDateFormat:function(e){return o(t.$locale(),e)},meridiem:this.$locale().meridiem,ordinal:this.$locale().ordinal}}.bind(this)()},n.localeData=function(){var t=r();return{firstDayOfWeek:function(){return t.weekStart||0},weekdays:function(){return n.weekdays()},weekdaysShort:function(){return n.weekdaysShort()},weekdaysMin:function(){return n.weekdaysMin()},months:function(){return n.months()},monthsShort:function(){return n.monthsShort()},longDateFormat:function(e){return o(t,e)},meridiem:t.meridiem,ordinal:t.ordinal}},n.months=function(){return a(r(),"months")},n.monthsShort=function(){return a(r(),"monthsShort","months",3)},n.weekdays=function(e){return a(r(),"weekdays",null,null,e)},n.weekdaysShort=function(e){return a(r(),"weekdaysShort","weekdays",3,e)},n.weekdaysMin=function(e){return a(r(),"weekdaysMin","weekdays",2,e)}}},function(e,t,n){e.exports=function(){"use strict";var i="month",l="quarter";return function(e,t){var n=t.prototype;n.quarter=function(e){return this.$utils().u(e)?Math.ceil((this.month()+1)/3):this.month(this.month()%3+3*(e-1))};var a=n.add;n.add=function(e,t){return e=Number(e),this.$utils().p(t)===l?this.add(3*e,i):a.bind(this)(e,t)};var o=n.startOf;n.startOf=function(e,t){var n=this.$utils(),a=!!n.u(t)||t;if(n.p(e)===l){var r=this.quarter()-1;return a?this.month(3*r).startOf(i).startOf("day"):this.month(3*r+2).endOf(i).endOf("day")}return o.bind(this)(e,t)}}}()},function(e,t,n){e.exports=function(){"use strict";return function(e,t,n){var a=t.prototype,o=a.format;n.en.ordinal=function(e){var t=["th","st","nd","rd"],n=e%100;return"["+e+(t[(n-20)%10]||t[n]||t[0])+"]"},a.format=function(e){var t=this,n=this.$locale();if(!this.isValid())return o.bind(this)(e);var a=this.$utils(),r=(e||"YYYY-MM-DDTHH:mm:ssZ").replace(/\[([^\]]+)]|Q|wo|ww|w|WW|W|zzz|z|gggg|GGGG|Do|X|x|k{1,2}|S/g,function(e){switch(e){case"Q":return Math.ceil((t.$M+1)/3);case"Do":return n.ordinal(t.$D);case"gggg":return t.weekYear();case"GGGG":return t.isoWeekYear();case"wo":return n.ordinal(t.week(),"W");case"w":case"ww":return a.s(t.week(),"w"===e?1:2,"0");case"W":case"WW":return a.s(t.isoWeek(),"W"===e?1:2,"0");case"k":case"kk":return a.s(String(0===t.$H?24:t.$H),"k"===e?1:2,"0");case"X":return Math.floor(t.$d.getTime()/1e3);case"x":return t.$d.getTime();case"z":return"["+t.offsetName()+"]";case"zzz":return"["+t.offsetName("long")+"]";default:return e}});return o.bind(this)(r)}}}()},function(e,t,n){e.exports=function(){"use strict";var l="week",s="year";return function(e,t,i){var n=t.prototype;n.week=function(e){if(void 0===e&&(e=null),null!==e)return this.add(7*(e-this.week()),"day");var t=this.$locale().yearStart||1;if(11===this.month()&&this.date()>25){var n=i(this).startOf(s).add(1,s).date(t),a=i(this).endOf(l);if(n.isBefore(a))return 1}var r=i(this).startOf(s).date(t).startOf(l).subtract(1,"millisecond"),o=this.diff(r,l,!0);return o<0?i(this).startOf("week").week():Math.ceil(o)},n.weeks=function(e){return void 0===e&&(e=null),this.week(e)}}}()},function(e,t,n){e.exports=function(e){"use strict";function t(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var n=t(e),a={name:"zh-cn",weekdays:"ę˜ŸęœŸę—„_ꘟꜟäø€_ꘟꜟäŗŒ_ꘟꜟäø‰_ę˜ŸęœŸå››_ꘟꜟäŗ”_ę˜ŸęœŸå…­".split("_"),weekdaysShort:"å‘Øę—„_å‘Øäø€_å‘ØäŗŒ_å‘Øäø‰_å‘Ø四_å‘Øäŗ”_å‘Ø六".split("_"),weekdaysMin:"ę—„_äø€_äŗŒ_äø‰_四_äŗ”_六".split("_"),months:"äø€ęœˆ_äŗŒęœˆ_äø‰ęœˆ_å››ęœˆ_äŗ”ęœˆ_å…­ęœˆ_äøƒęœˆ_å…«ęœˆ_ä¹ęœˆ_åęœˆ_十äø€ęœˆ_十äŗŒęœˆ".split("_"),monthsShort:"1꜈_2꜈_3꜈_4꜈_5꜈_6꜈_7꜈_8꜈_9꜈_10꜈_11꜈_12꜈".split("_"),ordinal:function(e,t){return"W"===t?e+"å‘Ø":e+"ę—„"},weekStart:1,yearStart:4,formats:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY幓M꜈Dę—„",LLL:"YYYY幓M꜈Dę—„Ahē‚¹mm分",LLLL:"YYYY幓M꜈Dę—„ddddAhē‚¹mm分",l:"YYYY/M/D",ll:"YYYY幓M꜈Dę—„",lll:"YYYY幓M꜈Dę—„ HH:mm",llll:"YYYY幓M꜈Dę—„dddd HH:mm"},relativeTime:{future:"%s内",past:"%s前",s:"几ē§’",m:"1 分钟",mm:"%d 分钟",h:"1 å°ę—¶",hh:"%d å°ę—¶",d:"1 天",dd:"%d 天",M:"1 äøŖ꜈",MM:"%d äøŖ꜈",y:"1 幓",yy:"%d 幓"},meridiem:function(e,t){var n=100*e+t;return n<600?"凌ę™Ø":n<900?"ę—©äøŠ":n<1100?"äøŠåˆ":n<1300?"äø­åˆ":n<1800?"äø‹åˆ":"ꙚäøŠ"}};return n.default.locale(a,null,!0),a}(n(205))},function(e,t,n){"use strict";t.__esModule=!0,t.flex=t.transition=t.animation=void 0;var r=n(201),o=n(96);function a(e){if(!r.hasDOM)return!1;var n=document.createElement("div"),a=!1;return(0,o.each)(e,function(e,t){if(void 0!==n.style[t])return!(a={end:e})}),a}t.animation=a({WebkitAnimation:"webkitAnimationEnd",OAnimation:"oAnimationEnd",animation:"animationend"}),t.transition=a({WebkitTransition:"webkitTransitionEnd",OTransition:"oTransitionEnd",transition:"transitionend"}),t.flex=function(e){if(!r.hasDOM)return!1;var n=document.createElement("div"),a=!1;return(0,o.each)(e,function(e,t){return(0,o.each)(e,function(e){try{n.style[t]=e,a=a||n.style[t]===e}catch(e){}return!a}),!a}),a}({display:["flex","-webkit-flex","-moz-flex","-ms-flexbox"]})},function(e,t,n){"use strict";t.__esModule=!0,t.getFocusNodeList=i,t.saveLastFocusNode=function(){l=document.activeElement},t.clearLastFocusNode=function(){l=null},t.backLastFocusNode=function(){if(l)try{l.focus()}catch(e){}},t.limitTabRange=function(e,t){{var n,a;t.keyCode===r.default.TAB&&(e=i(e),n=e.length-1,-1<(a=e.indexOf(document.activeElement))&&(a=a+(t.shiftKey?-1:1),e[a=n<(a=a<0?n:a)?0:a].focus(),t.preventDefault()))}};var t=n(206),r=(t=t)&&t.__esModule?t:{default:t},a=n(96);function o(e){var t=e.nodeName.toLowerCase(),n=parseInt(e.getAttribute("tabindex"),10),n=!isNaN(n)&&-1a.height)&&(r[1]=-t.top-("t"===e?t.height:0))),r},this._getParentScrollOffset=function(e){var t=0,n=0;return e&&e.offsetParent&&e.offsetParent!==document.body&&(isNaN(e.offsetParent.scrollTop)||(t+=e.offsetParent.scrollTop),isNaN(e.offsetParent.scrollLeft)||(n+=e.offsetParent.scrollLeft)),{top:t,left:n}}};var h=a;function m(e){(0,o.default)(this,m),r.call(this),this.pinElement=e.pinElement,this.baseElement=e.baseElement,this.pinFollowBaseElementWhenFixed=e.pinFollowBaseElementWhenFixed,this.container=function(e){var t=e.container,e=e.baseElement;if(void 0===("undefined"==typeof document?"undefined":(0,i.default)(document)))return t;for(var n=(n=(0,l.default)(t,e))||document.body;"static"===y.dom.getStyle(n,"position");){if(!n||n===document.body)return document.body;n=n.parentNode}return n}(e),this.autoFit=e.autoFit||!1,this.align=e.align||"tl tl",this.offset=e.offset||[0,0],this.needAdjust=e.needAdjust||!1,this.isRtl=e.isRtl||!1}t.default=h,e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var w=a(n(2)),M=a(n(12)),k=n(0),S=a(k),E=a(n(13)),x=a(n(183)),C=a(n(79)),L=n(11);function a(e){return e&&e.__esModule?e:{default:e}}t.default=function(e){if(!k.useState||!k.useRef||!k.useEffect)return L.log.warning("need react version > 16.8.0"),null;var t=e.prefix,t=void 0===t?"next-":t,n=e.animation,a=void 0===n?{in:"expandInDown",out:"expandOutUp"}:n,r=e.visible,n=e.hasMask,o=e.align,i=e.points,o=void 0===i?o?o.split(" "):void 0:i,l=e.onPosition,i=e.children,s=e.className,u=e.style,d=e.wrapperClassName,c=e.beforeOpen,f=e.onOpen,p=e.afterOpen,h=e.beforeClose,m=e.onClose,g=e.afterClose,e=(0,M.default)(e,["prefix","animation","visible","hasMask","align","points","onPosition","children","className","style","wrapperClassName","beforeOpen","onOpen","afterOpen","beforeClose","onClose","afterClose"]),y=(0,k.useState)(!0),v=y[0],_=y[1],b=(0,k.useRef)(null),y=S.default.createElement(C.default.OverlayAnimate,{visible:r,animation:a,onEnter:function(){_(!1),"function"==typeof c&&c(b.current)},onEntering:function(){"function"==typeof f&&f(b.current)},onEntered:function(){"function"==typeof p&&p(b.current)},onExit:function(){"function"==typeof h&&h(b.current)},onExiting:function(){"function"==typeof m&&m(b.current)},onExited:function(){_(!0),"function"==typeof g&&g(b.current)},timeout:300,style:u},i?(0,k.cloneElement)(i,{className:(0,E.default)([t+"overlay-inner",s,i&&i.props&&i.props.className])}):S.default.createElement("span",null)),s=(0,E.default)(((u={})[t+"overlay-wrapper v2"]=!0,u[d]=d,u.opened=r,u));return S.default.createElement(x.default,(0,w.default)({},e,{visible:r,isAnimationEnd:v,hasMask:n,wrapperClassName:s,maskClassName:t+"overlay-backdrop",maskRender:function(e){return S.default.createElement(C.default.OverlayAnimate,{visible:r,animation:!!a&&{in:"fadeIn",out:"fadeOut"},timeout:300,unmountOnExit:!0},e)},points:o,onPosition:function(e){(0,w.default)(e,{align:e.config.points}),"function"==typeof l&&l(e)},ref:b}),y)},e.exports=t.default},function(n,e){function a(e,t){return n.exports=a=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},n.exports.__esModule=!0,n.exports.default=n.exports,a(e,t)}n.exports=a,n.exports.__esModule=!0,n.exports.default=n.exports},function(e,t,n){"use strict";t.__esModule=!0;var a,d=g(n(12)),c=g(n(2)),r=g(n(4)),o=g(n(5)),i=g(n(6)),s=n(0),f=g(s),p=n(23),l=n(30),u=g(n(3)),h=n(11),m=g(n(348));function g(e){return e&&e.__esModule?e:{default:e}}var y,n=h.func.noop,v=h.func.makeChain,_=h.func.bindCtx,u=(y=s.Component,(0,i.default)(b,y),b.getDerivedStateFromProps=function(e,t){return"visible"in e?(0,c.default)({},t,{visible:e.visible}):null},b.prototype.componentWillUnmount=function(){var t=this;["_timer","_hideTimer","_showTimer"].forEach(function(e){t[e]&&clearTimeout(t[e])})},b.prototype.handleVisibleChange=function(e,t,n){"visible"in this.props||this.setState({visible:e}),this.props.onVisibleChange(e,t,n)},b.prototype.handleTriggerClick=function(e){this.state.visible&&!this.props.canCloseByTrigger||this.handleVisibleChange(!this.state.visible,"fromTrigger",e)},b.prototype.handleTriggerKeyDown=function(e){var t=this.props.triggerClickKeycode;(Array.isArray(t)?t:[t]).includes(e.keyCode)&&(e.preventDefault(),this.handleTriggerClick(e))},b.prototype.handleTriggerMouseEnter=function(e){var t=this;this._mouseNotFirstOnMask=!1,this._hideTimer&&(clearTimeout(this._hideTimer),this._hideTimer=null),this._showTimer&&(clearTimeout(this._showTimer),this._showTimer=null),this.state.visible||(this._showTimer=setTimeout(function(){t.handleVisibleChange(!0,"fromTrigger",e)},this.props.delay))},b.prototype.handleTriggerMouseLeave=function(e,t){var n=this;this._showTimer&&(clearTimeout(this._showTimer),this._showTimer=null),this.state.visible&&(this._hideTimer=setTimeout(function(){n.handleVisibleChange(!1,t||"fromTrigger",e)},this.props.delay))},b.prototype.handleTriggerFocus=function(e){this.handleVisibleChange(!0,"fromTrigger",e)},b.prototype.handleTriggerBlur=function(e){this._isForwardContent||this.handleVisibleChange(!1,"fromTrigger",e),this._isForwardContent=!1},b.prototype.handleContentMouseDown=function(){this._isForwardContent=!0},b.prototype.handleContentMouseEnter=function(){clearTimeout(this._hideTimer)},b.prototype.handleContentMouseLeave=function(e){this.handleTriggerMouseLeave(e,"fromContent")},b.prototype.handleMaskMouseEnter=function(){this._mouseNotFirstOnMask||(clearTimeout(this._hideTimer),this._hideTimer=null,this._mouseNotFirstOnMask=!1)},b.prototype.handleMaskMouseLeave=function(){this._mouseNotFirstOnMask=!0},b.prototype.handleRequestClose=function(e,t){this.handleVisibleChange(!1,e,t)},b.prototype.renderTrigger=function(){var e,t,n,a,r,o,i,l=this,s=this.props,u=s.trigger,s=s.disabled,d={key:"trigger","aria-haspopup":!0,"aria-expanded":this.state.visible};return this.state.visible||(d["aria-describedby"]=void 0),s||(s=this.props.triggerType,s=Array.isArray(s)?s:[s],e=u&&u.props||{},t=e.onClick,n=e.onKeyDown,a=e.onMouseEnter,r=e.onMouseLeave,o=e.onFocus,i=e.onBlur,s.forEach(function(e){switch(e){case"click":d.onClick=v(l.handleTriggerClick,t),d.onKeyDown=v(l.handleTriggerKeyDown,n);break;case"hover":d.onMouseEnter=v(l.handleTriggerMouseEnter,a),d.onMouseLeave=v(l.handleTriggerMouseLeave,r);break;case"focus":d.onFocus=v(l.handleTriggerFocus,o),d.onBlur=v(l.handleTriggerBlur,i)}})),u&&f.default.cloneElement(u,d)},b.prototype.renderContent=function(){var t=this,e=this.props,n=e.children,e=e.triggerType,e=Array.isArray(e)?e:[e],n=s.Children.only(n),a=n.props,r=a.onMouseDown,o=a.onMouseEnter,i=a.onMouseLeave,l={key:"portal"};return e.forEach(function(e){switch(e){case"focus":l.onMouseDown=v(t.handleContentMouseDown,r);break;case"hover":l.onMouseEnter=v(t.handleContentMouseEnter,o),l.onMouseLeave=v(t.handleContentMouseLeave,i)}}),f.default.cloneElement(n,l)},b.prototype.renderPortal=function(){function e(){return(0,p.findDOMNode)(t)}var t=this,n=this.props,a=n.target,r=n.safeNode,o=n.followTrigger,i=n.triggerType,l=n.hasMask,s=n.wrapperStyle,n=(0,d.default)(n,["target","safeNode","followTrigger","triggerType","hasMask","wrapperStyle"]),u=this.props.container,r=Array.isArray(r)?[].concat(r):[r],s=(r.unshift(e),s||{});return o&&(u=function(e){return e&&e.parentNode||e},s.position="relative"),"hover"===i&&l&&(n.onMaskMouseEnter=this.handleMaskMouseEnter,n.onMaskMouseLeave=this.handleMaskMouseLeave),f.default.createElement(m.default,(0,c.default)({},n,{key:"overlay",ref:function(e){return t.overlay=e},visible:this.state.visible,target:a||e,container:u,safeNode:r,wrapperStyle:s,triggerType:i,hasMask:l,onRequestClose:this.handleRequestClose}),this.props.children&&this.renderContent())},b.prototype.render=function(){return[this.renderTrigger(),this.renderPortal()]},a=i=b,i.propTypes={children:u.default.node,trigger:u.default.element,triggerType:u.default.oneOfType([u.default.string,u.default.array]),triggerClickKeycode:u.default.oneOfType([u.default.number,u.default.array]),visible:u.default.bool,defaultVisible:u.default.bool,onVisibleChange:u.default.func,disabled:u.default.bool,autoFit:u.default.bool,delay:u.default.number,canCloseByTrigger:u.default.bool,target:u.default.any,safeNode:u.default.any,followTrigger:u.default.bool,container:u.default.any,hasMask:u.default.bool,wrapperStyle:u.default.object,rtl:u.default.bool,v2:u.default.bool,placement:u.default.string,placementOffset:u.default.number},i.defaultProps={triggerType:"hover",triggerClickKeycode:[h.KEYCODE.SPACE,h.KEYCODE.ENTER],defaultVisible:!1,onVisibleChange:n,disabled:!1,autoFit:!1,delay:200,canCloseByTrigger:!0,followTrigger:!1,container:function(){return document.body},rtl:!1},a);function b(e){(0,r.default)(this,b);var t=(0,o.default)(this,y.call(this,e));return t.state={visible:void 0===e.visible?e.defaultVisible:e.visible},_(t,["handleTriggerClick","handleTriggerKeyDown","handleTriggerMouseEnter","handleTriggerMouseLeave","handleTriggerFocus","handleTriggerBlur","handleContentMouseEnter","handleContentMouseLeave","handleContentMouseDown","handleRequestClose","handleMaskMouseEnter","handleMaskMouseLeave"]),t}u.displayName="Popup",t.default=(0,l.polyfill)(u),e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var T=a(n(2)),D=a(n(12)),O=n(0),N=a(O),P=a(n(13)),j=a(n(183)),Y=a(n(79)),I=n(11);function a(e){return e&&e.__esModule?e:{default:e}}t.default=function(r){if(!O.useState||!O.useRef||!O.useEffect)return I.log.warning("need react version > 16.8.0"),null;var e=r.prefix,e=void 0===e?"next-":e,t=r.animation,n=void 0===t?{in:"expandInDown",out:"expandOutUp"}:t,t=r.defaultVisible,a=r.onVisibleChange,o=void 0===a?function(){}:a,a=r.trigger,i=r.triggerType,i=void 0===i?"hover":i,l=r.overlay,s=r.onPosition,u=r.children,d=r.className,c=r.style,f=r.wrapperClassName,p=r.triggerClickKeycode,h=r.align,m=r.beforeOpen,g=r.onOpen,y=r.afterOpen,v=r.beforeClose,_=r.onClose,b=r.afterClose,w=(0,D.default)(r,["prefix","animation","defaultVisible","onVisibleChange","trigger","triggerType","overlay","onPosition","children","className","style","wrapperClassName","triggerClickKeycode","align","beforeOpen","onOpen","afterOpen","beforeClose","onClose","afterClose"]),t=(0,O.useState)(t),M=t[0],k=t[1],t=(0,O.useState)(n),S=t[0],E=t[1],t=(0,O.useState)(!0),x=t[0],C=t[1],L=(0,O.useRef)(null),t=((0,O.useEffect)(function(){"visible"in r&&k(r.visible)},[r.visible]),(0,O.useEffect)(function(){"animation"in r&&S!==n&&E(n)},[n]),l?u:a),a=l||u,a=N.default.createElement(Y.default.OverlayAnimate,{visible:M,animation:S,timeout:200,onEnter:function(){C(!1),"function"==typeof m&&m(L.current)},onEntering:function(){"function"==typeof g&&g(L.current)},onEntered:function(){"function"==typeof y&&y(L.current)},onExit:function(){"function"==typeof v&&v(L.current)},onExiting:function(){"function"==typeof _&&_(L.current)},onExited:function(){C(!0),"function"==typeof b&&b(L.current)},style:c},a?(0,O.cloneElement)(a,{className:(0,P.default)([e+"overlay-inner",d,a&&a.props&&a.props.className])}):N.default.createElement("span",null)),u=(0,P.default)(((l={})[e+"overlay-wrapper v2"]=!0,l[f]=f,l.opened=M,l)),c={};h&&(c.points=h.split(" "));return N.default.createElement(j.default.Popup,(0,T.default)({},w,c,{wrapperClassName:u,overlay:a,visible:M,isAnimationEnd:x,triggerType:i,onVisibleChange:function(e){for(var t=arguments.length,n=Array(1a&&y.shift(),i.default.render(d.default.createElement(l.default,l.default.getContext(),d.default.createElement(m,{dataSource:y})),g),{key:n,close:function(){r.timer&&clearTimeout(r.timer);var e=y.indexOf(r);-1 16.8.0")}},e.exports=t.default},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){"use strict";n(546)},function(e,t,n){},function(e,t,n){},function(e,t,n){"use strict";t.__esModule=!0,t.default=void 0;var p=s(n(2)),r=s(n(4)),o=s(n(5)),a=s(n(6)),h=s(n(38)),m=s(n(0)),i=s(n(3)),g=s(n(13)),y=n(11),l=s(n(26)),v=s(n(355));function s(e){return e&&e.__esModule?e:{default:e}}function _(e,r){var o=r.size,i=r.device,l=r.labelAlign,s=r.labelTextAlign,u=r.labelCol,d=r.wrapperCol,c=r.responsive,f=r.colon;return m.default.Children.map(e,function(e){return y.obj.isReactFragment(e)?_(e.props.children,r):e&&-1<["function","object"].indexOf((0,h.default)(e.type))&&"form_item"===e.type._typeMark?(t={labelCol:e.props.labelCol||u,wrapperCol:e.props.wrapperCol||d,labelAlign:e.props.labelAlign||("phone"===i?"top":l),labelTextAlign:e.props.labelTextAlign||s,colon:"colon"in e.props?e.props.colon:f,size:e.props.size||o,responsive:c},m.default.cloneElement(e,(n=t,a={},Object.keys(n).forEach(function(e){void 0!==n[e]&&(a[e]=n[e])}),a))):e;var t,n,a})}u=m.default.Component,(0,a.default)(b,u),b.prototype.getChildContext=function(){return{_formField:this.props.field||this._formField,_formSize:this.props.size,_formDisabled:this.props.disabled,_formPreview:this.props.isPreview,_formFullWidth:this.props.fullWidth,_formLabelForErrorMessage:this.props.useLabelForErrorMessage}},b.prototype.componentDidUpdate=function(e){var t=this.props;this._formField&&("value"in t&&t.value!==e.value&&this._formField.setValues(t.value),"error"in t&&t.error!==e.error&&this._formField.setValues(t.error))},b.prototype.render=function(){var e=this.props,t=e.className,n=e.inline,a=e.size,r=(e.device,e.labelAlign,e.labelTextAlign,e.onSubmit),o=e.children,i=(e.labelCol,e.wrapperCol,e.style),l=e.prefix,s=e.rtl,u=e.isPreview,d=e.component,c=e.responsive,f=e.gap,n=(e.colon,(0,g.default)(((e={})[l+"form"]=!0,e[l+"inline"]=n,e[""+l+a]=a,e[l+"form-responsive-grid"]=c,e[l+"form-preview"]=u,e[t]=!!t,e))),a=_(o,this.props);return m.default.createElement(d,(0,p.default)({role:"grid"},y.obj.pickOthers(b.propTypes,this.props),{className:n,style:i,dir:s?"rtl":void 0,onSubmit:r}),c?m.default.createElement(v.default,{gap:f},a):a)},a=n=b,n.propTypes={prefix:i.default.string,inline:i.default.bool,size:i.default.oneOf(["large","medium","small"]),fullWidth:i.default.bool,labelAlign:i.default.oneOf(["top","left","inset"]),labelTextAlign:i.default.oneOf(["left","right"]),field:i.default.any,saveField:i.default.func,labelCol:i.default.object,wrapperCol:i.default.object,onSubmit:i.default.func,children:i.default.any,className:i.default.string,style:i.default.object,value:i.default.object,onChange:i.default.func,component:i.default.oneOfType([i.default.string,i.default.func]),fieldOptions:i.default.object,rtl:i.default.bool,device:i.default.oneOf(["phone","tablet","desktop"]),responsive:i.default.bool,isPreview:i.default.bool,useLabelForErrorMessage:i.default.bool,colon:i.default.bool,disabled:i.default.bool,gap:i.default.oneOfType([i.default.arrayOf(i.default.number),i.default.number])},n.defaultProps={prefix:"next-",onSubmit:function(e){e.preventDefault()},size:"medium",labelAlign:"left",onChange:y.func.noop,component:"form",saveField:y.func.noop,device:"desktop",colon:!1,disabled:!1},n.childContextTypes={_formField:i.default.object,_formSize:i.default.string,_formDisabled:i.default.bool,_formPreview:i.default.bool,_formFullWidth:i.default.bool,_formLabelForErrorMessage:i.default.bool};var u,n=a;function b(e){(0,r.default)(this,b);var t,n,a=(0,o.default)(this,u.call(this,e));return a.onChange=function(e,t){a.props.onChange(a._formField.getValues(),{name:e,value:t,field:a._formField})},a._formField=null,!1!==e.field&&(t=(0,p.default)({},e.fieldOptions,{onChange:a.onChange}),e.field?(a._formField=e.field,n=a._formField.options.onChange,t.onChange=y.func.makeChain(n,a.onChange),a._formField.setOptions&&a._formField.setOptions(t)):("value"in e&&(t.values=e.value),a._formField=new l.default(a,t)),e.locale&&e.locale.Validate&&a._formField.setOptions({messages:e.locale.Validate}),e.saveField(a._formField)),a}n.displayName="Form",t.default=n,e.exports=t.default},function(e,t,n){"use strict";var a=n(86),m=(Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,a(n(160))),i=a(n(551)),r=a(n(162)),o=a(n(161)),b=a(n(163)),w=a(n(73)),l=a(n(352)),s=a(n(353)),g=a(n(557)),M=n(566),u={state:"",valueName:"value",trigger:"onChange",inputValues:[]},a=function(){function a(e){var t=this,n=1e.length)&&(t=e.length);for(var n=0,a=new Array(t);n=a.length)return n;var o=a[r],n=e(t&&t[o],n,a,r+1);return t?Array.isArray(t)?((a=[].concat(t))[o]=n,a):(0,s.default)({},t,(0,i.default)({},o,n)):((r=isNaN(o)?{}:[])[o]=n,r)};t=function(){};void 0!==e&&e.env,n.warning=t}.call(this,r(354))},function(e,t,n){"use strict";t.__esModule=!0,t.cloneAndAddKey=function(e){{var t;if(e&&(0,a.isValidElement)(e))return t=e.key||"error",(0,a.cloneElement)(e,{key:t})}return e},t.scrollToFirstError=function(e){var t=e.errorsGroup,n=e.options,a=e.instance;if(t&&n.scrollToFirstError){var r,o=void 0,i=void 0;for(r in t)if(t.hasOwnProperty(r)){var l=u.default.findDOMNode(a[r]);if(!l)return;var s=l.offsetTop;(void 0===i||s), use instead of.'),S.default.cloneElement(e,{className:t,size:c||C(r)})):(0,k.isValidElement)(e)?e:S.default.createElement("span",{className:a+"btn-helper"},e)}),t=d,_=(0,b.default)({},x.obj.pickOthers(Object.keys(L.propTypes),e),{type:o,disabled:p,onClick:h,className:(0,E.default)(n)});return"button"!==t&&(delete _.type,_.disabled&&(delete _.onClick,_.href&&delete _.href)),S.default.createElement(t,(0,b.default)({},_,{dir:g?"rtl":void 0,onMouseUp:this.onMouseUp,ref:this.buttonRefHandler}),l,u)},a=n=L,n.propTypes=(0,b.default)({},l.default.propTypes,{prefix:r.default.string,rtl:r.default.bool,type:r.default.oneOf(["primary","secondary","normal"]),size:r.default.oneOf(["small","medium","large"]),icons:r.default.shape({loading:r.default.node}),iconSize:r.default.oneOfType([r.default.oneOf(["xxs","xs","small","medium","large","xl","xxl","xxxl","inherit"]),r.default.number]),htmlType:r.default.oneOf(["submit","reset","button"]),component:r.default.oneOf(["button","a","div","span"]),loading:r.default.bool,ghost:r.default.oneOf([!0,!1,"light","dark"]),text:r.default.bool,warning:r.default.bool,disabled:r.default.bool,onClick:r.default.func,className:r.default.string,onMouseUp:r.default.func,children:r.default.node}),n.defaultProps={prefix:"next-",type:"normal",size:"medium",icons:{},htmlType:"button",component:"button",loading:!1,ghost:!1,text:!1,warning:!1,disabled:!1,onClick:function(){}};var u,l=a;function L(){var e,t;(0,o.default)(this,L);for(var n=arguments.length,a=Array(n),r=0;ra[r])return!0;if(n[r] 0, or `null`');if(W(i,"numericSeparator")&&"boolean"!=typeof i.numericSeparator)throw new TypeError('option "numericSeparator", if provided, must be `true` or `false`');var t=i.numericSeparator;if(void 0===n)return"undefined";if(null===n)return"null";if("boolean"==typeof n)return n?"true":"false";if("string"==typeof n)return function e(t,n){if(t.length>n.maxStringLength)return a=t.length-n.maxStringLength,a="... "+a+" more character"+(1"}if(H(n)){if(0===n.length)return"[]";var v=$(n,h);return l&&!function(e){for(var t=0;t "+h(e,n))}),G("Map",_.call(n),u,l)):function(e){if(b&&e&&"object"==typeof e)try{b.call(e);try{_.call(e)}catch(e){return 1}return e instanceof Set}catch(e){}return}(n)?(d=[],X.call(n,function(e){d.push(h(e,n))}),G("Set",b.call(n),d,l)):function(e){if(w&&e&&"object"==typeof e)try{w.call(e,w);try{M.call(e,M)}catch(e){return 1}return e instanceof WeakMap}catch(e){}return}(n)?K("WeakMap"):function(e){if(M&&e&&"object"==typeof e)try{M.call(e,M);try{w.call(e,w)}catch(e){return 1}return e instanceof WeakSet}catch(e){}return}(n)?K("WeakSet"):function(e){if(k&&e&&"object"==typeof e)try{return k.call(e),1}catch(e){}return}(n)?K("WeakRef"):"[object Number]"!==V(c=n)||N&&"object"==typeof c&&N in c?function(e){if(e&&"object"==typeof e&&T)try{return T.call(e),1}catch(e){}return}(n)?U(h(T.call(n))):"[object Boolean]"!==V(t=n)||N&&"object"==typeof t&&N in t?"[object String]"!==V(e=n)||N&&"object"==typeof e&&N in e?("[object Date]"!==V(t=n)||N&&"object"==typeof t&&N in t)&&!F(n)?(e=$(n,h),t=j?j(n)===Object.prototype:n instanceof Object||n.constructor===Object,f=n instanceof Object?"":"null prototype",p=!t&&N&&Object(n)===n&&N in n?S.call(V(n),8,-1):f?"Object":"",t=(!t&&"function"==typeof n.constructor&&n.constructor.name?n.constructor.name+" ":"")+(p||f?"["+L.call(C.call([],p||[],f||[]),": ")+"] ":""),0===e.length?t+"{}":l?t+"{"+q(e,l)+"}":t+"{ "+L.call(e,", ")+" }"):String(n):U(h(String(n))):U(Q.call(n)):U(h(Number(n)))};var s=Object.prototype.hasOwnProperty||function(e){return e in this};function W(e,t){return s.call(e,t)}function V(e){return i.call(e)}function B(e,t){if(e.indexOf)return e.indexOf(t);for(var n=0,a=e.length;n, as child."),a.push(t),e.props.children&&(t.children=n(e.props.children)))}),a}(e.children):t},N.prototype.fetchInfoFromBinaryChildren=function(e){function r(e,t){return t=t||0,e.forEach(function(e){e.children?t=r(e.children,t):t+=1}),t}var a=!1,o=[],i=[],e=(function t(){var e=0r.tRight&&(e=r.tRight,r.changedPageX=r.tRight-r.startLeft),e-r.cellLefto.clientHeight,o.scrollWidth,o.clientWidth,o={},e||(o[r]=0,o[a]=0),+i&&(o.marginBottom=-i,o.paddingBottom=i,e&&(o[a]=i)),h.dom.setStyle(this.headerNode,o)),n&&!this.props.lockType&&this.headerNode&&(r=this.headerNode.querySelector("."+t+"table-header-fixer"),e=h.dom.getStyle(this.headerNode,"height"),a=h.dom.getStyle(this.headerNode,"paddingBottom"),h.dom.setStyle(r,{width:i,height:e-a}))},o.prototype.render=function(){var e=this.props,t=e.components,n=e.className,a=e.prefix,r=e.fixedHeader,o=e.lockType,i=e.dataSource,e=(e.maxBodyHeight,(0,u.default)(e,["components","className","prefix","fixedHeader","lockType","dataSource","maxBodyHeight"]));return r&&((t=(0,s.default)({},t)).Header||(t.Header=m.default),t.Body||(t.Body=g.default),t.Wrapper||(t.Wrapper=y.default),n=(0,p.default)(((r={})[a+"table-fixed"]=!0,r[a+"table-wrap-empty"]=!i.length,r[n]=n,r))),c.default.createElement(l,(0,s.default)({},e,{dataSource:i,lockType:o,components:t,className:n,prefix:a}))},o}(c.default.Component),n.FixedHeader=m.default,n.FixedBody=g.default,n.FixedWrapper=y.default,n.propTypes=(0,s.default)({hasHeader:r.default.bool,fixedHeader:r.default.bool,maxBodyHeight:r.default.oneOfType([r.default.number,r.default.string])},l.propTypes),n.defaultProps=(0,s.default)({},l.defaultProps,{hasHeader:!0,fixedHeader:!1,maxBodyHeight:200,components:{},refs:{},prefix:"next-"}),n.childContextTypes={fixedHeader:r.default.bool,getNode:r.default.func,onFixedScrollSync:r.default.func,getTableInstanceForFixed:r.default.func,maxBodyHeight:r.default.oneOfType([r.default.number,r.default.string])};var t,n=t;return n.displayName="FixedTable",(0,o.statics)(n,l),n};var c=l(n(0)),r=l(n(3)),f=n(23),p=l(n(13)),h=n(11),m=l(n(129)),g=l(n(390)),y=l(n(130)),o=n(67);function l(e){return e&&e.__esModule?e:{default:e}}e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var i=o(n(12)),f=o(n(2)),r=o(n(4)),l=o(n(5)),s=o(n(6)),u=(t.default=function(o){e=t=function(n){function a(e,t){(0,r.default)(this,a);var c=(0,l.default)(this,n.call(this,e,t));return c.addSelection=function(e){var t=c.props,n=t.prefix,a=t.rowSelection,t=t.size,a=a.columnProps&&a.columnProps()||{};e.find(function(e){return"selection"===e.key})||e.unshift((0,f.default)({key:"selection",title:c.renderSelectionHeader.bind(c),cell:c.renderSelectionBody.bind(c),width:"small"===t?34:50,className:n+"table-selection "+n+"table-prerow",__normalized:!0},a))},c.renderSelectionHeader=function(){var e=c.selectAllRow,t={},n=c.props,a=n.rowSelection,r=n.primaryKey,o=n.dataSource,i=n.entireDataSource,n=n.locale,l=c.state.selectedRowKeys,s=a.mode||"multiple",u=!!l.length,d=!1,i=(c.flatDataSource(i||o).filter(function(e,t){return!a.getProps||!(a.getProps(e,t)||{}).disabled}).map(function(e){return e[r]}).forEach(function(e){-1===l.indexOf(e)?u=!1:d=!0}),t.onClick=b(function(e){e.stopPropagation()},t.onClick),a.titleProps&&a.titleProps()||{});return u&&(d=!1),["multiple"===s?p.default.createElement(h.default,(0,f.default)({key:"_total",indeterminate:d,"aria-label":n.selectAll,checked:u,onChange:e},t,i)):null,a.titleAddons&&a.titleAddons()]},c.renderSelectionBody=function(e,t,n){var a=c.props,r=a.rowSelection,a=a.primaryKey,o=c.state.selectedRowKeys,i=r.mode||"multiple",o=-1s.length&&(u=o),w(u.filter(function(e){return-1=Math.max(a-y,0)&&oc.clientHeight;this.isLock()?(e=this.bodyLeftNode,t=this.bodyRightNode,n=this.getWrapperNode("right"),a=f?d:0,c=c.offsetHeight-d,f||(r[s]=0,r[u]=0),+d?(r.marginBottom=-d,r.paddingBottom=d):(r.marginBottom=-20,r.paddingBottom=20),c={"max-height":c},o||+d||(c[u]=0),+d&&(c[u]=-d),e&&g.dom.setStyle(e,c),t&&g.dom.setStyle(t,c),n&&+d&&g.dom.setStyle(n,i?"left":"right",a+"px")):(r.marginBottom=-d,r.paddingBottom=d,r[u]=0,f||(r[s]=0)),l&&g.dom.setStyle(l,r)},a.prototype.adjustHeaderSize=function(){var o=this;this.isLock()&&this.tableInc.groupChildren.forEach(function(e,t){var n=o.tableInc.groupChildren[t].length-1,n=o.getHeaderCellNode(t,n),a=o.getHeaderCellNode(t,0),r=o.getHeaderCellNode(t,0,"right"),t=o.getHeaderCellNode(t,0,"left");n&&r&&(n=n.offsetHeight,g.dom.setStyle(r,"height",n),setTimeout(function(){var e=o.tableRightInc.affixRef;return e&&e.getInstance()&&e.getInstance().updatePosition()})),a&&t&&(r=a.offsetHeight,g.dom.setStyle(t,"height",r),setTimeout(function(){var e=o.tableLeftInc.affixRef;return e&&e.getInstance()&&e.getInstance().updatePosition()}))})},a.prototype.adjustRowHeight=function(){var n=this;this.isLock()&&this.tableInc.props.dataSource.forEach(function(e,t){t=""+("object"===(void 0===e?"undefined":(0,r.default)(e))&&"__rowIndex"in e?e.__rowIndex:t)+(e.__expanded?"_expanded":"");n.setRowHeight(t,"left"),n.setRowHeight(t,"right")})},a.prototype.setRowHeight=function(e,t){var t=this.getRowNode(e,t),e=this.getRowNode(e),e=(M?e&&e.offsetHeight:e&&parseFloat(getComputedStyle(e).height))||"auto",n=(M?t&&t.offsetHeight:t&&parseFloat(getComputedStyle(t).height))||"auto";t&&e!==n&&g.dom.setStyle(t,"height",e)},a.prototype.getWrapperNode=function(e){e=e?e.charAt(0).toUpperCase()+e.substr(1):"";try{return(0,u.findDOMNode)(this["lock"+e+"El"])}catch(e){return null}},a.prototype.getRowNode=function(e,t){t=this["table"+(t=t?t.charAt(0).toUpperCase()+t.substr(1):"")+"Inc"];try{return(0,u.findDOMNode)(t.getRowRef(e))}catch(e){return null}},a.prototype.getHeaderCellNode=function(e,t,n){n=this["table"+(n=n?n.charAt(0).toUpperCase()+n.substr(1):"")+"Inc"];try{return(0,u.findDOMNode)(n.getHeaderCellRef(e,t))}catch(e){return null}},a.prototype.getCellNode=function(e,t,n){n=this["table"+(n=n?n.charAt(0).toUpperCase()+n.substr(1):"")+"Inc"];try{return(0,u.findDOMNode)(n.getCellRef(e,t))}catch(e){return null}},a.prototype.render=function(){var e,t=this.props,n=(t.children,t.columns,t.prefix),a=t.components,r=t.className,o=t.dataSource,i=t.tableWidth,t=(0,f.default)(t,["children","columns","prefix","components","className","dataSource","tableWidth"]),l=this.normalizeChildrenState(this.props),s=l.lockLeftChildren,u=l.lockRightChildren,l=l.children,d={left:this.getFlatenChildrenLength(s),right:this.getFlatenChildrenLength(u),origin:this.getFlatenChildrenLength(l)};return this._notNeedAdjustLockLeft&&(s=[]),this._notNeedAdjustLockRight&&(u=[]),this.lockLeftChildren=s,this.lockRightChildren=u,this.isOriginLock()?((a=(0,p.default)({},a)).Body=a.Body||v.default,a.Header=a.Header||_.default,a.Wrapper=a.Wrapper||b.default,a.Row=a.Row||y.default,r=(0,m.default)(((e={})[n+"table-lock"]=!0,e[n+"table-wrap-empty"]=!o.length,e[r]=r,e)),e=[h.default.createElement(c,(0,p.default)({},t,{dataSource:o,key:"lock-left",columns:s,className:n+"table-lock-left",lengths:d,prefix:n,lockType:"left",components:a,ref:this.saveLockLeftRef,loading:!1,"aria-hidden":!0})),h.default.createElement(c,(0,p.default)({},t,{dataSource:o,key:"lock-right",columns:u,className:n+"table-lock-right",lengths:d,prefix:n,lockType:"right",components:a,ref:this.saveLockRightRef,loading:!1,"aria-hidden":!0}))],h.default.createElement(c,(0,p.default)({},t,{tableWidth:i,dataSource:o,columns:l,prefix:n,lengths:d,wrapperContent:e,components:a,className:r}))):h.default.createElement(c,this.props)},a}(h.default.Component),t.LockRow=y.default,t.LockBody=v.default,t.LockHeader=_.default,t.propTypes=(0,p.default)({scrollToCol:a.default.number,scrollToRow:a.default.number},c.propTypes),t.defaultProps=(0,p.default)({},c.defaultProps),t.childContextTypes={getTableInstance:a.default.func,getLockNode:a.default.func,onLockBodyScroll:a.default.func,onRowMouseEnter:a.default.func,onRowMouseLeave:a.default.func};var e,t=e;return t.displayName="LockTable",(0,w.statics)(t,c),t},n(0)),h=c(s),u=n(23),a=c(n(3)),m=c(n(13)),d=c(n(172)),g=n(11),y=c(n(174)),v=c(n(391)),_=c(n(392)),b=c(n(130)),w=n(67);function c(e){return e&&e.__esModule?e:{default:e}}var M=g.env.ieVersion;function k(e){return function n(e){return e.map(function(e){var t=(0,p.default)({},e);return e.children&&(e.children=n(e.children)),t})}(e)}e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var s=l(n(12)),u=l(n(2)),r=l(n(4)),o=l(n(5)),i=l(n(6)),d=(t.default=function(l){e=t=function(n){function a(e,t){(0,r.default)(this,a);var d=(0,o.default)(this,n.call(this,e));return d.state={},d.updateOffsetArr=function(){var e=d.splitChildren||{},t=e.lockLeftChildren,n=e.lockRightChildren,e=e.originChildren,a=d.getFlatenChildren(t).length,r=d.getFlatenChildren(n).length,e=a+r+d.getFlatenChildren(e).length,a=0r.top-e.offset?(t?(s.position="absolute",s.top=a-(r.top-e.offset),u="relative"):(s.position="fixed",s.top=e.offset+n.top),d._setAffixStyle(s,!0),d._setContainerStyle(l)):e.bottom&&a![^\\|>]*)\\s+)?(?\\||>)(?\\+|\\-|\\d+|\\+\\d+|\\-\\d+|\\d+\\+|\\d+\\-)?(? +#.*)?$"),R.prototype.PATTERN_FOLDED_SCALAR_END=new p("(?\\||>)(?\\+|\\-|\\d+|\\+\\d+|\\-\\d+|\\d+\\+|\\d+\\-)?(? +#.*)?$"),R.prototype.PATTERN_SEQUENCE_ITEM=new p("^\\-((?\\s+)(?.+?))?\\s*$"),R.prototype.PATTERN_ANCHOR_VALUE=new p("^&(?[^ ]+) *(?.*)"),R.prototype.PATTERN_COMPACT_NOTATION=new p("^(?"+j.REGEX_QUOTED_STRING+"|[^ '\"\\{\\[].*?) *\\:(\\s+(?.+?))?\\s*$"),R.prototype.PATTERN_MAPPING_ITEM=new p("^(?"+j.REGEX_QUOTED_STRING+"|[^ '\"\\[\\{].*?) *\\:(\\s+(?.+?))?\\s*$"),R.prototype.PATTERN_DECIMAL=new p("\\d+"),R.prototype.PATTERN_INDENT_SPACES=new p("^ +"),R.prototype.PATTERN_TRAILING_LINES=new p("(\n*)$"),R.prototype.PATTERN_YAML_HEADER=new p("^\\%YAML[: ][\\d\\.]+.*\n","m"),R.prototype.PATTERN_LEADING_COMMENTS=new p("^(\\#.*?\n)+","m"),R.prototype.PATTERN_DOCUMENT_MARKER_START=new p("^\\-\\-\\-.*?\n","m"),R.prototype.PATTERN_DOCUMENT_MARKER_END=new p("^\\.\\.\\.\\s*$","m"),R.prototype.PATTERN_FOLDED_SCALAR_BY_INDENTATION={},R.prototype.CONTEXT_NONE=0,R.prototype.CONTEXT_SEQUENCE=1,R.prototype.CONTEXT_MAPPING=2,R.prototype.parse=function(e,t,n){var a,r,o,i,l,s,u,d,c,f,p,h,m,g,y,v,_,b,w,M,k,S,E,x,C,L,T,D,O,N,P;for(null==t&&(t=!1),null==n&&(n=null),this.currentLineNb=-1,this.currentLine="",this.lines=this.cleanup(e).split("\n"),i=null,o=this.CONTEXT_NONE,r=!1;this.moveToNextLine();)if(!this.isCurrentLineEmpty()){if("\t"===this.currentLine[0])throw new Y("A YAML file cannot contain tabs as indentation.",this.getRealCurrentLineNb()+1,this.currentLine);if(d=k=!1,P=this.PATTERN_SEQUENCE_ITEM.exec(this.currentLine)){if(this.CONTEXT_MAPPING===o)throw new Y("You cannot define a sequence item when in a mapping");o=this.CONTEXT_SEQUENCE,null==i&&(i=[]),null!=P.value&&(M=this.PATTERN_ANCHOR_VALUE.exec(P.value))&&(d=M.ref,P.value=M.value),null==P.value||""===I.trim(P.value," ")||0===I.ltrim(P.value," ").indexOf("#")?this.currentLineNb=this.lines.length-1)&&(this.currentLine=this.lines[++this.currentLineNb],!0)},R.prototype.moveToPreviousLine=function(){this.currentLine=this.lines[--this.currentLineNb]},R.prototype.parseValue=function(t,e,n){var a,r,o,i;if(0===t.indexOf("*")){if(t=-1!==(r=t.indexOf("#"))?t.substr(1,r-2):t.slice(1),void 0===this.refs[t])throw new Y('Reference "'+t+'" does not exist.',this.currentLine);return this.refs[t]}if(r=this.PATTERN_FOLDED_SCALAR_ALL.exec(t))return i=null!=(i=r.modifiers)?i:"",o=Math.abs(parseInt(i)),isNaN(o)&&(o=0),i=this.parseFoldedScalar(r.separator,this.PATTERN_DECIMAL.replace(i,""),o),null!=r.type?(j.configure(e,n),j.parseScalar(r.type+" "+i)):i;if("["!==(o=t.charAt(0))&&"{"!==o&&'"'!==o&&"'"!==o)return this.isNextLineIndented()&&(t+="\n"+this.getNextEmbedBlock()),j.parse(t,e,n);for(;;)try{return j.parse(t,e,n)}catch(e){if(!((a=e)instanceof l&&this.moveToNextLine()))throw a.parsedLine=this.getRealCurrentLineNb()+1,a.snippet=this.currentLine,a;t+="\n"+I.trim(this.currentLine," ")}},R.prototype.parseFoldedScalar=function(e,t,n){var a,r,o,i,l,s,u,d,c,f;if(null==t&&(t=""),null==n&&(n=0),!(u=this.moveToNextLine()))return"";for(a=this.isCurrentLineBlank(),f="";u&&a;)(u=this.moveToNextLine())&&(f+="\n",a=this.isCurrentLineBlank());if(0<(n=0===n&&(l=this.PATTERN_INDENT_SPACES.exec(this.currentLine))?l[0].length:n))for(null==(d=this.PATTERN_FOLDED_SCALAR_BY_INDENTATION[n])&&(d=new p("^ {"+n+"}(.*)$"),R.prototype.PATTERN_FOLDED_SCALAR_BY_INDENTATION[n]=d);u&&(a||(l=d.exec(this.currentLine)));)f+=a?this.currentLine.slice(n):l[1],(u=this.moveToNextLine())&&(f+="\n",a=this.isCurrentLineBlank());else u&&(f+="\n");if(u&&this.moveToPreviousLine(),">"===e){for(s="",r=0,o=(c=f.split("\n")).length;rn&&(e=!0),this.moveToPreviousLine(),e)},R.prototype.isCurrentLineEmpty=function(){var e=I.trim(this.currentLine," ");return 0===e.length||"#"===e.charAt(0)},R.prototype.isCurrentLineBlank=function(){return""===I.trim(this.currentLine," ")},R.prototype.isCurrentLineComment=function(){return"#"===I.ltrim(this.currentLine," ").charAt(0)},R.prototype.cleanup=function(e){var t,n,a,r,o,i,l,s,u,d,c;for(-1!==e.indexOf("\r")&&(e=e.split("\r\n").join("\n").split("\r").join("\n")),e=(u=this.PATTERN_YAML_HEADER.replaceAll(e,""))[0],u=u[1],this.offset+=u,c=(u=this.PATTERN_LEADING_COMMENTS.replaceAll(e,"",1))[0],1===u[1]&&(this.offset+=I.subStrCount(e,"\n")-I.subStrCount(c,"\n"),e=c),c=(u=this.PATTERN_DOCUMENT_MARKER_START.replaceAll(e,"",1))[0],1===u[1]&&(this.offset+=I.subStrCount(e,"\n")-I.subStrCount(c,"\n"),e=c,e=this.PATTERN_DOCUMENT_MARKER_END.replace(e,"")),d=-1,a=0,o=(s=e.split("\n")).length;a=!%@`]"),r.requiresDoubleQuoting=function(e){return this.PATTERN_CHARACTERS_TO_ESCAPE.test(e)},r.escapeWithDoubleQuotes=function(e){var t;return'"'+this.PATTERN_MAPPING_ESCAPEES.replace(e,(t=this,function(e){return t.MAPPING_ESCAPEES_TO_ESCAPED[e]}))+'"'},r.requiresSingleQuoting=function(e){return this.PATTERN_SINGLE_QUOTING.test(e)},r.escapeWithSingleQuotes=function(e){return"'"+e.replace(/'/g,"''")+"'"},e.exports=r},function(e,t){var i={}.hasOwnProperty,n=function(e){var t,n=o,a=e;for(t in a)i.call(a,t)&&(n[t]=a[t]);function r(){this.constructor=n}function o(e,t,n){this.message=e,this.parsedLine=t,this.snippet=n}return r.prototype=a.prototype,n.prototype=new r,n.__super__=a.prototype,o.prototype.toString=function(){return null!=this.parsedLine&&null!=this.snippet?" "+this.message+" (line "+this.parsedLine+": '"+this.snippet+"')":" "+this.message},o}(Error);e.exports=n},function(e,t,n){var f,p;function a(){}p=n(112),f=n(396),a.indentation=4,a.prototype.dump=function(e,t,n,a,r){var o,i,l,s,u,d,c;if(null==t&&(t=0),null==a&&(a=!1),null==r&&(r=null),s="",u=(n=null==n?0:n)?p.strRepeat(" ",n):"",t<=0||"object"!=typeof e||e instanceof Date||p.isEmpty(e))s+=u+f.dump(e,a,r);else if(e instanceof Array)for(o=0,l=e.length;ou&&!c&&(o=o.slice(0,u),e=C.default.createElement(m.default,{key:"_count",type:"primary",size:p,animation:!1},d(a,t))),0=D.KEYCODE.LEFT&&t<=D.KEYCODE.DOWN&&e.preventDefault(),e=void 0,t===D.KEYCODE.RIGHT||t===D.KEYCODE.DOWN?(e=o.getNextActiveKey(!0),o.handleTriggerEvent(o.props.triggerType,e)):t!==D.KEYCODE.LEFT&&t!==D.KEYCODE.UP||(e=o.getNextActiveKey(!1),o.handleTriggerEvent(o.props.triggerType,e)))},o.state={activeKey:o.getDefaultActiveKey(e)},o}l.displayName="Tab",t.default=(0,s.polyfill)(l),e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var h=f(n(2)),a=f(n(4)),r=f(n(5)),o=f(n(6)),m=f(n(0)),i=n(23),l=f(n(3)),g=f(n(13)),s=f(n(24)),u=f(n(62)),d=f(n(50)),y=f(n(79)),v=n(11),c=n(402);function f(e){return e&&e.__esModule?e:{default:e}}var p,_={float:"right",zIndex:1},b={float:"left",zIndex:1},w={dropdown:"arrow-down",prev:"arrow-left",next:"arrow-right"},M=u.default.Popup,u=(p=m.default.Component,(0,o.default)(k,p),k.prototype.componentDidMount=function(){this.props.animation||this.initialSettings(),v.events.on(window,"resize",this.onWindowResized)},k.prototype.componentDidUpdate=function(e){var t=this;clearTimeout(this.scrollTimer),this.scrollTimer=setTimeout(function(){t.scrollToActiveTab()},410),clearTimeout(this.slideTimer),this.slideTimer=setTimeout(function(){t.setSlideBtn()},410),"dropdown"!==this.props.excessMode||(0,c.tabsArrayShallowEqual)(this.props.tabs,e.tabs)||this.getDropdownItems(this.props)},k.prototype.componentWillUnmount=function(){v.events.off(window,"resize",this.onWindowResized)},k.prototype.initialSettings=function(){this.setSlideBtn(),this.getDropdownItems(this.props)},k.prototype.setOffset=function(e){var t=!(1n&&(t.current=n),this.setState(t),this.props.onPageSizeChange(e)},I.prototype.renderPageTotal=function(){var e=this.props,t=e.prefix,n=e.total,e=e.totalRender,a=this.state,r=a.currentPageSize,a=a.current;return N.default.createElement("div",{className:t+"pagination-total"},e(n,[(a-1)*r+1,a*r]))},I.prototype.renderPageItem=function(e){var t=this.props,n=t.prefix,a=t.size,r=t.link,o=t.pageNumberRender,i=t.total,l=t.pageSize,t=t.locale,s=this.state.current,i=Y(i,l),l=parseInt(e,10)===s,a={size:a,className:(0,P.default)(((s={})[n+"pagination-item"]=!0,s[n+"current"]=l,s)),onClick:l?m:this.onPageItemClick.bind(this,e)};return r&&(a.component="a",a.href=r.replace("{page}",e)),N.default.createElement(c.default,(0,O.default)({"aria-label":j.str.template(t.total,{current:e,total:i})},a,{key:e}),o(e))},I.prototype.renderPageFirst=function(e){var t=this.props,n=t.prefix,a=t.size,r=t.shape,t=t.locale,a={disabled:e<=1,size:a,className:(0,P.default)(((a={})[n+"pagination-item"]=!0,a[n+"prev"]=!0,a)),onClick:this.onPageItemClick.bind(this,e-1)},n=N.default.createElement(d.default,{type:"arrow-left",className:n+"pagination-icon-prev"});return N.default.createElement(c.default,(0,O.default)({},a,{"aria-label":j.str.template(t.labelPrev,{current:e})}),n,"arrow-only"===r||"arrow-prev-only"===r||"no-border"===r?"":t.prev)},I.prototype.renderPageLast=function(e,t){var n=this.props,a=n.prefix,r=n.size,o=n.shape,n=n.locale,r={disabled:t<=e,size:r,className:(0,P.default)(((t={})[a+"pagination-item"]=!0,t[a+"next"]=!0,t)),onClick:this.onPageItemClick.bind(this,e+1)},t=N.default.createElement(d.default,{type:"arrow-right",className:a+"pagination-icon-next"});return N.default.createElement(c.default,(0,O.default)({},r,{"aria-label":j.str.template(n.labelNext,{current:e})}),"arrow-only"===o||"no-border"===o?"":n.next,t)},I.prototype.renderPageEllipsis=function(e){var t=this.props.prefix;return N.default.createElement(d.default,{className:t+"pagination-ellipsis "+t+"pagination-icon-ellipsis",type:"ellipsis",key:"ellipsis-"+e})},I.prototype.renderPageJump=function(){var t=this,e=this.props,n=e.prefix,a=e.size,e=e.locale,r=this.state.inputValue;return[N.default.createElement("span",{className:n+"pagination-jump-text"},e.goTo),N.default.createElement(f.default,{className:n+"pagination-jump-input",type:"text","aria-label":e.inputAriaLabel,size:a,value:r,onChange:this.onInputChange.bind(this),onKeyDown:function(e){e.keyCode===j.KEYCODE.ENTER&&t.handleJump(e)}}),N.default.createElement("span",{className:n+"pagination-jump-text"},e.page),N.default.createElement(c.default,{className:n+"pagination-jump-go",size:a,onClick:this.handleJump},e.go)]},I.prototype.renderPageDisplay=function(e,t){var n=this.props,a=n.prefix,n=n.pageNumberRender;return N.default.createElement("span",{className:a+"pagination-display"},N.default.createElement("em",null,n(e)),"/",n(t))},I.prototype.renderPageList=function(e,t){var n=this.props,a=n.prefix,n=n.pageShowCount,r=[];if(t<=n)for(var o=1;o<=t;o++)r.push(this.renderPageItem(o));else{var n=n-3,i=parseInt(n/2,10),l=void 0,s=void 0;r.push(this.renderPageItem(1)),s=e+i,(l=e-i)<=1&&(s=(l=2)+n),2=e.length&&-1=this.props.children.length?(this.update(this.props),this.changeSlide({message:"index",index:this.props.children.length-this.props.slidesToShow,currentSlide:this.state.currentSlide})):s.obj.shallowEqual(e,this.props)||this.update(this.props),this.adaptHeight()},p.prototype.componentWillUnmount=function(){this.animationEndCallback&&clearTimeout(this.animationEndCallback),s.events.off(window,"resize",this.onWindowResized),this.state.autoPlayTimer&&clearInterval(this.state.autoPlayTimer)},p.prototype.onWindowResized=function(){this.update(this.props),this.setState({animating:!1}),clearTimeout(this.animationEndCallback),delete this.animationEndCallback},p.prototype.slickGoTo=function(e){"number"==typeof e&&this.changeSlide({message:"index",index:e,currentSlide:this.state.currentSlide})},p.prototype.onEnterArrow=function(e){this.arrowHoverHandler(e)},p.prototype.onLeaveArrow=function(){this.arrowHoverHandler()},p.prototype._instanceRefHandler=function(e,t){this[e]=t},p.prototype.render=function(){var e=this.props,t=e.prefix,n=e.animation,a=e.arrows,r=e.arrowSize,o=e.arrowPosition,i=e.arrowDirection,l=e.dots,s=e.dotsClass,u=e.cssEase,d=e.speed,c=e.infinite,f=e.centerMode,p=e.centerPadding,h=e.lazyLoad,m=e.dotsDirection,g=e.rtl,y=e.slidesToShow,v=e.slidesToScroll,_=e.variableWidth,b=e.vertical,w=e.verticalSwiping,M=e.focusOnSelect,k=e.children,S=e.dotsRender,e=e.triggerType,E=this.state,x=E.currentSlide,C=E.lazyLoadedList,L=E.slideCount,T=E.slideWidth,D=E.slideHeight,O=E.trackStyle,N=E.listHeight,E=E.dragging,u={prefix:t,animation:n,cssEase:u,speed:d,infinite:c,centerMode:f,focusOnSelect:M?this.selectHandler:null,currentSlide:x,lazyLoad:h,lazyLoadedList:C,rtl:g,slideWidth:T,slideHeight:D,slidesToShow:y,slidesToScroll:v,slideCount:L,trackStyle:O,variableWidth:_,vertical:b,verticalSwiping:w,triggerType:e},d=void 0,h=(!0===l&&yt.startX?1:-1),!0===this.props.verticalSwiping&&(t.swipeLength=Math.round(Math.sqrt(Math.pow(t.curY-t.startY,2))),a=t.curY>t.startY?1:-1),r=this.state.currentSlide,l=Math.ceil(this.state.slideCount/this.props.slidesToScroll),o=this.swipeDirection(this.state.touchObject),i=t.swipeLength,!1===this.props.infinite&&(0===r&&"right"===o||l<=r+1&&"left"===o)&&(i=t.swipeLength*this.props.edgeFriction,!1===this.state.edgeDragged&&this.props.edgeEvent&&(this.props.edgeEvent(o),this.setState({edgeDragged:!0}))),!1===this.state.swiped&&this.props.swipeEvent&&(this.props.swipeEvent(o),this.setState({swiped:!0})),this.setState({touchObject:t,swipeLeft:l=n+i*a,trackStyle:(0,u.getTrackCSS)((0,s.default)({left:l},this.props,this.state))}),Math.abs(t.curX-t.startX)<.8*Math.abs(t.curY-t.startY)||4t[t.length-1])e=t[t.length-1];else for(var a in t){if(e-1*n.state.swipeLeft)return t=e,!1}else if(e.offsetLeft-a+(n.getWidth(e)||0)/2>-1*n.state.swipeLeft)return t=e,!1;return!0}),Math.abs(t.dataset.index-this.state.currentSlide)||1):this.props.slidesToScroll},swipeEnd:function(e){if(this.state.dragging){var t=this.state.touchObject,n=this.state.listWidth/this.props.touchThreshold,a=this.swipeDirection(t);if(this.props.verticalSwiping&&(n=this.state.listHeight/this.props.touchThreshold),this.setState({dragging:!1,edgeDragged:!1,swiped:!1,swipeLeft:null,touchObject:{}}),t.swipeLength)if(t.swipeLength>n){e.preventDefault();var r=void 0,o=void 0;switch(a){case"left":case"down":o=this.state.currentSlide+this.getSlideCount(),r=this.props.swipeToSlide?this.checkNavigable(o):o,this.setState({currentDirection:0});break;case"right":case"up":o=this.state.currentSlide-this.getSlideCount(),r=this.props.swipeToSlide?this.checkNavigable(o):o,this.setState({currentDirection:1});break;default:r=this.state.currentSlide}this.slideHandler(r)}else{t=(0,u.getTrackLeft)((0,s.default)({slideIndex:this.state.currentSlide,trackRef:this.track},this.props,this.state));this.setState({trackStyle:(0,u.getTrackAnimateCSS)((0,s.default)({left:t},this.props,this.state))})}}else this.props.swipe&&e.preventDefault()},onInnerSliderEnter:function(){this.props.autoplay&&this.props.pauseOnHover&&this.pause()},onInnerSliderLeave:function(){this.props.autoplay&&this.props.pauseOnHover&&this.autoPlay()}},e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var g=a(n(2)),d=a(n(0)),c=a(n(23)),y=n(405);function a(e){return e&&e.__esModule?e:{default:e}}t.default={initialize:function(t){var n=this,e=c.default.findDOMNode(this.list),a=d.default.Children.count(t.children),r=this.getWidth(e)||0,o=this.getWidth(c.default.findDOMNode(this.track))||0,i=void 0,e=(i=t.vertical?r:(r-(t.centerMode&&2*parseInt(t.centerPadding)))/t.slidesToShow,this.getHeight(e.querySelector('[data-index="0"]'))||0),l=e*t.slidesToShow,s=t.slidesToShow||1,u="activeIndex"in t?t.activeIndex:t.defaultActiveIndex,s=t.rtl?a-1-(s-1)-u:u;this.setState({slideCount:a,slideWidth:i,listWidth:r,trackWidth:o,currentSlide:s,slideHeight:e,listHeight:l},function(){var e=(0,y.getTrackLeft)((0,g.default)({slideIndex:n.state.currentSlide,trackRef:n.track},t,n.state)),e=(0,y.getTrackCSS)((0,g.default)({left:e},t,n.state));n.setState({trackStyle:e}),n.autoPlay()})},update:function(e){this.initialize(e)},getWidth:function(e){return"clientWidth"in e?e.clientWidth:e&&e.getBoundingClientRect().width},getHeight:function(e){return"clientHeight"in e?e.clientHeight:e&&e.getBoundingClientRect().height},adaptHeight:function(){var e,t;this.props.adaptiveHeight&&(t='[data-index="'+this.state.currentSlide+'"]',this.list&&(t=(e=c.default.findDOMNode(this.list)).querySelector(t).offsetHeight,e.style.height=t+"px"))},canGoNext:function(e){var t=!0;return e.infinite||(e.centerMode?e.currentSlide>=e.slideCount-1&&(t=!1):(e.slideCount<=e.slidesToShow||e.currentSlide>=e.slideCount-e.slidesToShow)&&(t=!1)),t},slideHandler:function(e){var t=this,n=this.props.rtl,a=void 0,r=void 0,o=void 0;if(!this.props.waitForAnimate||!this.state.animating){if("fade"===this.props.animation)return r=this.state.currentSlide,!1===this.props.infinite&&(e<0||e>=this.state.slideCount)?void 0:(a=e<0?e+this.state.slideCount:e>=this.state.slideCount?e-this.state.slideCount:e,this.props.lazyLoad&&this.state.lazyLoadedList.indexOf(a)<0&&this.setState({lazyLoadedList:this.state.lazyLoadedList.concat(a)}),o=function(){t.setState({animating:!1}),t.props.onChange(a),delete t.animationEndCallback},this.props.onBeforeChange(this.state.currentSlide,a),this.setState({animating:!0,currentSlide:a},function(){this.animationEndCallback=setTimeout(o,this.props.speed+20)}),void this.autoPlay());a=e,n?a<0?!1===this.props.infinite?r=0:this.state.slideCount%this.props.slidesToScroll!=0?a+this.props.slidesToScroll<=0?(r=this.state.slideCount+a,a=this.state.slideCount-this.props.slidesToScroll):r=a=0:r=this.state.slideCount+a:r=a>=this.state.slideCount?!1===this.props.infinite?this.state.slideCount-this.props.slidesToShow:this.state.slideCount%this.props.slidesToScroll!=0?0:a-this.state.slideCount:a:r=a<0?!1===this.props.infinite?0:this.state.slideCount%this.props.slidesToScroll!=0?this.state.slideCount-this.state.slideCount%this.props.slidesToScroll:this.state.slideCount+a:a>=this.state.slideCount?!1===this.props.infinite?this.state.slideCount-this.props.slidesToShow:this.state.slideCount%this.props.slidesToScroll!=0?0:a-this.state.slideCount:a;var i,e=(0,y.getTrackLeft)((0,g.default)({slideIndex:a,trackRef:this.track},this.props,this.state)),l=(0,y.getTrackLeft)((0,g.default)({slideIndex:r,trackRef:this.track},this.props,this.state));if(!1===this.props.infinite&&(e=l),this.props.lazyLoad){for(var s=!0,u=[],d=this.state.slideCount,c=a<0?d+a:r,f=c;f=s.activeIndex?"visible":"hidden",d.transition="opacity "+s.speed+"ms "+s.cssEase,d.WebkitTransition="opacity "+s.speed+"ms "+s.cssEase,s.vertical?d.top=-s.activeIndex*s.slideHeight:d.left=-s.activeIndex*s.slideWidth),s.vertical&&(d.width="100%"),d),u=(d=(0,v.default)({activeIndex:e},c),a=d.prefix,u=r=i=void 0,o=(u=d.rtl?d.slideCount-1-d.activeIndex:d.activeIndex)<0||u>=d.slideCount,d.centerMode?(n=Math.floor(d.slidesToShow/2),r=(u-d.currentSlide)%d.slideCount==0,u>d.currentSlide-n-1&&u<=d.currentSlide+n&&(i=!0)):i=d.currentSlide<=u&&u=u,u=(0,k.default)(((v={})[o+"upload-list-item"]=!0,v[o+"hidden"]=u,v)),v=this.props.children||i.card.addPhoto,c=r?S.func.prevent:c,_=S.obj.pickOthers(C.propTypes,this.props),b=S.obj.pickOthers(E.default.propTypes,_);if(h&&"function"==typeof m)return e=(0,k.default)(((e={})[o+"form-preview"]=!0,e[l]=!!l,e)),M.default.createElement("div",{style:s,className:e},m(this.state.value,this.props));return M.default.createElement(E.default,(0,w.default)({className:l,style:s,listType:"card",closable:!0,locale:i,value:this.state.value,onRemove:c,onCancel:f,onPreview:d,itemRender:g,isPreview:h,uploader:this.uploaderRef,reUpload:y,showDownload:n},_),M.default.createElement(x.default,(0,w.default)({},b,{shape:"card",prefix:o,disabled:r,action:a,timeout:p,isPreview:h,value:this.state.value,onProgress:this.onProgress,onChange:this.onChange,ref:function(e){return t.saveRef(e)},className:u}),v))},d=n=C,n.displayName="Card",n.propTypes={prefix:l.default.string,locale:l.default.object,children:l.default.object,value:l.default.oneOfType([l.default.array,l.default.object]),defaultValue:l.default.oneOfType([l.default.array,l.default.object]),onPreview:l.default.func,onChange:l.default.func,onRemove:l.default.func,onCancel:l.default.func,itemRender:l.default.func,reUpload:l.default.bool,showDownload:l.default.bool,onProgress:l.default.func,isPreview:l.default.bool,renderPreview:l.default.func},n.defaultProps={prefix:"next-",locale:u.default.Upload,showDownload:!0,onChange:S.func.noop,onPreview:S.func.noop,onProgress:S.func.noop},a=function(){var n=this;this.onProgress=function(e,t){n.setState({value:e}),n.props.onProgress(e,t)},this.onChange=function(e,t){"value"in n.props||n.setState({value:e}),n.props.onChange(e,t)}};var f,i=d;function C(e){(0,r.default)(this,C);var t=(0,o.default)(this,f.call(this,e)),n=(a.call(t),void 0),n="value"in e?e.value:e.defaultValue;return t.state={value:Array.isArray(n)?n:[],uploaderRef:t.uploaderRef},t}t.default=(0,s.polyfill)(i),e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var u=m(n(2)),d=m(n(12)),o=m(n(4)),i=m(n(5)),a=m(n(6)),c=m(n(0)),r=m(n(3)),f=m(n(13)),p=m(n(24)),l=n(11),s=m(n(44)),h=m(n(180));function m(e){return e&&e.__esModule?e:{default:e}}g=c.default.Component,(0,a.default)(y,g),y.prototype.abort=function(e){this.uploaderRef.abort(e)},y.prototype.startUpload=function(){this.uploaderRef.startUpload()},y.prototype.render=function(){var e=this.props,t=e.className,n=e.style,a=e.shape,r=e.locale,o=e.prefix,i=e.listType,e=(0,d.default)(e,["className","style","shape","locale","prefix","listType"]),l=o+"upload-drag",t=(0,f.default)(((s={})[l]=!0,s[l+"-over"]=this.state.dragOver,s[t]=!!t,s)),s=this.props.children||c.default.createElement("div",{className:t},c.default.createElement("p",{className:l+"-icon"},c.default.createElement(p.default,{size:"large",className:l+"-upload-icon"})),c.default.createElement("p",{className:l+"-text"},r.drag.text),c.default.createElement("p",{className:l+"-hint"},r.drag.hint));return c.default.createElement(h.default,(0,u.default)({},e,{prefix:o,shape:a,listType:i,dragable:!0,style:n,onDragOver:this.onDragOver,onDragLeave:this.onDragLeave,onDrop:this.onDrop,ref:this.saveUploaderRef}),s)},a=n=y,n.propTypes={prefix:r.default.string,locale:r.default.object,shape:r.default.string,onDragOver:r.default.func,onDragLeave:r.default.func,onDrop:r.default.func,limit:r.default.number,className:r.default.string,style:r.default.object,defaultValue:r.default.array,children:r.default.node,listType:r.default.string,timeout:r.default.number},n.defaultProps={prefix:"next-",onDragOver:l.func.noop,onDragLeave:l.func.noop,onDrop:l.func.noop,locale:s.default.Upload};var g,r=a;function y(){var e,t;(0,o.default)(this,y);for(var n=arguments.length,a=Array(n),r=0;r> actual = namespaceController.getNamespaces(); + assertTrue(actual.ok()); + assertEquals(200, actual.getCode()); + assertEquals(namespace, actual.getData().get(0)); } @Test - public void getNamespaceByNamespaceId() throws Exception { - String url = "/v1/console/namespaces"; - Mockito.when(persistService.findTenantByKp(any(String.class))).thenReturn(null); - Mockito.when(persistService.configInfoCount(any(String.class))).thenReturn(0); - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(url) - .param("show", "all").param("namespaceId", ""); - Assert.assertEquals(200, mockmvc.perform(builder).andReturn().getResponse().getStatus()); + public void testGetNamespaceByNamespaceId() throws Exception { + NamespaceAllInfo namespace = new NamespaceAllInfo("", "public", 0, 0, 0, ""); + when(namespaceOperationService.getNamespace("")).thenReturn(namespace); + assertEquals(namespace, namespaceController.getNamespace("")); } @Test - public void createNamespace() throws Exception { - - try { - persistService.insertTenantInfoAtomic(String.valueOf(1), "testId", "name", - "testDesc", "resourceId", 3000L); - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post(NAMESPACE_URL) - .param("customNamespaceId", "nsId") - .param("namespaceName", "nsService").param("namespaceDesc", "desc"); - Assert.assertEquals(200, mockmvc.perform(builder).andReturn().getResponse().getStatus()); - } catch (Exception e) { - Assert.assertNull(e); + public void testCreateNamespaceWithCustomId() throws Exception { + namespaceController.createNamespace("test-Id", "testName", "testDesc"); + verify(namespaceOperationService).createNamespace("test-Id", "testName", "testDesc"); + } + + @Test + public void testCreateNamespaceWithIllegalCustomId() throws Exception { + assertFalse(namespaceController.createNamespace("test.Id", "testName", "testDesc")); + verify(namespaceOperationService, never()).createNamespace("test.Id", "testName", "testDesc"); + } + + @Test + public void testCreateNamespaceWithLongCustomId() throws Exception { + StringBuilder longId = new StringBuilder(); + for (int i = 0; i < 129; i++) { + longId.append("a"); } - + assertFalse(namespaceController.createNamespace(longId.toString(), "testName", "testDesc")); + verify(namespaceOperationService, never()).createNamespace(longId.toString(), "testName", "testDesc"); } @Test - public void checkNamespaceIdExist() throws Exception { - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(NAMESPACE_URL) - .param("checkNamespaceIdExist", "true").param("customNamespaceId", "12"); - MockHttpServletResponse response = mockmvc.perform(builder).andReturn().getResponse(); - Assert.assertEquals("false", response.getContentAsString()); - Mockito.when(persistService.tenantInfoCountByTenantId("")).thenReturn(0); - Mockito.when(persistService.tenantInfoCountByTenantId("123")).thenReturn(1); + public void testCreateNamespaceWithAutoId() throws Exception { + assertFalse(namespaceController.createNamespace("", "testName", "testDesc")); + verify(namespaceOperationService) + .createNamespace(matches("[A-Za-z\\d]{8}-[A-Za-z\\d]{4}-[A-Za-z\\d]{4}-[A-Za-z\\d]{4}-[A-Za-z\\d]{12}"), + eq("testName"), eq("testDesc")); } @Test - public void editNamespace() { - try { - persistService.updateTenantNameAtomic("1", "testId", "nsShowName", "namespaceDesc"); - createNamespace(); - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put(NAMESPACE_URL) - .param("namespace", "testId").param("namespaceShowName", "nsShowName") - .param("namespaceDesc", "desc"); - Assert.assertEquals("true", mockmvc.perform(builder).andReturn().getResponse().getContentAsString()); - } catch (Exception e) { - Assert.assertNull(e); - } + public void testCreateNamespaceFailure() throws NacosException { + when(namespaceOperationService.createNamespace(anyString(), anyString(), anyString())) + .thenThrow(new NacosException(500, "test")); + assertFalse(namespaceController.createNamespace("", "testName", "testDesc")); + } + + @Test + public void testCheckNamespaceIdExist() throws Exception { + when(persistService.tenantInfoCountByTenantId("public")).thenReturn(1); + when(persistService.tenantInfoCountByTenantId("123")).thenReturn(0); + assertFalse(namespaceController.checkNamespaceIdExist("")); + assertTrue(namespaceController.checkNamespaceIdExist("public")); + assertFalse(namespaceController.checkNamespaceIdExist("123")); + } + + @Test + public void testEditNamespace() { + namespaceController.editNamespace("test-Id", "testName", "testDesc"); + verify(namespaceOperationService).editNamespace("test-Id", "testName", "testDesc"); } @Test public void deleteConfig() throws Exception { - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.delete(NAMESPACE_URL) - .param("namespaceId", ""); - Assert.assertEquals("true", mockmvc.perform(builder).andReturn().getResponse().getContentAsString()); + namespaceController.deleteNamespace("test-Id"); + verify(namespaceOperationService).removeNamespace("test-Id"); } } diff --git a/console/src/test/java/com/alibaba/nacos/console/controller/v2/NamespaceControllerV2Test.java b/console/src/test/java/com/alibaba/nacos/console/controller/v2/NamespaceControllerV2Test.java new file mode 100644 index 000000000..8983adecc --- /dev/null +++ b/console/src/test/java/com/alibaba/nacos/console/controller/v2/NamespaceControllerV2Test.java @@ -0,0 +1,137 @@ +/* + * Copyright 1999-2022 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.nacos.console.controller.v2; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.console.enums.NamespaceTypeEnum; +import com.alibaba.nacos.console.model.Namespace; +import com.alibaba.nacos.console.model.NamespaceAllInfo; +import com.alibaba.nacos.console.model.form.NamespaceForm; +import com.alibaba.nacos.console.service.NamespaceOperationService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * NamespaceControllerV2Test. + * @author dongyafei + * @date 2022/8/16 + */ +@RunWith(MockitoJUnitRunner.class) +public class NamespaceControllerV2Test { + + private NamespaceControllerV2 namespaceControllerV2; + + @Mock + private NamespaceOperationService namespaceOperationService; + + private static final String TEST_NAMESPACE_ID = "testId"; + + private static final String TEST_NAMESPACE_NAME = "testName"; + + private static final String TEST_NAMESPACE_DESC = "testDesc"; + + @Before + public void setUp() throws Exception { + namespaceControllerV2 = new NamespaceControllerV2(namespaceOperationService); + } + + @Test + public void testGetNamespaceList() { + Namespace namespace = new Namespace(); + namespace.setNamespace(TEST_NAMESPACE_ID); + namespace.setNamespaceShowName(TEST_NAMESPACE_NAME); + namespace.setNamespaceDesc(TEST_NAMESPACE_DESC); + List namespaceList = Collections.singletonList(namespace); + when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList); + + Result> actualResult = namespaceControllerV2.getNamespaceList(); + verify(namespaceOperationService).getNamespaceList(); + assertEquals(ErrorCode.SUCCESS.getCode(), actualResult.getCode()); + + List actualList = actualResult.getData(); + Namespace actualNamespace = actualList.get(0); + assertEquals(namespaceList.size(), actualList.size()); + assertEquals(namespace.getNamespace(), actualNamespace.getNamespace()); + assertEquals(namespace.getNamespaceShowName(), actualNamespace.getNamespaceShowName()); + assertEquals(namespace.getNamespaceDesc(), actualNamespace.getNamespaceDesc()); + + } + + @Test + public void testGetNamespace() throws NacosException { + NamespaceAllInfo namespaceAllInfo = new NamespaceAllInfo(TEST_NAMESPACE_ID, TEST_NAMESPACE_NAME, 200, + 1, NamespaceTypeEnum.GLOBAL.getType(), TEST_NAMESPACE_DESC); + when(namespaceOperationService.getNamespace(TEST_NAMESPACE_ID)).thenReturn(namespaceAllInfo); + Result result = namespaceControllerV2.getNamespace(TEST_NAMESPACE_ID); + verify(namespaceOperationService).getNamespace(TEST_NAMESPACE_ID); + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + NamespaceAllInfo namespace = result.getData(); + assertEquals(namespaceAllInfo.getNamespace(), namespace.getNamespace()); + assertEquals(namespaceAllInfo.getNamespaceShowName(), namespace.getNamespaceShowName()); + assertEquals(namespaceAllInfo.getNamespaceDesc(), namespace.getNamespaceDesc()); + assertEquals(namespaceAllInfo.getQuota(), namespace.getQuota()); + assertEquals(namespaceAllInfo.getConfigCount(), namespace.getConfigCount()); + } + + @Test + public void testCreateNamespace() throws NacosException { + when(namespaceOperationService + .createNamespace(TEST_NAMESPACE_ID, TEST_NAMESPACE_NAME, TEST_NAMESPACE_DESC)).thenReturn(true); + Result result = namespaceControllerV2 + .createNamespace(new NamespaceForm(TEST_NAMESPACE_ID, TEST_NAMESPACE_NAME, TEST_NAMESPACE_DESC)); + + verify(namespaceOperationService).createNamespace(TEST_NAMESPACE_ID, TEST_NAMESPACE_NAME, TEST_NAMESPACE_DESC); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(true, result.getData()); + } + + @Test + public void testEditNamespace() throws NacosException { + when(namespaceOperationService.editNamespace(TEST_NAMESPACE_ID, TEST_NAMESPACE_NAME, TEST_NAMESPACE_DESC)).thenReturn(true); + Result result = namespaceControllerV2 + .editNamespace(new NamespaceForm(TEST_NAMESPACE_ID, TEST_NAMESPACE_NAME, TEST_NAMESPACE_DESC)); + + verify(namespaceOperationService).editNamespace(TEST_NAMESPACE_ID, TEST_NAMESPACE_NAME, TEST_NAMESPACE_DESC); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(true, result.getData()); + } + + @Test + public void testDeleteNamespace() { + when(namespaceOperationService.removeNamespace(TEST_NAMESPACE_ID)).thenReturn(true); + Result result = namespaceControllerV2.deleteNamespace(TEST_NAMESPACE_ID); + + verify(namespaceOperationService).removeNamespace(TEST_NAMESPACE_ID); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(true, result.getData()); + } +} diff --git a/console/src/test/java/com/alibaba/nacos/console/service/NamespaceOperationServiceTest.java b/console/src/test/java/com/alibaba/nacos/console/service/NamespaceOperationServiceTest.java new file mode 100644 index 000000000..4a153310f --- /dev/null +++ b/console/src/test/java/com/alibaba/nacos/console/service/NamespaceOperationServiceTest.java @@ -0,0 +1,139 @@ +/* + * Copyright 1999-2022 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.nacos.console.service; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.config.server.model.TenantInfo; +import com.alibaba.nacos.config.server.service.repository.PersistService; +import com.alibaba.nacos.console.enums.NamespaceTypeEnum; +import com.alibaba.nacos.console.model.Namespace; +import com.alibaba.nacos.console.model.NamespaceAllInfo; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * NamespaceOperationServiceTest. + * @author dongyafei + * @date 2022/8/16 + */ +@RunWith(MockitoJUnitRunner.class) +public class NamespaceOperationServiceTest { + + private NamespaceOperationService namespaceOperationService; + + @Mock + private PersistService persistService; + + private static final String TEST_NAMESPACE_ID = "testId"; + + private static final String TEST_NAMESPACE_NAME = "testName"; + + private static final String TEST_NAMESPACE_DESC = "testDesc"; + + private static final String DEFAULT_NAMESPACE = "public"; + + private static final int DEFAULT_QUOTA = 200; + + private static final String DEFAULT_KP = "1"; + + @Before + public void setUp() throws Exception { + namespaceOperationService = new NamespaceOperationService(persistService); + } + + @Test + public void testGetNamespaceList() { + TenantInfo tenantInfo = new TenantInfo(); + tenantInfo.setTenantId(TEST_NAMESPACE_ID); + tenantInfo.setTenantName(TEST_NAMESPACE_NAME); + tenantInfo.setTenantDesc(TEST_NAMESPACE_DESC); + when(persistService.findTenantByKp(DEFAULT_KP)).thenReturn(Collections.singletonList(tenantInfo)); + when(persistService.configInfoCount(anyString())).thenReturn(1); + + List list = namespaceOperationService.getNamespaceList(); + assertEquals(2, list.size()); + Namespace namespaceA = list.get(0); + assertEquals("", namespaceA.getNamespace()); + assertEquals(DEFAULT_NAMESPACE, namespaceA.getNamespaceShowName()); + assertEquals(DEFAULT_QUOTA, namespaceA.getQuota()); + assertEquals(1, namespaceA.getConfigCount()); + + Namespace namespaceB = list.get(1); + assertEquals(TEST_NAMESPACE_ID, namespaceB.getNamespace()); + assertEquals(TEST_NAMESPACE_NAME, namespaceB.getNamespaceShowName()); + assertEquals(1, namespaceB.getConfigCount()); + } + + @Test(expected = NacosApiException.class) + public void testGetNamespace() throws NacosException { + + TenantInfo tenantInfo = new TenantInfo(); + tenantInfo.setTenantId(TEST_NAMESPACE_ID); + tenantInfo.setTenantName(TEST_NAMESPACE_NAME); + tenantInfo.setTenantDesc(TEST_NAMESPACE_DESC); + when(persistService.findTenantByKp(DEFAULT_KP, TEST_NAMESPACE_ID)).thenReturn(tenantInfo); + when(persistService.findTenantByKp(DEFAULT_KP, "test_not_exist_id")).thenReturn(null); + when(persistService.configInfoCount(anyString())).thenReturn(1); + NamespaceAllInfo namespaceAllInfo = new NamespaceAllInfo(TEST_NAMESPACE_ID, TEST_NAMESPACE_NAME, DEFAULT_QUOTA, + 1, NamespaceTypeEnum.GLOBAL.getType(), TEST_NAMESPACE_DESC); + NamespaceAllInfo namespace = namespaceOperationService.getNamespace(TEST_NAMESPACE_ID); + assertEquals(namespaceAllInfo.getNamespace(), namespace.getNamespace()); + assertEquals(namespaceAllInfo.getNamespaceShowName(), namespace.getNamespaceShowName()); + assertEquals(namespaceAllInfo.getNamespaceDesc(), namespace.getNamespaceDesc()); + assertEquals(namespaceAllInfo.getQuota(), namespace.getQuota()); + assertEquals(namespaceAllInfo.getConfigCount(), namespace.getConfigCount()); + + namespaceOperationService.getNamespace("test_not_exist_id"); + + } + + @Test + public void testCreateNamespace() throws NacosException { + when(persistService.tenantInfoCountByTenantId(anyString())).thenReturn(0); + namespaceOperationService.createNamespace(TEST_NAMESPACE_ID, TEST_NAMESPACE_NAME, TEST_NAMESPACE_DESC); + verify(persistService) + .insertTenantInfoAtomic(eq(DEFAULT_KP), eq(TEST_NAMESPACE_ID), eq(TEST_NAMESPACE_NAME), eq(TEST_NAMESPACE_DESC), + any(), anyLong()); + } + + @Test + public void testEditNamespace() { + namespaceOperationService.editNamespace(TEST_NAMESPACE_ID, TEST_NAMESPACE_NAME, TEST_NAMESPACE_DESC); + verify(persistService).updateTenantNameAtomic(DEFAULT_KP, TEST_NAMESPACE_ID, TEST_NAMESPACE_NAME, TEST_NAMESPACE_DESC); + } + + @Test + public void testRemoveNamespace() { + namespaceOperationService.removeNamespace(TEST_NAMESPACE_ID); + verify(persistService).removeTenantInfoAtomic(DEFAULT_KP, TEST_NAMESPACE_ID); + } +} diff --git a/core/pom.xml b/core/pom.xml index 2052b1f73..08d218d41 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -63,6 +63,10 @@ ${project.groupId} nacos-auth + + com.alibaba.nacos + nacos-trace-plugin + org.springframework.boot diff --git a/core/src/main/java/com/alibaba/nacos/core/cluster/MemberUtil.java b/core/src/main/java/com/alibaba/nacos/core/cluster/MemberUtil.java index 3a81aeb08..8dce8846a 100644 --- a/core/src/main/java/com/alibaba/nacos/core/cluster/MemberUtil.java +++ b/core/src/main/java/com/alibaba/nacos/core/cluster/MemberUtil.java @@ -18,9 +18,9 @@ package com.alibaba.nacos.core.cluster; import com.alibaba.nacos.common.utils.ExceptionUtil; import com.alibaba.nacos.common.utils.InternetAddressUtil; +import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.utils.Loggers; import com.alibaba.nacos.sys.env.EnvUtil; -import com.alibaba.nacos.common.utils.StringUtils; import java.time.LocalDateTime; import java.util.ArrayList; @@ -31,7 +31,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.concurrent.ThreadLocalRandom; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -145,6 +144,30 @@ public class MemberUtil { } } + /** + * Successful processing of the operation on the node and update metadata. + * + * @param member {@link Member} + * @since 2.1.2 + */ + public static void onSuccess(final ServerMemberManager manager, final Member member, final Member receivedMember) { + if (isMetadataChanged(member, receivedMember)) { + manager.getMemberAddressInfos().add(member.getAddress()); + member.setState(NodeState.UP); + member.setFailAccessCnt(0); + member.setExtendInfo(receivedMember.getExtendInfo()); + member.setAbilities(receivedMember.getAbilities()); + manager.notifyMemberChange(member); + } else { + onSuccess(manager, member); + } + } + + private static boolean isMetadataChanged(Member expected, Member actual) { + return !Objects.equals(expected.getAbilities(), actual.getAbilities()) || isBasicInfoChangedInExtendInfo( + expected, actual); + } + public static void onFail(final ServerMemberManager manager, final Member member) { // To avoid null pointer judgments, pass in one NONE_EXCEPTION onFail(manager, member, ExceptionUtil.NONE_EXCEPTION); @@ -161,7 +184,8 @@ public class MemberUtil { final NodeState old = member.getState(); member.setState(NodeState.SUSPICIOUS); member.setFailAccessCnt(member.getFailAccessCnt() + 1); - int maxFailAccessCnt = EnvUtil.getProperty(MEMBER_FAIL_ACCESS_CNT_PROPERTY, Integer.class, DEFAULT_MEMBER_FAIL_ACCESS_CNT); + int maxFailAccessCnt = EnvUtil + .getProperty(MEMBER_FAIL_ACCESS_CNT_PROPERTY, Integer.class, DEFAULT_MEMBER_FAIL_ACCESS_CNT); // If the number of consecutive failures to access the target node reaches // a maximum, or the link request is rejected, the state is directly down @@ -192,34 +216,6 @@ public class MemberUtil { } } - /** - * We randomly pick k nodes. - * - * @param members member list - * @param filter filter {@link Predicate} - * @param k node number - * @return target members - */ - @SuppressWarnings("PMD.UndefineMagicConstantRule") - public static Collection kRandom(Collection members, Predicate filter, int k) { - - Set kMembers = new HashSet<>(); - - // Here thinking similar consul gossip protocols random k node - int totalSize = members.size(); - Member[] membersArray = members.toArray(new Member[totalSize]); - ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current(); - for (int i = 0; i < 3 * totalSize && kMembers.size() < k; i++) { - int idx = threadLocalRandom.nextInt(totalSize); - Member member = membersArray[idx]; - if (filter.test(member)) { - kMembers.add(member); - } - } - - return kMembers; - } - /** * Default configuration format resolution, only NACos-Server IP or IP :port or hostname: Port information. */ @@ -265,7 +261,7 @@ public class MemberUtil { */ public static boolean isBasicInfoChanged(Member actual, Member expected) { if (null == expected) { - return null == actual; + return null != actual; } if (!expected.getIp().equals(actual.getIp())) { return true; diff --git a/core/src/main/java/com/alibaba/nacos/core/cluster/ServerMemberManager.java b/core/src/main/java/com/alibaba/nacos/core/cluster/ServerMemberManager.java index 16c6824a5..f0245e5e3 100644 --- a/core/src/main/java/com/alibaba/nacos/core/cluster/ServerMemberManager.java +++ b/core/src/main/java/com/alibaba/nacos/core/cluster/ServerMemberManager.java @@ -32,6 +32,7 @@ import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacos.common.utils.ExceptionUtil; +import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.VersionUtils; import com.alibaba.nacos.core.ability.ServerAbilityInitializer; @@ -46,7 +47,6 @@ import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.utils.InetUtils; import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.context.ApplicationListener; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import javax.annotation.PreDestroy; @@ -142,7 +142,6 @@ public class ServerMemberManager implements ApplicationListener(); EnvUtil.setContextPath(servletContext.getContextPath()); - init(); } @@ -179,24 +178,6 @@ public class ServerMemberManager implements ApplicationListener() { @Override public void onReceive(RestResult result) { - if (result.getCode() == HttpStatus.NOT_IMPLEMENTED.value() - || result.getCode() == HttpStatus.NOT_FOUND.value()) { - Loggers.CLUSTER - .warn("{} version is too low, it is recommended to upgrade the version : {}", - target, VersionUtils.version); - Member memberNew = null; - if (target.getExtendVal(MemberMetaDataConstants.VERSION) != null) { - memberNew = target.copy(); - // Clean up remote version info. - // This value may still stay in extend info when remote server has been downgraded to old version. - memberNew.delExtendVal(MemberMetaDataConstants.VERSION); - memberNew.delExtendVal(MemberMetaDataConstants.READY_TO_UPGRADE); - Loggers.CLUSTER.warn("{} : Clean up version info," - + " target has been downgrade to old version.", memberNew); - } - if (target.getAbilities() != null - && target.getAbilities().getRemoteAbility() != null && target.getAbilities() - .getRemoteAbility().isSupportRemoteConnection()) { - if (memberNew == null) { - memberNew = target.copy(); - } - memberNew.getAbilities().getRemoteAbility().setSupportRemoteConnection(false); - Loggers.CLUSTER - .warn("{} : Clear support remote connection flag,target may rollback version ", - memberNew); - } - if (memberNew != null) { - update(memberNew); - } - return; - } if (result.ok()) { - MemberUtil.onSuccess(ServerMemberManager.this, target); + handleReportResult(result.getData(), target); } else { Loggers.CLUSTER.warn("failed to report new info to target node : {}, result : {}", target.getAddress(), result); @@ -597,6 +566,25 @@ public class ServerMemberManager implements ApplicationListener self() { - return RestResultUtils.success(memberManager.getSelf()); + @GetMapping(value = "/node/self") + public Result self() { + return Result.success(nacosClusterOperationService.self()); } /** @@ -68,35 +70,24 @@ public class NacosClusterV2Controller { * @param state match state * @return members that matches condition */ - @GetMapping(value = "/nodes") - public RestResult> listNodes(@RequestParam(value = "address", required = false) String address, - @RequestParam(value = "state", required = false) String state) { + @GetMapping(value = "/node/list") + public Result> listNodes(@RequestParam(value = "address", required = false) String address, + @RequestParam(value = "state", required = false) String state) throws NacosException { NodeState nodeState = null; if (StringUtils.isNoneBlank(state)) { try { nodeState = NodeState.valueOf(state.toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { - return RestResultUtils.failedWithMsg(400, "Illegal state: " + state); + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.ILLEGAL_STATE, "Illegal state: " + state); } } - - Collection members = memberManager.allMembers(); - Collection result = new ArrayList<>(); - - for (Member member : members) { - if (StringUtils.isNoneBlank(address) && !StringUtils.startsWith(member.getAddress(), address)) { - continue; - } - - if (nodeState != null && member.getState() != nodeState) { - continue; - } - - result.add(member); - } - - return RestResultUtils.success(result); + return Result.success(nacosClusterOperationService.listNodes(address, nodeState)); + } + + @GetMapping(value = "/node/self/health") + public Result selfHealth() { + return Result.success(nacosClusterOperationService.selfHealth()); } // The client can get all the nacos node information in the current @@ -108,25 +99,13 @@ public class NacosClusterV2Controller { * @param nodes List of {@link Member} * @return {@link RestResult} */ - @PutMapping(value = "/nodes") - public RestResult updateNodes(@RequestBody List nodes) { - for (Member node : nodes) { - if (!node.check()) { - LoggerUtils.printIfWarnEnabled(Loggers.CLUSTER, "node information is illegal, ignore node: {}", node); - continue; - } - - LoggerUtils.printIfDebugEnabled(Loggers.CLUSTER, "node state updating, node: {}", node); - node.setState(NodeState.UP); - node.setFailAccessCnt(0); - - boolean update = memberManager.update(node); - if (!update) { - LoggerUtils.printIfErrorEnabled(Loggers.CLUSTER, "node state update failed, node: {}", node); - } + @PutMapping(value = "/node/list") + public Result updateNodes(@RequestBody List nodes) throws NacosApiException { + if (nodes == null || nodes.size() == 0) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "required parameter 'nodes' is missing"); } - - return RestResultUtils.success(); + return Result.success(nacosClusterOperationService.updateNodes(nodes)); } /** @@ -136,13 +115,12 @@ public class NacosClusterV2Controller { * @return {@link RestResult} */ @PutMapping(value = "/lookup") - public RestResult updateLookup(@RequestBody LookupUpdateRequest request) { - try { - memberManager.switchLookup(request.getType()); - return RestResultUtils.success(); - } catch (Throwable ex) { - return RestResultUtils.failed(ex.getMessage()); + public Result updateLookup(LookupUpdateRequest request) throws NacosException { + if (request == null || request.getType() == null) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "required parameter 'type' is missing"); } + return Result.success(nacosClusterOperationService.updateLookup(request)); } /** diff --git a/core/src/main/java/com/alibaba/nacos/core/distributed/raft/JRaftServer.java b/core/src/main/java/com/alibaba/nacos/core/distributed/raft/JRaftServer.java index e6fd31009..8e09ddfc1 100644 --- a/core/src/main/java/com/alibaba/nacos/core/distributed/raft/JRaftServer.java +++ b/core/src/main/java/com/alibaba/nacos/core/distributed/raft/JRaftServer.java @@ -348,7 +348,7 @@ public class JRaftServer { * @return join success */ void registerSelfToCluster(String groupId, PeerId selfIp, Configuration conf) { - for (; ; ) { + while (!isShutdown) { try { List peerIds = cliService.getPeers(groupId, conf); if (peerIds.contains(selfIp)) { diff --git a/core/src/main/java/com/alibaba/nacos/core/distributed/raft/processor/NacosGetRequestProcessor.java b/core/src/main/java/com/alibaba/nacos/core/distributed/raft/processor/NacosGetRequestProcessor.java deleted file mode 100644 index 59d826823..000000000 --- a/core/src/main/java/com/alibaba/nacos/core/distributed/raft/processor/NacosGetRequestProcessor.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.nacos.core.distributed.raft.processor; - -import com.alibaba.nacos.consistency.ProtoMessageUtil; -import com.alibaba.nacos.consistency.Serializer; -import com.alibaba.nacos.consistency.entity.GetRequest; -import com.alibaba.nacos.core.distributed.raft.JRaftServer; -import com.alipay.sofa.jraft.rpc.RpcContext; -import com.alipay.sofa.jraft.rpc.RpcProcessor; - -/** - * deal with {@link GetRequest}. - * - * @author liaochuntao - */ -@Deprecated -public class NacosGetRequestProcessor extends AbstractProcessor implements RpcProcessor { - - private static final String INTEREST_NAME = GetRequest.class.getName(); - - private final JRaftServer server; - - public NacosGetRequestProcessor(JRaftServer server, Serializer serializer) { - super(serializer); - this.server = server; - } - - @Override - public void handleRequest(final RpcContext rpcCtx, GetRequest request) { - handleRequest(server, request.getGroup(), rpcCtx, ProtoMessageUtil.convertToReadRequest(request)); - } - - @Override - public String interest() { - return INTEREST_NAME; - } -} diff --git a/core/src/main/java/com/alibaba/nacos/core/distributed/raft/processor/NacosLogProcessor.java b/core/src/main/java/com/alibaba/nacos/core/distributed/raft/processor/NacosLogProcessor.java deleted file mode 100644 index a57c6ab05..000000000 --- a/core/src/main/java/com/alibaba/nacos/core/distributed/raft/processor/NacosLogProcessor.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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.nacos.core.distributed.raft.processor; - -import com.alibaba.nacos.consistency.ProtoMessageUtil; -import com.alibaba.nacos.consistency.Serializer; -import com.alibaba.nacos.consistency.entity.Log; -import com.alibaba.nacos.core.distributed.raft.JRaftServer; -import com.alipay.sofa.jraft.rpc.RpcContext; -import com.alipay.sofa.jraft.rpc.RpcProcessor; - -/** - * deal with {@link Log}. - * - * @author liaochuntao - */ -@Deprecated -public class NacosLogProcessor extends AbstractProcessor implements RpcProcessor { - - private static final String INTEREST_NAME = Log.class.getName(); - - private final JRaftServer server; - - public NacosLogProcessor(JRaftServer server, Serializer serializer) { - super(serializer); - this.server = server; - } - - @Override - public void handleRequest(final RpcContext rpcCtx, Log log) { - handleRequest(server, log.getGroup(), rpcCtx, ProtoMessageUtil.convertToWriteRequest(log)); - } - - @Override - public String interest() { - return INTEREST_NAME; - } - -} diff --git a/core/src/main/java/com/alibaba/nacos/core/distributed/raft/utils/JRaftUtils.java b/core/src/main/java/com/alibaba/nacos/core/distributed/raft/utils/JRaftUtils.java index f0782ef47..faff30b45 100644 --- a/core/src/main/java/com/alibaba/nacos/core/distributed/raft/utils/JRaftUtils.java +++ b/core/src/main/java/com/alibaba/nacos/core/distributed/raft/utils/JRaftUtils.java @@ -25,12 +25,10 @@ import com.alibaba.nacos.consistency.entity.Response; import com.alibaba.nacos.consistency.entity.WriteRequest; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.core.distributed.raft.JRaftServer; -import com.alibaba.nacos.core.distributed.raft.processor.NacosGetRequestProcessor; -import com.alibaba.nacos.core.distributed.raft.processor.NacosLogProcessor; import com.alibaba.nacos.core.distributed.raft.processor.NacosReadRequestProcessor; import com.alibaba.nacos.core.distributed.raft.processor.NacosWriteRequestProcessor; -import com.alibaba.nacos.sys.utils.ApplicationUtils; import com.alibaba.nacos.core.utils.Loggers; +import com.alibaba.nacos.sys.utils.ApplicationUtils; import com.alibaba.nacos.sys.utils.DiskUtils; import com.alipay.sofa.jraft.CliService; import com.alipay.sofa.jraft.RouteTable; @@ -72,7 +70,7 @@ public class JRaftUtils { MarshallerRegistry registry = raftRpcFactory.getMarshallerRegistry(); registry.registerResponseInstance(Log.class.getName(), Response.getDefaultInstance()); registry.registerResponseInstance(GetRequest.class.getName(), Response.getDefaultInstance()); - + registry.registerResponseInstance(WriteRequest.class.getName(), Response.getDefaultInstance()); registry.registerResponseInstance(ReadRequest.class.getName(), Response.getDefaultInstance()); @@ -80,11 +78,6 @@ public class JRaftUtils { RaftRpcServerFactory.addRaftRequestProcessors(rpcServer, RaftExecutor.getRaftCoreExecutor(), RaftExecutor.getRaftCliServiceExecutor()); - // Deprecated - rpcServer.registerProcessor(new NacosLogProcessor(server, SerializeFactory.getDefault())); - // Deprecated - rpcServer.registerProcessor(new NacosGetRequestProcessor(server, SerializeFactory.getDefault())); - rpcServer.registerProcessor(new NacosWriteRequestProcessor(server, SerializeFactory.getDefault())); rpcServer.registerProcessor(new NacosReadRequestProcessor(server, SerializeFactory.getDefault())); @@ -110,7 +103,7 @@ public class JRaftUtils { copy.setRaftMetaUri(metaDataUri); copy.setSnapshotUri(snapshotUri); } - + public static List toStrings(List peerIds) { return peerIds.stream().map(peerId -> peerId.getEndpoint().toString()).collect(Collectors.toList()); } diff --git a/core/src/main/java/com/alibaba/nacos/core/remote/control/TpsMonitorManager.java b/core/src/main/java/com/alibaba/nacos/core/remote/control/TpsMonitorManager.java index b0455556c..4c8f7aa9e 100644 --- a/core/src/main/java/com/alibaba/nacos/core/remote/control/TpsMonitorManager.java +++ b/core/src/main/java/com/alibaba/nacos/core/remote/control/TpsMonitorManager.java @@ -16,7 +16,6 @@ package com.alibaba.nacos.core.remote.control; -import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.executor.ExecutorFactory; import com.alibaba.nacos.common.notify.Event; @@ -36,6 +35,7 @@ import org.springframework.stereotype.Service; import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.util.Arrays; import java.util.List; @@ -153,31 +153,30 @@ public class TpsMonitorManager extends Subscriber imp @Override public void onEvent(TpsControlRuleChangeEvent event) { - - Loggers.TPS_CONTROL - .info("Tps control rule change event receive,pointName={}, ruleContent={} ", event.getPointName(), - event.ruleContent); - if (event == null || event.getPointName() == null) { + if (event == null) { + Loggers.TPS_CONTROL.info("Tps control rule change event receive, but event is null"); return; } + + Loggers.TPS_CONTROL + .info("Tps control rule change event receive, pointName={}, ruleContent={} ", event.getPointName(), + event.ruleContent); + if (event.getPointName() == null) { + return; + } + try { TpsControlRule tpsControlRule = StringUtils.isBlank(event.ruleContent) ? new TpsControlRule() : JacksonUtils.toObj(event.ruleContent, TpsControlRule.class); if (!points.containsKey(event.getPointName())) { - Loggers.TPS_CONTROL.info("Tps control rule change event ignore,pointName={} ", event.getPointName()); + Loggers.TPS_CONTROL.info("Tps control rule change event ignore, pointName={} ", event.getPointName()); return; } - try { - saveRuleToLocal(event.getPointName(), tpsControlRule); - } catch (Throwable throwable) { - Loggers.TPS_CONTROL - .warn("Tps control rule persist fail,pointName={},error={} ", event.getPointName(), throwable); - - } + + saveRuleToLocal(event.getPointName(), tpsControlRule); } catch (Exception e) { - Loggers.TPS_CONTROL.warn("Tps control rule apply error ,error= ", e); + Loggers.TPS_CONTROL.warn("Tps control rule apply error, error= ", e); } - } @Override @@ -300,14 +299,18 @@ public class TpsMonitorManager extends Subscriber imp } private synchronized void saveRuleToLocal(String pointName, TpsControlRule tpsControlRule) throws IOException { - - File pointFile = getRuleFile(pointName); - if (!pointFile.exists()) { - pointFile.createNewFile(); + try { + File pointFile = getRuleFile(pointName); + if (!pointFile.exists()) { + pointFile.createNewFile(); + } + String content = JacksonUtils.toJson(tpsControlRule); + DiskUtils.writeFile(pointFile, content.getBytes(StandardCharsets.UTF_8), false); + Loggers.TPS_CONTROL.info("Save rule to local,pointName={}, ruleContent ={} ", pointName, content); + } catch (IOException e) { + Loggers.TPS_CONTROL + .warn("Tps control rule persist fail, pointName={},error={} ", pointName, e); } - String content = JacksonUtils.toJson(tpsControlRule); - DiskUtils.writeFile(pointFile, content.getBytes(Constants.ENCODE), false); - Loggers.TPS_CONTROL.info("Save rule to local,pointName={}, ruleContent ={} ", pointName, content); } private File getRuleFile(String pointName) { diff --git a/core/src/main/java/com/alibaba/nacos/core/service/NacosClusterOperationService.java b/core/src/main/java/com/alibaba/nacos/core/service/NacosClusterOperationService.java new file mode 100644 index 000000000..5e99ce54a --- /dev/null +++ b/core/src/main/java/com/alibaba/nacos/core/service/NacosClusterOperationService.java @@ -0,0 +1,107 @@ +/* + * Copyright 1999-2022 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.nacos.core.service; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.common.utils.LoggerUtils; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.core.cluster.Member; +import com.alibaba.nacos.core.cluster.NodeState; +import com.alibaba.nacos.core.cluster.ServerMemberManager; +import com.alibaba.nacos.core.model.request.LookupUpdateRequest; +import com.alibaba.nacos.core.utils.Loggers; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * NacosClusterOperationService. + * @author dongyafei + * @date 2022/8/15 + */ +@Service +public class NacosClusterOperationService { + + private final ServerMemberManager memberManager; + + public NacosClusterOperationService(ServerMemberManager memberManager) { + this.memberManager = memberManager; + } + + public Member self() { + return memberManager.getSelf(); + } + + /** + * The console displays the list of cluster members. + */ + public Collection listNodes(String address, NodeState nodeState) throws NacosException { + + Collection members = memberManager.allMembers(); + Collection result = new ArrayList<>(); + + for (Member member : members) { + if (StringUtils.isNoneBlank(address) && !StringUtils.startsWith(member.getAddress(), address)) { + continue; + } + if (nodeState != null && member.getState() != nodeState) { + continue; + } + result.add(member); + } + return result; + } + + /** + * cluster members information update. + */ + public Boolean updateNodes(List nodes) { + for (Member node : nodes) { + if (!node.check()) { + LoggerUtils.printIfWarnEnabled(Loggers.CLUSTER, "node information is illegal, ignore node: {}", node); + continue; + } + + LoggerUtils.printIfDebugEnabled(Loggers.CLUSTER, "node state updating, node: {}", node); + node.setState(NodeState.UP); + node.setFailAccessCnt(0); + + boolean update = memberManager.update(node); + if (!update) { + LoggerUtils.printIfErrorEnabled(Loggers.CLUSTER, "node state update failed, node: {}", node); + } + } + return true; + } + + /** + * Addressing mode switch. + */ + public Boolean updateLookup(LookupUpdateRequest request) throws NacosException { + memberManager.switchLookup(request.getType()); + return true; + } + + /** + * query health of current node. + */ + public String selfHealth() { + return memberManager.getSelf().getState().name(); + } +} diff --git a/core/src/main/java/com/alibaba/nacos/core/trace/NacosCombinedTraceSubscriber.java b/core/src/main/java/com/alibaba/nacos/core/trace/NacosCombinedTraceSubscriber.java new file mode 100644 index 000000000..9d69d5eb8 --- /dev/null +++ b/core/src/main/java/com/alibaba/nacos/core/trace/NacosCombinedTraceSubscriber.java @@ -0,0 +1,88 @@ +/* + * Copyright 1999-2021 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.nacos.core.trace; + +import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.notify.listener.SmartSubscriber; +import com.alibaba.nacos.common.trace.event.TraceEvent; +import com.alibaba.nacos.common.trace.publisher.TraceEventPublisherFactory; +import com.alibaba.nacos.plugin.trace.NacosTracePluginManager; +import com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Combined trace events subscriber. + * + * @author xiweng.yy + */ +public class NacosCombinedTraceSubscriber extends SmartSubscriber { + + private final Map, Set> interestedEvents; + + public NacosCombinedTraceSubscriber(Class combinedEvent) { + this.interestedEvents = new ConcurrentHashMap<>(); + TraceEventPublisherFactory.getInstance().addPublisherEvent(combinedEvent); + for (NacosTraceSubscriber each : NacosTracePluginManager.getInstance().getAllTraceSubscribers()) { + filterInterestedEvents(each, combinedEvent); + } + NotifyCenter.registerSubscriber(this, TraceEventPublisherFactory.getInstance()); + } + + private void filterInterestedEvents(NacosTraceSubscriber plugin, Class combinedEvent) { + for (Class each : plugin.subscribeTypes()) { + if (combinedEvent.isAssignableFrom(each)) { + interestedEvents.compute(each, (eventClass, nacosTraceSubscribers) -> { + if (null == nacosTraceSubscribers) { + nacosTraceSubscribers = new HashSet<>(); + } + nacosTraceSubscribers.add(plugin); + return nacosTraceSubscribers; + }); + } + } + } + + @Override + public List> subscribeTypes() { + return new LinkedList<>(interestedEvents.keySet()); + } + + @Override + public void onEvent(Event event) { + Set subscribers = interestedEvents.get(event.getClass()); + if (null == subscribers) { + return; + } + for (NacosTraceSubscriber each : subscribers) { + try { + each.onEvent((TraceEvent) event); + } catch (Exception ignored) { + } + } + } + + public void shutdown() { + NotifyCenter.deregisterSubscriber(this); + } +} diff --git a/core/src/main/java/com/alibaba/nacos/core/utils/WebUtils.java b/core/src/main/java/com/alibaba/nacos/core/utils/WebUtils.java index ed35a484b..1b7a43f75 100644 --- a/core/src/main/java/com/alibaba/nacos/core/utils/WebUtils.java +++ b/core/src/main/java/com/alibaba/nacos/core/utils/WebUtils.java @@ -79,9 +79,6 @@ public class WebUtils { * @return value */ public static String optional(final HttpServletRequest req, final String key, final String defaultValue) { - if (!req.getParameterMap().containsKey(key) || req.getParameterMap().get(key)[0] == null) { - return defaultValue; - } String value = req.getParameter(key); if (StringUtils.isBlank(value)) { return defaultValue; diff --git a/core/src/test/java/com/alibaba/nacos/core/cluster/MemberUtilTest.java b/core/src/test/java/com/alibaba/nacos/core/cluster/MemberUtilTest.java index 88867ef20..c1750dbee 100644 --- a/core/src/test/java/com/alibaba/nacos/core/cluster/MemberUtilTest.java +++ b/core/src/test/java/com/alibaba/nacos/core/cluster/MemberUtilTest.java @@ -17,27 +17,33 @@ package com.alibaba.nacos.core.cluster; import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.common.utils.ThreadUtils; import com.alibaba.nacos.sys.env.EnvUtil; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.mock.env.MockEnvironment; -import org.springframework.mock.web.MockServletContext; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; import java.net.ConnectException; +import java.util.Collection; import java.util.Collections; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; +import java.util.HashSet; +import java.util.Set; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class MemberUtilTest { @@ -46,19 +52,27 @@ public class MemberUtilTest { private static final int PORT = 8848; + @Mock + private ServerMemberManager memberManager; + private ConfigurableEnvironment environment; private Member originalMember; - private ServerMemberManager memberManager; + private Set mockMemberAddressInfos; + + private String nacosHome; @Before public void setUp() throws Exception { environment = new MockEnvironment(); EnvUtil.setEnvironment(environment); EnvUtil.setIsStandalone(true); - memberManager = new ServerMemberManager(new MockServletContext()); + nacosHome = EnvUtil.getNacosHome(); + EnvUtil.setNacosHomePath(nacosHome + File.separator + "MemberUtilTest"); originalMember = buildMember(); + mockMemberAddressInfos = new HashSet<>(); + when(memberManager.getMemberAddressInfos()).thenReturn(mockMemberAddressInfos); } private Member buildMember() { @@ -67,8 +81,108 @@ public class MemberUtilTest { @After public void tearDown() throws NacosException { - NotifyCenter.deregisterPublisher(MembersChangeEvent.class); - memberManager.shutdown(); + EnvUtil.setNacosHomePath(nacosHome); + } + + @Test + public void testCopy() { + Member expected = Member.builder().build(); + expected.setIp("2.2.2.2"); + expected.setPort(9999); + expected.setState(NodeState.SUSPICIOUS); + expected.setExtendVal(MemberMetaDataConstants.VERSION, "test"); + expected.getAbilities().getRemoteAbility().setSupportRemoteConnection(true); + MemberUtil.copy(expected, originalMember); + assertEquals(expected.getIp(), originalMember.getIp()); + assertEquals(expected.getPort(), originalMember.getPort()); + assertEquals(expected.getAddress(), originalMember.getAddress()); + assertEquals(NodeState.SUSPICIOUS, originalMember.getState()); + assertEquals("test", originalMember.getExtendVal(MemberMetaDataConstants.VERSION)); + assertTrue(originalMember.getAbilities().getRemoteAbility().isSupportRemoteConnection()); + } + + @Test + public void testSingleParseWithPort() { + Member actual = MemberUtil.singleParse(IP + ":2222"); + assertEquals(IP, actual.getIp()); + assertEquals(2222, actual.getPort()); + assertEquals(IP + ":2222", actual.getAddress()); + assertEquals(NodeState.UP, actual.getState()); + assertEquals(true, actual.getExtendVal(MemberMetaDataConstants.READY_TO_UPGRADE)); + assertEquals("1222", actual.getExtendVal(MemberMetaDataConstants.RAFT_PORT)); + assertFalse(actual.getAbilities().getRemoteAbility().isSupportRemoteConnection()); + } + + @Test + public void testSingleParseWithoutPort() { + Member actual = MemberUtil.singleParse(IP); + assertEquals(IP, actual.getIp()); + assertEquals(PORT, actual.getPort()); + assertEquals(IP + ":" + PORT, actual.getAddress()); + assertEquals(NodeState.UP, actual.getState()); + assertEquals(true, actual.getExtendVal(MemberMetaDataConstants.READY_TO_UPGRADE)); + assertEquals("7848", actual.getExtendVal(MemberMetaDataConstants.RAFT_PORT)); + assertFalse(actual.getAbilities().getRemoteAbility().isSupportRemoteConnection()); + } + + @Test + public void testIsSupportedLongCon() { + assertFalse(MemberUtil.isSupportedLongCon(originalMember)); + originalMember.getAbilities().getRemoteAbility().setSupportRemoteConnection(true); + assertTrue(MemberUtil.isSupportedLongCon(originalMember)); + originalMember.getAbilities().setRemoteAbility(null); + assertFalse(MemberUtil.isSupportedLongCon(originalMember)); + originalMember.setAbilities(null); + assertFalse(MemberUtil.isSupportedLongCon(originalMember)); + } + + @Test + public void testMultiParse() { + Collection address = new HashSet<>(); + address.add("1.1.1.1:3306"); + address.add("1.1.1.1"); + Collection actual = MemberUtil.multiParse(address); + assertEquals(2, actual.size()); + } + + @Test + public void testSyncToFile() throws IOException { + File file = new File(EnvUtil.getClusterConfFilePath()); + file.getParentFile().mkdirs(); + assertTrue(file.createNewFile()); + MemberUtil.syncToFile(Collections.singleton(originalMember)); + try (BufferedReader reader = new BufferedReader(new FileReader(EnvUtil.getClusterConfFilePath()))) { + String line = ""; + while ((line = reader.readLine()) != null) { + if (!line.startsWith("#")) { + assertEquals(IP + ":" + PORT, line.trim()); + return; + } + } + Assert.fail("No found member info in cluster.conf"); + } finally { + file.delete(); + } + } + + @Test + public void testReadServerConf() { + Collection address = new HashSet<>(); + address.add("1.1.1.1:3306"); + address.add("1.1.1.1"); + Collection actual = MemberUtil.readServerConf(address); + assertEquals(2, actual.size()); + } + + @Test + public void testSelectTargetMembers() { + Collection input = new HashSet<>(); + input.add(originalMember); + Member member = buildMember(); + member.setIp("2.2.2.2"); + input.add(member); + Set actual = MemberUtil.selectTargetMembers(input, member1 -> member1.getIp().equals(IP)); + assertEquals(1, actual.size()); } @Test @@ -126,126 +240,106 @@ public class MemberUtilTest { assertTrue(MemberUtil.isBasicInfoChanged(newMember, originalMember)); } + @Test + public void testIsBasicInfoChangedForChangedAbilities() { + Member newMember = buildMember(); + newMember.getAbilities().getRemoteAbility().setSupportRemoteConnection(true); + assertTrue(MemberUtil.isBasicInfoChanged(newMember, originalMember)); + } + + @Test + public void testIsBasicInfoChangedForChangedNull() { + Member newMember = buildMember(); + assertTrue(MemberUtil.isBasicInfoChanged(newMember, null)); + } + @Test public void testMemberOnFailWhenReachMaxFailAccessCnt() { final Member remote = buildMember(); - memberManager.memberJoin(Collections.singletonList(remote)); - + mockMemberAddressInfos.add(remote.getAddress()); + remote.setState(NodeState.SUSPICIOUS); remote.setFailAccessCnt(2); MemberUtil.onFail(memberManager, remote); - - final Member search1 = memberManager.find(remote.getAddress()); - Assert.assertEquals(3, search1.getFailAccessCnt()); - Assert.assertEquals(NodeState.SUSPICIOUS, search1.getState()); - + Assert.assertEquals(3, remote.getFailAccessCnt()); + Assert.assertEquals(NodeState.SUSPICIOUS, remote.getState()); + verify(memberManager, never()).notifyMemberChange(remote); + Assert.assertTrue(mockMemberAddressInfos.isEmpty()); MemberUtil.onFail(memberManager, remote); - - final Member search2 = memberManager.find(remote.getAddress()); - Assert.assertEquals(4, search2.getFailAccessCnt()); - Assert.assertEquals(NodeState.DOWN, search2.getState()); - - MemberUtil.onSuccess(memberManager, remote); - final Member search3 = memberManager.find(remote.getAddress()); - Assert.assertEquals(0, search3.getFailAccessCnt()); - Assert.assertEquals(NodeState.UP, search3.getState()); + Assert.assertEquals(4, remote.getFailAccessCnt()); + Assert.assertEquals(NodeState.DOWN, remote.getState()); + verify(memberManager).notifyMemberChange(remote); } @Test public void testMemberOnFailWhenConnectRefused() { final Member remote = buildMember(); - memberManager.memberJoin(Collections.singletonList(remote)); - + mockMemberAddressInfos.add(remote.getAddress()); remote.setFailAccessCnt(1); MemberUtil.onFail(memberManager, remote, new ConnectException(MemberUtil.TARGET_MEMBER_CONNECT_REFUSE_ERRMSG)); - - final Member search1 = memberManager.find(remote.getAddress()); - Assert.assertEquals(2, search1.getFailAccessCnt()); - Assert.assertEquals(NodeState.DOWN, search1.getState()); - - MemberUtil.onSuccess(memberManager, remote); - final Member search2 = memberManager.find(remote.getAddress()); - Assert.assertEquals(0, search2.getFailAccessCnt()); - Assert.assertEquals(NodeState.UP, search2.getState()); - } - - @Test - public void testMemberOnFailListener() throws InterruptedException { - - final AtomicBoolean received = new AtomicBoolean(false); - final AtomicReference reference = new AtomicReference<>(); - - NotifyCenter.registerSubscriber(new MemberChangeListener() { - @Override - public void onEvent(MembersChangeEvent event) { - reference.set(event); - received.set(true); - } - }); - - final Member remote = buildMember(); - memberManager.memberJoin(Collections.singletonList(remote)); - - remote.setFailAccessCnt(1); - MemberUtil.onFail(memberManager, remote, new ConnectException(MemberUtil.TARGET_MEMBER_CONNECT_REFUSE_ERRMSG)); - ThreadUtils.sleep(4000); - Assert.assertTrue(received.get()); - final MembersChangeEvent event1 = reference.get(); - final Member member1 = event1.getMembers().stream() - .filter(member -> StringUtils.equals(remote.getAddress(), member.getAddress())).findFirst() - .orElseThrow(() -> new AssertionError("member is null")); - Assert.assertEquals(2, member1.getFailAccessCnt()); - Assert.assertEquals(NodeState.DOWN, member1.getState()); - received.set(false); - - MemberUtil.onSuccess(memberManager, remote); - ThreadUtils.sleep(4000); - Assert.assertTrue(received.get()); - final MembersChangeEvent event2 = reference.get(); - final Member member2 = event2.getMembers().stream() - .filter(member -> StringUtils.equals(remote.getAddress(), member.getAddress())).findFirst() - .orElseThrow(() -> new AssertionError("member is null")); - Assert.assertEquals(0, member2.getFailAccessCnt()); - Assert.assertEquals(NodeState.UP, member2.getState()); - } - - @Test - public void testMemberOnSuccessWhenMemberAlreadyUP() { - final AtomicBoolean received = new AtomicBoolean(false); - - NotifyCenter.registerSubscriber(new MemberChangeListener() { - @Override - public void onEvent(MembersChangeEvent event) { - received.set(true); - } - }); - - final Member remote = buildMember(); - memberManager.updateMember(remote); - - MemberUtil.onSuccess(memberManager, remote); - ThreadUtils.sleep(4000); - Assert.assertFalse(received.get()); + Assert.assertEquals(2, remote.getFailAccessCnt()); + Assert.assertEquals(NodeState.DOWN, remote.getState()); + Assert.assertTrue(mockMemberAddressInfos.isEmpty()); + verify(memberManager).notifyMemberChange(remote); } @SuppressWarnings("checkstyle:AbbreviationAsWordInName") @Test public void testMemberOnFailWhenMemberAlreadyNOUP() { - final AtomicBoolean received = new AtomicBoolean(false); - - NotifyCenter.registerSubscriber(new MemberChangeListener() { - @Override - public void onEvent(MembersChangeEvent event) { - received.set(true); - } - }); - final Member remote = buildMember(); - remote.setState(NodeState.SUSPICIOUS); - memberManager.updateMember(remote); - + remote.setState(NodeState.DOWN); + remote.setFailAccessCnt(4); MemberUtil.onFail(memberManager, remote); - ThreadUtils.sleep(4000); - Assert.assertFalse(received.get()); + verify(memberManager, never()).notifyMemberChange(remote); } + @Test + public void testMemberOnSuccessFromDown() { + final Member remote = buildMember(); + remote.setState(NodeState.DOWN); + remote.setFailAccessCnt(4); + MemberUtil.onSuccess(memberManager, remote); + assertEquals(NodeState.UP, remote.getState()); + assertEquals(0, remote.getFailAccessCnt()); + verify(memberManager).notifyMemberChange(remote); + } + + @Test + public void testMemberOnSuccessWhenMemberAlreadyUP() { + final Member remote = buildMember(); + memberManager.updateMember(remote); + MemberUtil.onSuccess(memberManager, remote); + verify(memberManager, never()).notifyMemberChange(remote); + } + + @Test + public void testMemberOnSuccessWhenMemberNotUpdated() { + final Member remote = buildMember(); + final Member reportResult = buildMember(); + MemberUtil.onSuccess(memberManager, remote, reportResult); + assertFalse(remote.getAbilities().getRemoteAbility().isSupportRemoteConnection()); + assertTrue(mockMemberAddressInfos.contains(remote.getAddress())); + verify(memberManager, never()).notifyMemberChange(remote); + } + + @Test + public void testMemberOnSuccessWhenMemberUpdatedAbilities() { + final Member remote = buildMember(); + final Member reportResult = buildMember(); + reportResult.getAbilities().getRemoteAbility().setSupportRemoteConnection(true); + MemberUtil.onSuccess(memberManager, remote, reportResult); + assertTrue(remote.getAbilities().getRemoteAbility().isSupportRemoteConnection()); + assertTrue(mockMemberAddressInfos.contains(remote.getAddress())); + verify(memberManager).notifyMemberChange(remote); + } + + @Test + public void testMemberOnSuccessWhenMemberUpdatedExtendInfo() { + final Member remote = buildMember(); + final Member reportResult = buildMember(); + reportResult.setExtendVal(MemberMetaDataConstants.VERSION, "test"); + MemberUtil.onSuccess(memberManager, remote, reportResult); + assertEquals("test", remote.getExtendVal(MemberMetaDataConstants.VERSION)); + assertTrue(mockMemberAddressInfos.contains(remote.getAddress())); + verify(memberManager).notifyMemberChange(remote); + } } diff --git a/core/src/test/java/com/alibaba/nacos/core/cluster/ServerMemberManagerTest.java b/core/src/test/java/com/alibaba/nacos/core/cluster/ServerMemberManagerTest.java index 8f39f611f..0a68ce5fc 100644 --- a/core/src/test/java/com/alibaba/nacos/core/cluster/ServerMemberManagerTest.java +++ b/core/src/test/java/com/alibaba/nacos/core/cluster/ServerMemberManagerTest.java @@ -16,11 +16,18 @@ package com.alibaba.nacos.core.cluster; +import com.alibaba.nacos.api.ability.ServerAbilities; import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.auth.config.AuthConfigs; +import com.alibaba.nacos.common.http.Callback; +import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; +import com.alibaba.nacos.common.model.RestResult; +import com.alibaba.nacos.common.model.RestResultUtils; import com.alibaba.nacos.common.notify.EventPublisher; import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.sys.env.EnvUtil; -import com.alibaba.nacos.sys.utils.InetUtils; +import com.alibaba.nacos.sys.utils.ApplicationUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -31,7 +38,9 @@ import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.test.util.ReflectionTestUtils; import javax.servlet.ServletContext; import java.util.Collections; @@ -42,6 +51,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -61,6 +73,12 @@ public class ServerMemberManagerTest { @Mock private WebServerInitializedEvent mockEvent; + @Mock + private AuthConfigs authConfigs; + + @Mock + private ConfigurableApplicationContext context; + private ServerMemberManager serverMemberManager; private static final AtomicBoolean EVENT_PUBLISH = new AtomicBoolean(false); @@ -69,6 +87,8 @@ public class ServerMemberManagerTest { public void setUp() throws Exception { when(environment.getProperty("server.port", Integer.class, 8848)).thenReturn(8848); when(environment.getProperty("nacos.member-change-event.queue.size", Integer.class, 128)).thenReturn(128); + when(context.getBean(AuthConfigs.class)).thenReturn(authConfigs); + ApplicationUtils.injectContext(context); EnvUtil.setEnvironment(environment); EnvUtil.setIsStandalone(true); when(servletContext.getContextPath()).thenReturn(""); @@ -85,14 +105,6 @@ public class ServerMemberManagerTest { serverMemberManager.shutdown(); } - @Test - public void testInit() { - String selfIp = InetUtils.getSelfIP(); - Member member = serverMemberManager.getSelf(); - assertEquals(selfIp, member.getIp()); - assertTrue(member.getAbilities().getRemoteAbility().isSupportRemoteConnection()); - } - @Test public void testUpdateNonExistMember() { Member newMember = Member.builder().ip("1.1.1.2").port(8848).state(NodeState.UP).build(); @@ -138,7 +150,7 @@ public class ServerMemberManagerTest { Member member = Member.builder().ip("1.1.3.3").port(8848).state(NodeState.DOWN).build(); boolean joinResult = serverMemberManager.memberJoin(Collections.singletonList(member)); assertTrue(joinResult); - + List ips = serverMemberManager.getServerListUnhealth(); assertEquals(1, ips.size()); @@ -170,4 +182,50 @@ public class ServerMemberManagerTest { int port = EnvUtil.getPort(); Assert.assertEquals(port, 8848); } + + @Test + public void testReportTaskWithoutMemberInfo() { + Member testMember = Member.builder().ip("1.1.1.1").port(8848).state(NodeState.DOWN) + .extendInfo(Collections.singletonMap(MemberMetaDataConstants.VERSION, "test")).build(); + testMember.setAbilities(new ServerAbilities()); + testMember.getAbilities().getRemoteAbility().setSupportRemoteConnection(true); + serverMemberManager.updateMember(testMember); + assertTrue( + serverMemberManager.find("1.1.1.1:8848").getExtendInfo().containsKey(MemberMetaDataConstants.VERSION)); + NacosAsyncRestTemplate mockAsyncRestTemplate = mock(NacosAsyncRestTemplate.class); + ReflectionTestUtils.setField(serverMemberManager, "asyncRestTemplate", mockAsyncRestTemplate); + doAnswer(invocationOnMock -> { + Callback callback = invocationOnMock.getArgument(5); + RestResult result = RestResultUtils.success("true"); + callback.onReceive(result); + return null; + }).when(mockAsyncRestTemplate).post(anyString(), any(), any(), any(), any(), any()); + serverMemberManager.getInfoReportTask().run(); + assertEquals("test", serverMemberManager.find("1.1.1.1:8848").getExtendVal(MemberMetaDataConstants.VERSION)); + assertEquals(NodeState.UP, serverMemberManager.find("1.1.1.1:8848").getState()); + } + + @Test + public void testReportTaskWithMemberInfoChanged() { + Member testMember = Member.builder().ip("1.1.1.1").port(8848).state(NodeState.DOWN) + .extendInfo(Collections.singletonMap(MemberMetaDataConstants.VERSION, "test")).build(); + testMember.setAbilities(new ServerAbilities()); + testMember.getAbilities().getRemoteAbility().setSupportRemoteConnection(true); + serverMemberManager.updateMember(testMember); + assertTrue( + serverMemberManager.find("1.1.1.1:8848").getExtendInfo().containsKey(MemberMetaDataConstants.VERSION)); + Member newMember = Member.builder().ip("1.1.1.1").port(8848).state(NodeState.DOWN) + .extendInfo(Collections.singletonMap(MemberMetaDataConstants.VERSION, "new")).build(); + NacosAsyncRestTemplate mockAsyncRestTemplate = mock(NacosAsyncRestTemplate.class); + ReflectionTestUtils.setField(serverMemberManager, "asyncRestTemplate", mockAsyncRestTemplate); + doAnswer(invocationOnMock -> { + Callback callback = invocationOnMock.getArgument(5); + RestResult result = RestResultUtils.success(JacksonUtils.toJson(newMember)); + callback.onReceive(result); + return null; + }).when(mockAsyncRestTemplate).post(anyString(), any(), any(), any(), any(), any()); + serverMemberManager.getInfoReportTask().run(); + assertEquals("new", serverMemberManager.find("1.1.1.1:8848").getExtendVal(MemberMetaDataConstants.VERSION)); + assertEquals(NodeState.UP, serverMemberManager.find("1.1.1.1:8848").getState()); + } } diff --git a/core/src/test/java/com/alibaba/nacos/core/controller/NacosClusterControllerTest.java b/core/src/test/java/com/alibaba/nacos/core/controller/NacosClusterControllerTest.java index 4e2e00683..bc4eeab14 100644 --- a/core/src/test/java/com/alibaba/nacos/core/controller/NacosClusterControllerTest.java +++ b/core/src/test/java/com/alibaba/nacos/core/controller/NacosClusterControllerTest.java @@ -18,6 +18,7 @@ package com.alibaba.nacos.core.controller; import com.alibaba.nacos.common.model.RestResult; +import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.core.cluster.NodeState; import com.alibaba.nacos.core.cluster.ServerMemberManager; @@ -37,6 +38,8 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import static org.junit.Assert.assertEquals; + /** * {@link NacosClusterController} unit test. * @@ -61,9 +64,9 @@ public class NacosClusterControllerTest { public void testSelf() { Member self = new Member(); Mockito.when(serverMemberManager.getSelf()).thenReturn(self); - + RestResult result = nacosClusterController.self(); - Assert.assertEquals(self, result.getData()); + assertEquals(self, result.getData()); } @Test @@ -72,17 +75,17 @@ public class NacosClusterControllerTest { member1.setIp("1.1.1.1"); List members = Arrays.asList(member1); Mockito.when(serverMemberManager.allMembers()).thenReturn(members); - + RestResult> result = nacosClusterController.listNodes("1.1.1.1"); - Assert.assertEquals(1, result.getData().size()); + assertEquals(1, result.getData().size()); } @Test public void testListSimpleNodes() { Mockito.when(serverMemberManager.getMemberAddressInfos()).thenReturn(Collections.singleton("1.1.1.1")); - + RestResult> result = nacosClusterController.listSimpleNodes(); - Assert.assertEquals(1, result.getData().size()); + assertEquals(1, result.getData().size()); } @Test @@ -90,21 +93,23 @@ public class NacosClusterControllerTest { Member self = new Member(); self.setState(NodeState.UP); Mockito.when(serverMemberManager.getSelf()).thenReturn(self); - + RestResult result = nacosClusterController.getHealth(); - Assert.assertEquals(NodeState.UP.name(), result.getData()); + assertEquals(NodeState.UP.name(), result.getData()); } @Test public void testReport() { + Member self = new Member(); Mockito.when(serverMemberManager.update(Mockito.any())).thenReturn(true); - + Mockito.when(serverMemberManager.getSelf()).thenReturn(self); Member member = new Member(); member.setIp("1.1.1.1"); member.setPort(8848); member.setAddress("test"); RestResult result = nacosClusterController.report(member); - Assert.assertTrue(Boolean.parseBoolean(result.getData())); + String expected = JacksonUtils.toJson(self); + assertEquals(expected, result.getData()); } @Test @@ -117,6 +122,6 @@ public class NacosClusterControllerTest { public void testLeave() throws Exception { RestResult result = nacosClusterController.leave(Collections.singletonList("1.1.1.1"), true); Assert.assertFalse(result.ok()); - Assert.assertEquals(405, result.getCode()); + assertEquals(405, result.getCode()); } } diff --git a/core/src/test/java/com/alibaba/nacos/core/controller/v2/NacosClusterControllerV2Test.java b/core/src/test/java/com/alibaba/nacos/core/controller/v2/NacosClusterControllerV2Test.java new file mode 100644 index 000000000..5936c4211 --- /dev/null +++ b/core/src/test/java/com/alibaba/nacos/core/controller/v2/NacosClusterControllerV2Test.java @@ -0,0 +1,138 @@ +/* + * Copyright 1999-2021 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.nacos.core.controller.v2; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.common.model.RestResult; +import com.alibaba.nacos.core.cluster.Member; +import com.alibaba.nacos.core.cluster.NodeState; +import com.alibaba.nacos.core.model.request.LookupUpdateRequest; +import com.alibaba.nacos.core.service.NacosClusterOperationService; +import com.alibaba.nacos.sys.env.EnvUtil; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.mock.env.MockEnvironment; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class NacosClusterControllerV2Test { + + @InjectMocks + private NacosClusterControllerV2 nacosClusterControllerV2; + + @Mock + private NacosClusterOperationService nacosClusterOperationService; + + private final MockEnvironment environment = new MockEnvironment(); + + @Before + public void setUp() { + nacosClusterControllerV2 = new NacosClusterControllerV2(nacosClusterOperationService); + EnvUtil.setEnvironment(environment); + } + + @Test + public void testSelf() { + Member self = new Member(); + when(nacosClusterOperationService.self()).thenReturn(self); + + Result result = nacosClusterControllerV2.self(); + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(self, result.getData()); + } + + @Test + public void testListNodes() throws NacosException { + Member member1 = new Member(); + member1.setIp("1.1.1.1"); + member1.setPort(8848); + member1.setState(NodeState.DOWN); + Member member2 = new Member(); + member2.setIp("2.2.2.2"); + member2.setPort(8848); + + List members = Arrays.asList(member1, member2); + Mockito.when(nacosClusterOperationService.listNodes(any(), any())).thenReturn(members); + + Result> result = nacosClusterControllerV2.listNodes("1.1.1.1", null); + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertTrue(result.getData().stream().findFirst().isPresent()); + assertEquals("1.1.1.1:8848", result.getData().stream().findFirst().get().getAddress()); + } + + @Test + public void testSelfHealth() { + String selfHealth = "UP"; + when(nacosClusterOperationService.selfHealth()).thenReturn(selfHealth); + + Result result = nacosClusterControllerV2.selfHealth(); + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(selfHealth, result.getData()); + } + + @Test + public void testUpdate() throws NacosApiException { + Member member = new Member(); + member.setIp("1.1.1.1"); + member.setPort(8848); + member.setAddress("test"); + when(nacosClusterOperationService.updateNodes(any())).thenReturn(true); + Result result = nacosClusterControllerV2.updateNodes(Collections.singletonList(member)); + verify(nacosClusterOperationService).updateNodes(any()); + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(true, result.getData()); + } + + @Test + public void testSwitchLookup() throws NacosException { + LookupUpdateRequest request = new LookupUpdateRequest(); + request.setType("test"); + + when(nacosClusterOperationService.updateLookup(any())).thenReturn(true); + Result result = nacosClusterControllerV2.updateLookup(request); + verify(nacosClusterOperationService).updateLookup(any()); + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(true, result.getData()); + } + + @Test + public void testLeave() throws Exception { + RestResult result = nacosClusterControllerV2.deleteNodes(Collections.singletonList("1.1.1.1")); + Assert.assertFalse(result.ok()); + Assert.assertEquals(405, result.getCode()); + } +} diff --git a/core/src/test/java/com/alibaba/nacos/core/controller/v2/NacosClusterV2ControllerTest.java b/core/src/test/java/com/alibaba/nacos/core/controller/v2/NacosClusterV2ControllerTest.java deleted file mode 100644 index d6af466e2..000000000 --- a/core/src/test/java/com/alibaba/nacos/core/controller/v2/NacosClusterV2ControllerTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 1999-2021 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.nacos.core.controller.v2; - -import com.alibaba.nacos.common.model.RestResult; -import com.alibaba.nacos.core.cluster.Member; -import com.alibaba.nacos.core.cluster.NodeState; -import com.alibaba.nacos.core.cluster.ServerMemberManager; -import com.alibaba.nacos.core.model.request.LookupUpdateRequest; -import com.alibaba.nacos.sys.env.EnvUtil; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.mock.env.MockEnvironment; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -@RunWith(MockitoJUnitRunner.class) -public class NacosClusterV2ControllerTest { - - @InjectMocks - private NacosClusterV2Controller nacosClusterV2Controller; - - @Mock - private ServerMemberManager serverMemberManager; - - private final MockEnvironment environment = new MockEnvironment(); - - @Before - public void setUp() { - EnvUtil.setEnvironment(environment); - } - - @Test - public void testSelf() { - Member self = new Member(); - Mockito.when(serverMemberManager.getSelf()).thenReturn(self); - - RestResult result = nacosClusterV2Controller.self(); - Assert.assertTrue(result.ok()); - Assert.assertEquals(self, result.getData()); - } - - @Test - public void testListNodes() { - Member member1 = new Member(); - member1.setIp("1.1.1.1"); - member1.setPort(8848); - member1.setState(NodeState.DOWN); - Member member2 = new Member(); - member2.setIp("2.2.2.2"); - member2.setPort(8848); - - List members = Arrays.asList(member1, member2); - Mockito.when(serverMemberManager.allMembers()).thenReturn(members); - - RestResult> result1 = nacosClusterV2Controller.listNodes("1.1.1.1", null); - Assert.assertTrue(result1.getData().stream().findFirst().isPresent()); - Assert.assertEquals("1.1.1.1:8848", result1.getData().stream().findFirst().get().getAddress()); - - RestResult> result2 = nacosClusterV2Controller.listNodes(null, "up"); - Assert.assertTrue(result2.getData().stream().findFirst().isPresent()); - Assert.assertEquals("2.2.2.2:8848", result2.getData().stream().findFirst().get().getAddress()); - } - - @Test - public void testUpdate() { - Mockito.when(serverMemberManager.update(Mockito.any())).thenReturn(true); - - Member member = new Member(); - member.setIp("1.1.1.1"); - member.setPort(8848); - member.setAddress("test"); - RestResult result = nacosClusterV2Controller.updateNodes(Collections.singletonList(member)); - Assert.assertTrue(result.ok()); - } - - @Test - public void testSwitchLookup() { - LookupUpdateRequest request = new LookupUpdateRequest(); - request.setType("test"); - - RestResult result = nacosClusterV2Controller.updateLookup(request); - Assert.assertTrue(result.ok()); - } - - @Test - public void testLeave() throws Exception { - RestResult result = nacosClusterV2Controller.deleteNodes(Collections.singletonList("1.1.1.1")); - Assert.assertFalse(result.ok()); - Assert.assertEquals(405, result.getCode()); - } -} diff --git a/core/src/test/java/com/alibaba/nacos/core/distributed/raft/processor/AbstractProcessorTest.java b/core/src/test/java/com/alibaba/nacos/core/distributed/raft/processor/AbstractProcessorTest.java index 8cde8e4a0..968f37785 100644 --- a/core/src/test/java/com/alibaba/nacos/core/distributed/raft/processor/AbstractProcessorTest.java +++ b/core/src/test/java/com/alibaba/nacos/core/distributed/raft/processor/AbstractProcessorTest.java @@ -63,7 +63,7 @@ public class AbstractProcessorTest { return null; } }; - AbstractProcessor processor = new NacosLogProcessor(server, SerializeFactory.getDefault()); + AbstractProcessor processor = new NacosWriteRequestProcessor(server, SerializeFactory.getDefault()); processor.execute(server, context, WriteRequest.newBuilder().build(), new JRaftServer.RaftGroupTuple()); Response response = reference.get(); diff --git a/core/src/test/java/com/alibaba/nacos/core/service/NacosClusterOperationServiceTest.java b/core/src/test/java/com/alibaba/nacos/core/service/NacosClusterOperationServiceTest.java new file mode 100644 index 000000000..5e46a42f7 --- /dev/null +++ b/core/src/test/java/com/alibaba/nacos/core/service/NacosClusterOperationServiceTest.java @@ -0,0 +1,140 @@ +/* + * Copyright 1999-2022 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.nacos.core.service; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.core.cluster.Member; +import com.alibaba.nacos.core.cluster.NodeState; +import com.alibaba.nacos.core.cluster.ServerMemberManager; +import com.alibaba.nacos.core.model.request.LookupUpdateRequest; +import com.alibaba.nacos.sys.env.EnvUtil; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.mock.env.MockEnvironment; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * NacosClusterOperationTest. + * @author dongyafei + * @date 2022/8/15 + */ + +@RunWith(MockitoJUnitRunner.class) +public class NacosClusterOperationServiceTest { + + private NacosClusterOperationService nacosClusterOperationService; + + @Mock + private ServerMemberManager serverMemberManager; + + @Mock + private final MockEnvironment environment = new MockEnvironment(); + + @Before + public void setUp() throws Exception { + this.nacosClusterOperationService = new NacosClusterOperationService(serverMemberManager); + EnvUtil.setEnvironment(environment); + } + + @Test + public void testSelf() { + Member member = new Member(); + member.setIp("1.1.1.1"); + member.setPort(8848); + member.setState(NodeState.UP); + + when(serverMemberManager.getSelf()).thenReturn(member); + + Member result = nacosClusterOperationService.self(); + assertEquals("1.1.1.1:8848", result.getAddress()); + } + + @Test + public void testListNodes() throws NacosException { + Member member1 = new Member(); + member1.setIp("1.1.1.1"); + member1.setPort(8848); + member1.setState(NodeState.DOWN); + Member member2 = new Member(); + member2.setIp("2.2.2.2"); + member2.setPort(8848); + List members = Arrays.asList(member1, member2); + + when(serverMemberManager.allMembers()).thenReturn(members); + + Collection result1 = nacosClusterOperationService.listNodes("1.1.1.1", null); + assertTrue(result1.stream().findFirst().isPresent()); + assertEquals("1.1.1.1:8848", result1.stream().findFirst().get().getAddress()); + + Collection result2 = nacosClusterOperationService.listNodes(null, NodeState.UP); + assertTrue(result2.stream().findFirst().isPresent()); + assertEquals("2.2.2.2:8848", result2.stream().findFirst().get().getAddress()); + } + + @Test + public void testSelfHealth() { + Member member = new Member(); + member.setIp("1.1.1.1"); + member.setPort(8848); + member.setState(NodeState.UP); + + when(serverMemberManager.getSelf()).thenReturn(member); + + String health = nacosClusterOperationService.selfHealth(); + assertEquals(NodeState.UP.name(), health); + } + + @Test + public void testUpdateNodes() { + Member member1 = new Member(); + member1.setIp("1.1.1.1"); + member1.setAddress("test"); + member1.setPort(8848); + member1.setState(NodeState.DOWN); + Member member2 = new Member(); + member2.setIp("2.2.2.2"); + member2.setPort(8848); + List members = Arrays.asList(member1, member2); + + when(serverMemberManager.update(any())).thenReturn(true); + Boolean result = nacosClusterOperationService.updateNodes(members); + verify(serverMemberManager, times(1)).update(any()); + assertEquals(true, result); + } + + @Test + public void testUpdateLookup() throws NacosException { + LookupUpdateRequest lookupUpdateRequest = new LookupUpdateRequest(); + lookupUpdateRequest.setType("test"); + Boolean result = nacosClusterOperationService.updateLookup(lookupUpdateRequest); + verify(serverMemberManager).switchLookup("test"); + assertEquals(true, result); + } +} diff --git a/core/src/test/java/com/alibaba/nacos/core/trace/NacosCombinedTraceSubscriberTest.java b/core/src/test/java/com/alibaba/nacos/core/trace/NacosCombinedTraceSubscriberTest.java new file mode 100644 index 000000000..0788e8ddc --- /dev/null +++ b/core/src/test/java/com/alibaba/nacos/core/trace/NacosCombinedTraceSubscriberTest.java @@ -0,0 +1,108 @@ +/* + * Copyright 1999-2021 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.nacos.core.trace; + +import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.trace.DeregisterInstanceReason; +import com.alibaba.nacos.common.trace.event.TraceEvent; +import com.alibaba.nacos.common.trace.event.naming.DeregisterInstanceTraceEvent; +import com.alibaba.nacos.common.trace.event.naming.NamingTraceEvent; +import com.alibaba.nacos.common.trace.event.naming.RegisterInstanceTraceEvent; +import com.alibaba.nacos.plugin.trace.NacosTracePluginManager; +import com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@SuppressWarnings("all") +@RunWith(MockitoJUnitRunner.class) +public class NacosCombinedTraceSubscriberTest { + + @Mock + private NacosTraceSubscriber mockSubscriber; + + @Mock + private NacosTraceSubscriber mockSubscriber2; + + private NacosCombinedTraceSubscriber combinedTraceSubscriber; + + @Before + public void setUp() throws Exception { + Map traceSubscribers = (Map) ReflectionTestUtils + .getField(NacosTracePluginManager.getInstance(), "traceSubscribers"); + traceSubscribers.put("nacos-combined", mockSubscriber); + traceSubscribers.put("nacos-combined2", mockSubscriber2); + List> testEvents = new LinkedList<>(); + testEvents.add(RegisterInstanceTraceEvent.class); + testEvents.add(DeregisterInstanceTraceEvent.class); + testEvents.add(TraceEvent.class); + when(mockSubscriber.subscribeTypes()).thenReturn(testEvents); + when(mockSubscriber2.subscribeTypes()).thenReturn(Collections.singletonList(RegisterInstanceTraceEvent.class)); + combinedTraceSubscriber = new NacosCombinedTraceSubscriber(NamingTraceEvent.class); + } + + @After + public void tearDown() throws Exception { + Map traceSubscribers = (Map) ReflectionTestUtils + .getField(NacosTracePluginManager.getInstance(), "traceSubscribers"); + traceSubscribers.remove("nacos-combined"); + traceSubscribers.remove("nacos-combined2"); + combinedTraceSubscriber.shutdown(); + } + + @Test + public void testSubscribeTypes() { + List> actual = combinedTraceSubscriber.subscribeTypes(); + assertEquals(2, actual.size()); + assertTrue(actual.contains(RegisterInstanceTraceEvent.class)); + assertTrue(actual.contains(DeregisterInstanceTraceEvent.class)); + } + + @Test + public void testOnEvent() { + RegisterInstanceTraceEvent event = new RegisterInstanceTraceEvent(1L, "", true, "", "", "", "", 1); + doThrow(new RuntimeException("test")).when(mockSubscriber2).onEvent(event); + combinedTraceSubscriber.onEvent(event); + verify(mockSubscriber).onEvent(event); + verify(mockSubscriber2).onEvent(event); + DeregisterInstanceTraceEvent event1 = new DeregisterInstanceTraceEvent(1L, "", true, + DeregisterInstanceReason.REQUEST, "", "", "", "", 1); + combinedTraceSubscriber.onEvent(event1); + verify(mockSubscriber).onEvent(event1); + verify(mockSubscriber2, never()).onEvent(event1); + TraceEvent event2 = new TraceEvent("", 1L, "", "", ""); + combinedTraceSubscriber.onEvent(event2); + verify(mockSubscriber, never()).onEvent(event2); + verify(mockSubscriber2, never()).onEvent(event2); + } +} diff --git a/distribution/conf/application.properties b/distribution/conf/application.properties index 60b24d823..285ecfb1e 100644 --- a/distribution/conf/application.properties +++ b/distribution/conf/application.properties @@ -18,7 +18,7 @@ ### Default web context path: server.servlet.contextPath=/nacos ### Include message field -server.error.include-message=ON_PARAM +server.error.include-message=ALWAYS ### Default web server port: server.port=8848 @@ -49,14 +49,6 @@ db.pool.config.maximumPoolSize=20 db.pool.config.minimumIdle=2 #*************** Naming Module Related Configurations ***************# -### Data dispatch task execution period in milliseconds: Will removed on v2.1.X, replace with nacos.core.protocol.distro.data.sync.delayMs -# nacos.naming.distro.taskDispatchPeriod=200 - -### Data count of batch sync task: Will removed on v2.1.X. Deprecated -# nacos.naming.distro.batchSyncKeyCount=1000 - -### Retry delay in milliseconds if sync task failed: Will removed on v2.1.X, replace with nacos.core.protocol.distro.data.sync.retryDelayMs -# nacos.naming.distro.syncRetryDelay=5000 ### If enable data warmup. If set to false, the server would accept request without local data preparation: # nacos.naming.data.warmup=true @@ -64,11 +56,6 @@ db.pool.config.minimumIdle=2 ### If enable the instance auto expiration, kind like of health check of instance: # nacos.naming.expireInstance=true -### will be removed and replaced by `nacos.naming.clean` properties -nacos.naming.empty-service.auto-clean=true -nacos.naming.empty-service.clean.initial-delay-ms=50000 -nacos.naming.empty-service.clean.period-time-ms=30000 - ### Add in 2.0.0 ### The interval to clean empty service, unit: milliseconds. # nacos.naming.clean.empty-service.interval=60000 @@ -139,7 +126,7 @@ server.tomcat.basedir=file:. ### If enable spring security, this option is deprecated in 1.2.0: #spring.security.enabled=false -### The ignore urls of auth, is deprecated in 1.2.0: +### The ignore urls of auth nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/** ### The auth system to use, currently only 'nacos' and 'ldap' is supported: @@ -171,6 +158,7 @@ nacos.core.auth.plugin.nacos.token.secret.key=SecretKey0123456789012345678901234 #nacos.core.auth.ldap.userDn=cn=admin,${nacos.core.auth.ldap.basedc} #nacos.core.auth.ldap.password=admin #nacos.core.auth.ldap.userdn=cn={0},dc=example,dc=org +#nacos.core.auth.ldap.filter.prefix=uid #*************** Istio Related Configurations ***************# diff --git a/distribution/conf/application.properties.example b/distribution/conf/application.properties.example index e4359aa08..bb43880cb 100644 --- a/distribution/conf/application.properties.example +++ b/distribution/conf/application.properties.example @@ -18,7 +18,7 @@ ### Default web context path: server.servlet.contextPath=/nacos ### Include message field -server.error.include-message=ON_PARAM +server.error.include-message=ALWAYS ### Default web server port: server.port=8848 @@ -49,15 +49,6 @@ db.pool.config.maximumPoolSize=20 db.pool.config.minimumIdle=2 #*************** Naming Module Related Configurations ***************# -### Data dispatch task execution period in milliseconds: Will removed on v2.1.X, replace with nacos.core.protocol.distro.data.sync.delayMs -# nacos.naming.distro.taskDispatchPeriod=200 - -### Data count of batch sync task: Will removed on v2.1.X. Deprecated -# nacos.naming.distro.batchSyncKeyCount=1000 - -### Retry delay in milliseconds if sync task failed: Will removed on v2.1.X, replace with nacos.core.protocol.distro.data.sync.retryDelayMs -# nacos.naming.distro.syncRetryDelay=5000 - ### If enable data warmup. If set to false, the server would accept request without local data preparation: # nacos.naming.data.warmup=true @@ -155,6 +146,7 @@ nacos.core.auth.enabled=false #nacos.core.auth.ldap.userDn=cn=admin,${nacos.core.auth.ldap.basedc} #nacos.core.auth.ldap.password=admin #nacos.core.auth.ldap.userdn=cn={0},dc=example,dc=org +#nacos.core.auth.ldap.filter.prefix=uid ### The token expiration in seconds: diff --git a/example/src/main/java/com/alibaba/nacos/example/NamingExample.java b/example/src/main/java/com/alibaba/nacos/example/NamingExample.java index 6e958d316..c195c1c06 100644 --- a/example/src/main/java/com/alibaba/nacos/example/NamingExample.java +++ b/example/src/main/java/com/alibaba/nacos/example/NamingExample.java @@ -41,8 +41,8 @@ public class NamingExample { public static void main(String[] args) throws NacosException, InterruptedException { Properties properties = new Properties(); - properties.setProperty("serverAddr", System.getProperty("serverAddr")); - properties.setProperty("namespace", System.getProperty("namespace")); + properties.setProperty("serverAddr", System.getProperty("serverAddr", "localhost")); + properties.setProperty("namespace", System.getProperty("namespace", "public")); NamingService naming = NamingFactory.createNamingService(properties); diff --git a/istio/src/main/java/com/alibaba/nacos/istio/util/IstioCrdUtil.java b/istio/src/main/java/com/alibaba/nacos/istio/util/IstioCrdUtil.java index 038ac55d5..5b809054c 100644 --- a/istio/src/main/java/com/alibaba/nacos/istio/util/IstioCrdUtil.java +++ b/istio/src/main/java/com/alibaba/nacos/istio/util/IstioCrdUtil.java @@ -73,7 +73,7 @@ public class IstioCrdUtil { if (StringUtils.isNotEmpty(instance.getMetadata().get("protocol"))) { protocol = instance.getMetadata().get("protocol"); - if (protocol.equals("triple")||protocol.equals("tri")){ + if ("triple".equals(protocol) || "tri".equals(protocol)){ protocol = "grpc"; } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/cluster/ServerListManager.java b/naming/src/main/java/com/alibaba/nacos/naming/cluster/ServerListManager.java deleted file mode 100644 index 548870d32..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/cluster/ServerListManager.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * 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.nacos.naming.cluster; - -import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.utils.InternetAddressUtil; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.core.cluster.Member; -import com.alibaba.nacos.core.cluster.MemberChangeListener; -import com.alibaba.nacos.core.cluster.MemberMetaDataConstants; -import com.alibaba.nacos.core.cluster.MembersChangeEvent; -import com.alibaba.nacos.core.cluster.NodeState; -import com.alibaba.nacos.core.cluster.ServerMemberManager; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeer; -import com.alibaba.nacos.naming.misc.GlobalExecutor; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.Message; -import com.alibaba.nacos.naming.misc.NamingProxy; -import com.alibaba.nacos.naming.misc.ServerStatusSynchronizer; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.misc.Synchronizer; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.sys.env.EnvUtil; -import com.alibaba.nacos.common.utils.StringUtils; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; - -/** - * The manager to globally refresh and operate server list. - * - * @author nkorange - * @since 1.0.0 - * @deprecated 1.3.0 This object will be deleted sometime after version 1.3.0 - */ -@Component("serverListManager") -public class ServerListManager extends MemberChangeListener { - - private static final String LOCALHOST_SITE = UtilsAndCommons.UNKNOWN_SITE; - - private final SwitchDomain switchDomain; - - private final ServerMemberManager memberManager; - - private final Synchronizer synchronizer = new ServerStatusSynchronizer(); - - private volatile List servers; - - public ServerListManager(final SwitchDomain switchDomain, final ServerMemberManager memberManager) { - this.switchDomain = switchDomain; - this.memberManager = memberManager; - NotifyCenter.registerSubscriber(this); - this.servers = new ArrayList<>(memberManager.allMembers()); - } - - @PostConstruct - public void init() { - GlobalExecutor.registerServerStatusReporter(new ServerStatusReporter(), 2000); - GlobalExecutor.registerServerInfoUpdater(new ServerInfoUpdater()); - } - - /** - * Judge whether contain server in cluster. - * - * @param serverAddress server address - * @return true if contain, otherwise false - */ - public boolean contains(String serverAddress) { - for (Member server : getServers()) { - if (Objects.equals(serverAddress, server.getAddress())) { - return true; - } - } - return false; - } - - public List getServers() { - return servers; - } - - @Override - public void onEvent(MembersChangeEvent event) { - this.servers = new ArrayList<>(event.getMembers()); - } - - /** - * Compatible with older version logic, In version 1.2.1 and before - * - * @param configInfo site:ip:lastReportTime:weight - */ - public synchronized void onReceiveServerStatus(String configInfo) { - - Loggers.SRV_LOG.info("receive config info: {}", configInfo); - - String[] configs = configInfo.split("\r\n"); - if (configs.length == 0) { - return; - } - - for (String config : configs) { - // site:ip:lastReportTime:weight - String[] params = config.split("#"); - if (params.length <= 3) { - Loggers.SRV_LOG.warn("received malformed distro map data: {}", config); - continue; - } - - String[] info = InternetAddressUtil.splitIPPortStr(params[1]); - Member server = Optional.ofNullable(memberManager.find(params[1])) - .orElse(Member.builder().ip(info[0]).state(NodeState.UP) - .port(Integer.parseInt(info[1])).build()); - - // This metadata information exists from 1.3.0 onwards "version" - if (server.getExtendVal(MemberMetaDataConstants.VERSION) == null) { - // copy to trigger member change event - server = server.copy(); - // received heartbeat from server of version before 1.3.0 - if (!server.getState().equals(NodeState.UP)) { - Loggers.SRV_LOG.info("member {} state changed to UP", server); - } - server.setState(NodeState.UP); - } - server.setExtendVal(MemberMetaDataConstants.SITE_KEY, params[0]); - server.setExtendVal(MemberMetaDataConstants.WEIGHT, params.length == 4 ? Integer.parseInt(params[3]) : 1); - memberManager.update(server); - - if (!contains(server.getAddress())) { - throw new IllegalArgumentException("server: " + server.getAddress() + " is not in serverlist"); - } - } - } - - private class ServerInfoUpdater implements Runnable { - - private int cursor = 0; - - @Override - public void run() { - List members = servers; - if (members.isEmpty()) { - return; - } - - this.cursor = (this.cursor + 1) % members.size(); - Member target = members.get(cursor); - if (Objects.equals(target.getAddress(), EnvUtil.getLocalAddress())) { - return; - } - - // This metadata information exists from 1.3.0 onwards "version" - if (target.getExtendVal(MemberMetaDataConstants.VERSION) != null) { - return; - } - - final String path = - UtilsAndCommons.NACOS_NAMING_OPERATOR_CONTEXT + UtilsAndCommons.NACOS_NAMING_CLUSTER_CONTEXT - + "/state"; - final Map params = new HashMap(2); - final String server = target.getAddress(); - - try { - String content = NamingProxy.reqCommon(path, params, server, false); - if (!StringUtils.EMPTY.equals(content)) { - RaftPeer raftPeer = JacksonUtils.toObj(content, RaftPeer.class); - if (null != raftPeer) { - String json = JacksonUtils.toJson(raftPeer); - Map map = JacksonUtils.toObj(json, HashMap.class); - target.setExtendVal("naming", map); - memberManager.update(target); - } - } - } catch (Exception ignore) { - // - } - } - } - - private class ServerStatusReporter implements Runnable { - - @Override - public void run() { - try { - - if (EnvUtil.getPort() <= 0) { - return; - } - - int weight = EnvUtil.getAvailableProcessors(0.5); - if (weight <= 0) { - weight = 1; - } - - long curTime = System.currentTimeMillis(); - String status = LOCALHOST_SITE + "#" + EnvUtil.getLocalAddress() + "#" + curTime + "#" + weight - + "\r\n"; - - List allServers = getServers(); - - if (!contains(EnvUtil.getLocalAddress())) { - Loggers.SRV_LOG.error("local ip is not in serverlist, ip: {}, serverlist: {}", - EnvUtil.getLocalAddress(), allServers); - return; - } - - if (allServers.size() > 0 && !EnvUtil.getLocalAddress() - .contains(InternetAddressUtil.localHostIP())) { - for (Member server : allServers) { - if (Objects.equals(server.getAddress(), EnvUtil.getLocalAddress())) { - continue; - } - - // This metadata information exists from 1.3.0 onwards "version" - if (server.getExtendVal(MemberMetaDataConstants.VERSION) != null) { - Loggers.SRV_LOG - .debug("[SERVER-STATUS] target {} has extend val {} = {}, use new api report status", - server.getAddress(), MemberMetaDataConstants.VERSION, - server.getExtendVal(MemberMetaDataConstants.VERSION)); - continue; - } - - Message msg = new Message(); - msg.setData(status); - - synchronizer.send(server.getAddress(), msg); - } - } - } catch (Exception e) { - Loggers.SRV_LOG.error("[SERVER-STATUS] Exception while sending server status", e); - } finally { - GlobalExecutor - .registerServerStatusReporter(this, switchDomain.getServerStatusSynchronizationPeriodMillis()); - } - - } - } - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/cluster/ServerStatusManager.java b/naming/src/main/java/com/alibaba/nacos/naming/cluster/ServerStatusManager.java index b9b866847..1020b7bbd 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/cluster/ServerStatusManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/cluster/ServerStatusManager.java @@ -35,7 +35,7 @@ import java.util.Optional; @Service public class ServerStatusManager { - @Resource(name = "consistencyDelegate") + @Resource(name = "persistentConsistencyServiceDelegate") private ConsistencyService consistencyService; private final SwitchDomain switchDomain; diff --git a/naming/src/main/java/com/alibaba/nacos/naming/cluster/transport/JacksonSerializer.java b/naming/src/main/java/com/alibaba/nacos/naming/cluster/transport/JacksonSerializer.java index e1ce5bc65..5e19bc6f5 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/cluster/transport/JacksonSerializer.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/cluster/transport/JacksonSerializer.java @@ -16,16 +16,8 @@ package com.alibaba.nacos.naming.cluster.transport; -import java.util.HashMap; -import java.util.Map; - -import org.springframework.stereotype.Component; - import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.pojo.Record; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.stereotype.Component; /** * Use Jackson to serialize data. @@ -50,25 +42,4 @@ public class JacksonSerializer implements Serializer { public T deserialize(byte[] data, Class clazz) { return JacksonUtils.toObj(data, clazz); } - - @Override - public Map> deserializeMap(byte[] data, Class clazz) { - Map> resultMap; - try { - resultMap = JacksonUtils.toObj(data, new TypeReference>>() { - }); - } catch (Exception e) { - Map dataMap = JacksonUtils.toObj(data, new TypeReference>() { - }); - resultMap = new HashMap<>(dataMap.size()); - for (Map.Entry entry : dataMap.entrySet()) { - Datum datum = new Datum<>(); - datum.timestamp.set(entry.getValue().get(TIMESTAMP_KEY).asLong()); - datum.key = entry.getValue().get(KEY).asText(); - datum.value = JacksonUtils.toObj(entry.getValue().get(VALUE).toString(), clazz); - resultMap.put(entry.getKey(), datum); - } - } - return resultMap; - } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/cluster/transport/Serializer.java b/naming/src/main/java/com/alibaba/nacos/naming/cluster/transport/Serializer.java index e15813e9b..229e068a2 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/cluster/transport/Serializer.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/cluster/transport/Serializer.java @@ -16,11 +16,6 @@ package com.alibaba.nacos.naming.cluster.transport; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.pojo.Record; - -import java.util.Map; - /** * Serializer specially for large map of data. * @@ -47,14 +42,4 @@ public interface Serializer { * @return deserialized data map */ T deserialize(byte[] data, Class clazz); - - /** - * Deserialize byte array data to target type. - * - * @param target type - * @param data data to deserialize - * @param clazz target type - * @return deserialized data map - */ - Map> deserializeMap(byte[] data, Class clazz); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/DelegateConsistencyServiceImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/DelegateConsistencyServiceImpl.java deleted file mode 100644 index c6df561ec..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/DelegateConsistencyServiceImpl.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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.nacos.naming.consistency; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.naming.consistency.ephemeral.EphemeralConsistencyService; -import com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyServiceDelegateImpl; -import com.alibaba.nacos.naming.pojo.Record; -import org.springframework.context.annotation.DependsOn; -import org.springframework.stereotype.Service; - -import java.util.Optional; - -/** - * Consistency delegate. - * - * @author nkorange - * @since 1.0.0 - */ -@DependsOn("ProtocolManager") -@Service("consistencyDelegate") -public class DelegateConsistencyServiceImpl implements ConsistencyService { - - private final PersistentConsistencyServiceDelegateImpl persistentConsistencyService; - - private final EphemeralConsistencyService ephemeralConsistencyService; - - public DelegateConsistencyServiceImpl(PersistentConsistencyServiceDelegateImpl persistentConsistencyService, - EphemeralConsistencyService ephemeralConsistencyService) { - this.persistentConsistencyService = persistentConsistencyService; - this.ephemeralConsistencyService = ephemeralConsistencyService; - } - - @Override - public void put(String key, Record value) throws NacosException { - mapConsistencyService(key).put(key, value); - } - - @Override - public void remove(String key) throws NacosException { - mapConsistencyService(key).remove(key); - } - - @Override - public Datum get(String key) throws NacosException { - return mapConsistencyService(key).get(key); - } - - @Override - public void listen(String key, RecordListener listener) throws NacosException { - - // this special key is listened by both: - if (KeyBuilder.SERVICE_META_KEY_PREFIX.equals(key)) { - persistentConsistencyService.listen(key, listener); - ephemeralConsistencyService.listen(key, listener); - return; - } - - mapConsistencyService(key).listen(key, listener); - } - - @Override - public void unListen(String key, RecordListener listener) throws NacosException { - mapConsistencyService(key).unListen(key, listener); - } - - @Override - public boolean isAvailable() { - return ephemeralConsistencyService.isAvailable() && persistentConsistencyService.isAvailable(); - } - - @Override - public Optional getErrorMsg() { - String errorMsg; - if (ephemeralConsistencyService.getErrorMsg().isPresent() - && persistentConsistencyService.getErrorMsg().isPresent()) { - errorMsg = "'" + ephemeralConsistencyService.getErrorMsg().get() + "' in Distro protocol and '" - + persistentConsistencyService.getErrorMsg().get() + "' in jRaft protocol"; - } else if (ephemeralConsistencyService.getErrorMsg().isPresent() - && !persistentConsistencyService.getErrorMsg().isPresent()) { - errorMsg = ephemeralConsistencyService.getErrorMsg().get(); - } else if (!ephemeralConsistencyService.getErrorMsg().isPresent() - && persistentConsistencyService.getErrorMsg().isPresent()) { - errorMsg = persistentConsistencyService.getErrorMsg().get(); - } else { - errorMsg = null; - } - return Optional.ofNullable(errorMsg); - } - - private ConsistencyService mapConsistencyService(String key) { - return KeyBuilder.matchEphemeralKey(key) ? ephemeralConsistencyService : persistentConsistencyService; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/KeyBuilder.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/KeyBuilder.java index 998bde582..d09a30472 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/KeyBuilder.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/consistency/KeyBuilder.java @@ -17,9 +17,6 @@ package com.alibaba.nacos.naming.consistency; import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.common.utils.StringUtils; - -import static com.alibaba.nacos.naming.misc.UtilsAndCommons.RAFT_CACHE_FILE_PREFIX; /** * Key operations for data. @@ -31,123 +28,17 @@ public class KeyBuilder { public static final String NAMESPACE_KEY_CONNECTOR = "##"; - private static final String EPHEMERAL_KEY_PREFIX = "ephemeral."; - public static final String SERVICE_META_KEY_PREFIX = "com.alibaba.nacos.naming.domains.meta."; - public static final String INSTANCE_LIST_KEY_PREFIX = "com.alibaba.nacos.naming.iplist."; - - public static final String BRIEF_SERVICE_META_KEY_PREFIX = "meta."; - - public static final String BRIEF_INSTANCE_LIST_KEY_PREFIX = "iplist."; - - public static final String RESOURCE_KEY_SNAPSHOT = "snapshot"; - - public static final String RESOURCE_KEY_CHECKSUM = "checksum"; - - private static String buildEphemeralInstanceListKey(String namespaceId, String serviceName) { - return INSTANCE_LIST_KEY_PREFIX + EPHEMERAL_KEY_PREFIX + namespaceId + NAMESPACE_KEY_CONNECTOR + serviceName; - } - - private static String buildPersistentInstanceListKey(String namespaceId, String serviceName) { - return INSTANCE_LIST_KEY_PREFIX + namespaceId + NAMESPACE_KEY_CONNECTOR + serviceName; - } - - public static String buildInstanceListKey(String namespaceId, String serviceName, boolean ephemeral) { - return ephemeral ? buildEphemeralInstanceListKey(namespaceId, serviceName) - : buildPersistentInstanceListKey(namespaceId, serviceName); - } - public static String buildServiceMetaKey(String namespaceId, String serviceName) { return SERVICE_META_KEY_PREFIX + namespaceId + NAMESPACE_KEY_CONNECTOR + serviceName; } - public static String getSwitchDomainKey() { + public static String getSwitchDomainKey() { return SERVICE_META_KEY_PREFIX + UtilsAndCommons.SWITCH_DOMAIN_NAME; } - public static boolean matchEphemeralInstanceListKey(String key) { - return key.startsWith(INSTANCE_LIST_KEY_PREFIX + EPHEMERAL_KEY_PREFIX); - } - - public static boolean matchInstanceListKey(String key) { - return key.startsWith(INSTANCE_LIST_KEY_PREFIX) || key.startsWith(BRIEF_INSTANCE_LIST_KEY_PREFIX); - } - - public static boolean matchInstanceListKey(String key, String namespaceId, String serviceName) { - return matchInstanceListKey(key) && matchServiceName(key, namespaceId, serviceName); - } - - public static boolean matchServiceMetaKey(String key) { - return key.startsWith(SERVICE_META_KEY_PREFIX) || key.startsWith(BRIEF_SERVICE_META_KEY_PREFIX); - } - - public static boolean matchServiceMetaKey(String key, String namespaceId, String serviceName) { - return matchServiceMetaKey(key) && matchServiceName(key, namespaceId, serviceName); - } - public static boolean matchSwitchKey(String key) { return key.endsWith(UtilsAndCommons.SWITCH_DOMAIN_NAME); } - - public static boolean matchServiceName(String key, String namespaceId, String serviceName) { - return key.endsWith(namespaceId + NAMESPACE_KEY_CONNECTOR + serviceName); - } - - public static boolean matchEphemeralKey(String key) { - // currently only instance list has ephemeral type: - return matchEphemeralInstanceListKey(key); - } - - public static boolean matchPersistentKey(String key) { - return !matchEphemeralKey(key); - } - - public static String briefInstanceListkey(String key) { - return BRIEF_INSTANCE_LIST_KEY_PREFIX + key.split(INSTANCE_LIST_KEY_PREFIX)[1]; - } - - public static String briefServiceMetaKey(String key) { - return BRIEF_SERVICE_META_KEY_PREFIX + key.split(SERVICE_META_KEY_PREFIX)[1]; - } - - public static String detailInstanceListkey(String key) { - return INSTANCE_LIST_KEY_PREFIX.substring(0, INSTANCE_LIST_KEY_PREFIX.indexOf(BRIEF_INSTANCE_LIST_KEY_PREFIX)) - + key; - } - - public static String detailServiceMetaKey(String key) { - return SERVICE_META_KEY_PREFIX.substring(0, SERVICE_META_KEY_PREFIX.indexOf(BRIEF_SERVICE_META_KEY_PREFIX)) - + key; - } - - public static String getNamespace(String key) { - - if (matchSwitchKey(key)) { - return StringUtils.EMPTY; - } - - if (matchServiceMetaKey(key)) { - return key.split(NAMESPACE_KEY_CONNECTOR)[0].substring(SERVICE_META_KEY_PREFIX.length()); - } - - if (matchEphemeralInstanceListKey(key)) { - return key.split(NAMESPACE_KEY_CONNECTOR)[0] - .substring(INSTANCE_LIST_KEY_PREFIX.length() + EPHEMERAL_KEY_PREFIX.length()); - } - - if (matchInstanceListKey(key)) { - return key.split(NAMESPACE_KEY_CONNECTOR)[0].substring(INSTANCE_LIST_KEY_PREFIX.length()); - } - - return StringUtils.EMPTY; - } - - public static String getServiceName(String key) { - return key.split(NAMESPACE_KEY_CONNECTOR)[1]; - } - - public static boolean isDatumCacheFile(String key) { - return key.startsWith(RAFT_CACHE_FILE_PREFIX); - } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/EphemeralConsistencyService.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/EphemeralConsistencyService.java deleted file mode 100644 index 0a66d3f98..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/EphemeralConsistencyService.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.nacos.naming.consistency.ephemeral; - -import com.alibaba.nacos.naming.consistency.ConsistencyService; - -/** - * A type of consistency for ephemeral data. - * - *

This kind of consistency is not required to store data on disk or database, because the ephemeral data always - * keeps a session with server and as long as the session still lives the ephemeral data won't be lost. - * - *

What is required is that writing should always be successful even if network partition happens. And when the - * network recovers, data of each partition is merged into one set, so the cluster resumes to a consistent status. - * - * @author nkorange - * @since 1.0.0 - */ -public interface EphemeralConsistencyService extends ConsistencyService { - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DataStore.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DataStore.java deleted file mode 100644 index fdece6539..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DataStore.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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.nacos.naming.consistency.ephemeral.distro; - -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.core.Instances; -import org.springframework.stereotype.Component; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Store of data. - * - * @author nkorange - * @since 1.0.0 - */ -@Component -public class DataStore { - - private Map dataMap = new ConcurrentHashMap<>(1024); - - public void put(String key, Datum value) { - dataMap.put(key, value); - } - - public Datum remove(String key) { - return dataMap.remove(key); - } - - public Set keys() { - return dataMap.keySet(); - } - - public Datum get(String key) { - return dataMap.get(key); - } - - public boolean contains(String key) { - return dataMap.containsKey(key); - } - - /** - * Batch get datum for a list of keys. - * - * @param keys of datum - * @return list of datum - */ - public Map batchGet(List keys) { - Map map = new HashMap<>(128); - for (String key : keys) { - Datum datum = dataMap.get(key); - if (datum == null) { - continue; - } - map.put(key, datum); - } - return map; - } - - public int getInstanceCount() { - int count = 0; - for (Map.Entry entry : dataMap.entrySet()) { - try { - Datum instancesDatum = entry.getValue(); - if (instancesDatum.value instanceof Instances) { - count += ((Instances) instancesDatum.value).getInstanceList().size(); - } - } catch (Exception ignore) { - } - } - return count; - } - - public Map getDataMap() { - return dataMap; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DistroConsistencyServiceImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DistroConsistencyServiceImpl.java deleted file mode 100644 index 16d41e17b..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DistroConsistencyServiceImpl.java +++ /dev/null @@ -1,462 +0,0 @@ -/* - * 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.nacos.naming.consistency.ephemeral.distro; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.consistency.DataOperation; -import com.alibaba.nacos.core.distributed.distro.DistroConfig; -import com.alibaba.nacos.core.distributed.distro.DistroProtocol; -import com.alibaba.nacos.core.distributed.distro.component.DistroDataProcessor; -import com.alibaba.nacos.core.distributed.distro.entity.DistroData; -import com.alibaba.nacos.core.distributed.distro.entity.DistroKey; -import com.alibaba.nacos.naming.cluster.ServerStatus; -import com.alibaba.nacos.naming.cluster.transport.Serializer; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.consistency.RecordListener; -import com.alibaba.nacos.naming.consistency.ephemeral.EphemeralConsistencyService; -import com.alibaba.nacos.naming.consistency.ephemeral.distro.combined.DistroHttpCombinedKey; -import com.alibaba.nacos.naming.core.DistroMapper; -import com.alibaba.nacos.naming.core.Instances; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import com.alibaba.nacos.naming.misc.GlobalConfig; -import com.alibaba.nacos.naming.misc.GlobalExecutor; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.pojo.Record; -import com.alibaba.nacos.sys.utils.ApplicationUtils; -import com.alibaba.nacos.common.utils.StringUtils; -import org.javatuples.Pair; -import org.springframework.context.annotation.DependsOn; - -import javax.annotation.PostConstruct; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; - -/** - * A consistency protocol algorithm called Distro - * - *

Use a distro algorithm to divide data into many blocks. Each Nacos server node takes responsibility for exactly - * one block of data. Each block of data is generated, removed and synchronized by its responsible server. So every - * Nacos server only handles writings for a subset of the total service data. - * - *

At mean time every Nacos server receives data sync of other Nacos server, so every Nacos server will eventually - * have a complete set of data. - * - * @author nkorange - * @since 1.0.0 - */ -@DependsOn("ProtocolManager") -@org.springframework.stereotype.Service("distroConsistencyService") -public class DistroConsistencyServiceImpl implements EphemeralConsistencyService, DistroDataProcessor { - - private static final String ON_RECEIVE_CHECKSUMS_PROCESSING_TAG = "1"; - - private final DistroMapper distroMapper; - - private final DataStore dataStore; - - private final Serializer serializer; - - private final SwitchDomain switchDomain; - - private final GlobalConfig globalConfig; - - private final DistroProtocol distroProtocol; - - private volatile Notifier notifier = new Notifier(); - - private Map> listeners = new ConcurrentHashMap<>(); - - private Map syncChecksumTasks = new ConcurrentHashMap<>(16); - - public DistroConsistencyServiceImpl(DistroMapper distroMapper, DataStore dataStore, Serializer serializer, - SwitchDomain switchDomain, GlobalConfig globalConfig, DistroProtocol distroProtocol) { - this.distroMapper = distroMapper; - this.dataStore = dataStore; - this.serializer = serializer; - this.switchDomain = switchDomain; - this.globalConfig = globalConfig; - this.distroProtocol = distroProtocol; - } - - @PostConstruct - public void init() { - GlobalExecutor.submitDistroNotifyTask(notifier); - } - - @Override - public void put(String key, Record value) throws NacosException { - onPut(key, value); - // If upgrade to 2.0.X, do not sync for v1. - if (ApplicationUtils.getBean(UpgradeJudgement.class).isUseGrpcFeatures()) { - return; - } - distroProtocol.sync(new DistroKey(key, KeyBuilder.INSTANCE_LIST_KEY_PREFIX), DataOperation.CHANGE, - DistroConfig.getInstance().getSyncDelayMillis()); - } - - @Override - public void remove(String key) throws NacosException { - onRemove(key); - listeners.remove(key); - } - - @Override - public Datum get(String key) throws NacosException { - return dataStore.get(key); - } - - /** - * Put a new record. - * - * @param key key of record - * @param value record - */ - public void onPut(String key, Record value) { - - if (KeyBuilder.matchEphemeralInstanceListKey(key)) { - Datum datum = new Datum<>(); - datum.value = (Instances) value; - datum.key = key; - datum.timestamp.incrementAndGet(); - dataStore.put(key, datum); - } - - if (!listeners.containsKey(key)) { - return; - } - - notifier.addTask(key, DataOperation.CHANGE); - } - - /** - * Remove a record. - * - * @param key key of record - */ - public void onRemove(String key) { - - dataStore.remove(key); - - if (!listeners.containsKey(key)) { - return; - } - - notifier.addTask(key, DataOperation.DELETE); - } - - /** - * Check sum when receive checksums request. - * - * @param checksumMap map of checksum - * @param server source server request checksum - */ - public void onReceiveChecksums(Map checksumMap, String server) { - - if (syncChecksumTasks.putIfAbsent(server, ON_RECEIVE_CHECKSUMS_PROCESSING_TAG) != null) { - // Already in process of this server: - Loggers.DISTRO.warn("sync checksum task already in process with {}", server); - return; - } - - try { - - List toUpdateKeys = new ArrayList<>(); - List toRemoveKeys = new ArrayList<>(); - for (Map.Entry entry : checksumMap.entrySet()) { - if (distroMapper.responsible(KeyBuilder.getServiceName(entry.getKey()))) { - // this key should not be sent from remote server: - Loggers.DISTRO.error("receive responsible key timestamp of " + entry.getKey() + " from " + server); - // abort the procedure: - return; - } - - if (!dataStore.contains(entry.getKey()) || dataStore.get(entry.getKey()).value == null || !dataStore - .get(entry.getKey()).value.getChecksum().equals(entry.getValue())) { - toUpdateKeys.add(entry.getKey()); - } - } - - for (String key : dataStore.keys()) { - - if (!server.equals(distroMapper.mapSrv(KeyBuilder.getServiceName(key)))) { - continue; - } - - if (!checksumMap.containsKey(key)) { - toRemoveKeys.add(key); - } - } - - Loggers.DISTRO - .info("to remove keys: {}, to update keys: {}, source: {}", toRemoveKeys, toUpdateKeys, server); - - for (String key : toRemoveKeys) { - onRemove(key); - } - - if (toUpdateKeys.isEmpty()) { - return; - } - - try { - DistroHttpCombinedKey distroKey = new DistroHttpCombinedKey(KeyBuilder.INSTANCE_LIST_KEY_PREFIX, - server); - distroKey.getActualResourceTypes().addAll(toUpdateKeys); - DistroData remoteData = distroProtocol.queryFromRemote(distroKey); - if (null != remoteData) { - processData(remoteData.getContent()); - } - } catch (Exception e) { - Loggers.DISTRO.error("get data from " + server + " failed!", e); - } - } finally { - // Remove this 'in process' flag: - syncChecksumTasks.remove(server); - } - } - - private boolean processData(byte[] data) throws Exception { - if (data.length > 0) { - Map> datumMap = serializer.deserializeMap(data, Instances.class); - - for (Map.Entry> entry : datumMap.entrySet()) { - dataStore.put(entry.getKey(), entry.getValue()); - - if (!listeners.containsKey(entry.getKey())) { - // pretty sure the service not exist: - if (switchDomain.isDefaultInstanceEphemeral()) { - // create empty service - Loggers.DISTRO.info("creating service {}", entry.getKey()); - Service service = new Service(); - String serviceName = KeyBuilder.getServiceName(entry.getKey()); - String namespaceId = KeyBuilder.getNamespace(entry.getKey()); - service.setName(serviceName); - service.setNamespaceId(namespaceId); - service.setGroupName(Constants.DEFAULT_GROUP); - // now validate the service. if failed, exception will be thrown - service.setLastModifiedMillis(System.currentTimeMillis()); - service.recalculateChecksum(); - - // The Listener corresponding to the key value must not be empty - RecordListener listener = listeners.get(KeyBuilder.SERVICE_META_KEY_PREFIX).peek(); - if (Objects.isNull(listener)) { - return false; - } - listener.onChange(KeyBuilder.buildServiceMetaKey(namespaceId, serviceName), service); - } - } - } - - for (Map.Entry> entry : datumMap.entrySet()) { - - if (!listeners.containsKey(entry.getKey())) { - // Should not happen: - Loggers.DISTRO.warn("listener of {} not found.", entry.getKey()); - continue; - } - - try { - for (RecordListener listener : listeners.get(entry.getKey())) { - listener.onChange(entry.getKey(), entry.getValue().value); - } - } catch (Exception e) { - Loggers.DISTRO.error("[NACOS-DISTRO] error while execute listener of key: {}", entry.getKey(), e); - continue; - } - - // Update data store if listener executed successfully: - dataStore.put(entry.getKey(), entry.getValue()); - } - } - return true; - } - - @Override - public boolean processData(DistroData distroData) { - DistroHttpData distroHttpData = (DistroHttpData) distroData; - Datum datum = (Datum) distroHttpData.getDeserializedContent(); - onPut(datum.key, datum.value); - return true; - } - - @Override - public String processType() { - return KeyBuilder.INSTANCE_LIST_KEY_PREFIX; - } - - @Override - public boolean processVerifyData(DistroData distroData, String sourceAddress) { - // If upgrade to 2.0.X, do not verify for v1. - if (ApplicationUtils.getBean(UpgradeJudgement.class).isUseGrpcFeatures()) { - return true; - } - DistroHttpData distroHttpData = (DistroHttpData) distroData; - Map verifyData = (Map) distroHttpData.getDeserializedContent(); - onReceiveChecksums(verifyData, sourceAddress); - return true; - } - - @Override - public boolean processSnapshot(DistroData distroData) { - try { - return processData(distroData.getContent()); - } catch (Exception e) { - return false; - } - } - - @Override - public void listen(String key, RecordListener listener) throws NacosException { - if (!listeners.containsKey(key)) { - listeners.put(key, new ConcurrentLinkedQueue<>()); - } - - if (listeners.get(key).contains(listener)) { - return; - } - - listeners.get(key).add(listener); - } - - @Override - public void unListen(String key, RecordListener listener) throws NacosException { - if (!listeners.containsKey(key)) { - return; - } - for (RecordListener recordListener : listeners.get(key)) { - if (recordListener.equals(listener)) { - listeners.get(key).remove(listener); - break; - } - } - } - - @Override - public boolean isAvailable() { - return isInitialized() || ServerStatus.UP.name().equals(switchDomain.getOverriddenServerStatus()); - } - - @Override - public Optional getErrorMsg() { - String errorMsg; - if (!isInitialized() && !ServerStatus.UP.name().equals(switchDomain.getOverriddenServerStatus())) { - errorMsg = "Distro protocol is not initialized"; - } else { - errorMsg = null; - } - return Optional.ofNullable(errorMsg); - } - - public boolean isInitialized() { - return distroProtocol.isInitialized() || !globalConfig.isDataWarmup(); - } - - public class Notifier implements Runnable { - - private ConcurrentHashMap services = new ConcurrentHashMap<>(10 * 1024); - - private BlockingQueue> tasks = new ArrayBlockingQueue<>(1024 * 1024); - - /** - * Add new notify task to queue. - * - * @param datumKey data key - * @param action action for data - */ - public void addTask(String datumKey, DataOperation action) { - - if (services.containsKey(datumKey) && action == DataOperation.CHANGE) { - return; - } - if (action == DataOperation.CHANGE) { - services.put(datumKey, StringUtils.EMPTY); - } - tasks.offer(Pair.with(datumKey, action)); - } - - public int getTaskSize() { - return tasks.size(); - } - - @Override - public void run() { - Loggers.DISTRO.info("distro notifier started"); - - for (; ; ) { - try { - Pair pair = tasks.take(); - handle(pair); - } catch (Throwable e) { - Loggers.DISTRO.error("[NACOS-DISTRO] Error while handling notifying task", e); - } - } - } - - private void handle(Pair pair) { - try { - String datumKey = pair.getValue0(); - DataOperation action = pair.getValue1(); - - services.remove(datumKey); - - int count = 0; - - if (!listeners.containsKey(datumKey)) { - return; - } - - for (RecordListener listener : listeners.get(datumKey)) { - - count++; - - try { - if (action == DataOperation.CHANGE) { - listener.onChange(datumKey, dataStore.get(datumKey).value); - continue; - } - - if (action == DataOperation.DELETE) { - listener.onDelete(datumKey); - continue; - } - } catch (Throwable e) { - Loggers.DISTRO.error("[NACOS-DISTRO] error while notifying listener of key: {}", datumKey, e); - } - } - - if (Loggers.DISTRO.isDebugEnabled()) { - Loggers.DISTRO - .debug("[NACOS-DISTRO] datum change notified, key: {}, listener count: {}, action: {}", - datumKey, count, action.name()); - } - } catch (Throwable e) { - Loggers.DISTRO.error("[NACOS-DISTRO] Error while handling notifying task", e); - } - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DistroHttpData.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DistroHttpData.java deleted file mode 100644 index 314770313..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DistroHttpData.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.nacos.naming.consistency.ephemeral.distro; - -import com.alibaba.nacos.core.distributed.distro.entity.DistroData; -import com.alibaba.nacos.core.distributed.distro.entity.DistroKey; - -/** - * Distro http received data. - * - *

- * Apply for old distro http api. The data content has been deserialize by spring mvc, so there is no need to - * deserialize again. - *

- * - * @author xiweng.yy - */ -public class DistroHttpData extends DistroData { - - private Object deserializedContent; - - public DistroHttpData(DistroKey distroKey, Object deserializedContent) { - setDistroKey(distroKey); - this.deserializedContent = deserializedContent; - } - - public Object getDeserializedContent() { - return deserializedContent; - } - - public void setDeserializedContent(Object deserializedContent) { - this.deserializedContent = deserializedContent; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DistroHttpRegistry.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DistroHttpRegistry.java deleted file mode 100644 index abbc805d2..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DistroHttpRegistry.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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.nacos.naming.consistency.ephemeral.distro; - -import com.alibaba.nacos.core.cluster.ServerMemberManager; -import com.alibaba.nacos.core.distributed.distro.component.DistroComponentHolder; -import com.alibaba.nacos.core.distributed.distro.task.DistroTaskEngineHolder; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.consistency.ephemeral.distro.combined.DistroHttpCombinedKeyTaskFailedHandler; -import com.alibaba.nacos.naming.consistency.ephemeral.distro.combined.DistroHttpDelayTaskProcessor; -import com.alibaba.nacos.naming.consistency.ephemeral.distro.component.DistroDataStorageImpl; -import com.alibaba.nacos.naming.consistency.ephemeral.distro.component.DistroHttpAgent; -import com.alibaba.nacos.naming.core.DistroMapper; -import com.alibaba.nacos.naming.misc.GlobalConfig; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; - -/** - * Distro http registry. - * - * @author xiweng.yy - */ -@Component -public class DistroHttpRegistry { - - private final DistroComponentHolder componentHolder; - - private final DistroTaskEngineHolder taskEngineHolder; - - private final DataStore dataStore; - - private final DistroMapper distroMapper; - - private final GlobalConfig globalConfig; - - private final DistroConsistencyServiceImpl consistencyService; - - private final ServerMemberManager memberManager; - - public DistroHttpRegistry(DistroComponentHolder componentHolder, DistroTaskEngineHolder taskEngineHolder, - DataStore dataStore, DistroMapper distroMapper, GlobalConfig globalConfig, - DistroConsistencyServiceImpl consistencyService, ServerMemberManager memberManager) { - this.componentHolder = componentHolder; - this.taskEngineHolder = taskEngineHolder; - this.dataStore = dataStore; - this.distroMapper = distroMapper; - this.globalConfig = globalConfig; - this.consistencyService = consistencyService; - this.memberManager = memberManager; - } - - /** - * Register necessary component to distro protocol for HTTP implement. - */ - @PostConstruct - public void doRegister() { - componentHolder.registerDataStorage(KeyBuilder.INSTANCE_LIST_KEY_PREFIX, - new DistroDataStorageImpl(dataStore, distroMapper)); - componentHolder.registerTransportAgent(KeyBuilder.INSTANCE_LIST_KEY_PREFIX, new DistroHttpAgent(memberManager)); - componentHolder.registerFailedTaskHandler(KeyBuilder.INSTANCE_LIST_KEY_PREFIX, - new DistroHttpCombinedKeyTaskFailedHandler(taskEngineHolder)); - taskEngineHolder.registerNacosTaskProcessor(KeyBuilder.INSTANCE_LIST_KEY_PREFIX, - new DistroHttpDelayTaskProcessor(globalConfig, taskEngineHolder)); - componentHolder.registerDataProcessor(consistencyService); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpCombinedKey.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpCombinedKey.java deleted file mode 100644 index a947e0362..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpCombinedKey.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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.nacos.naming.consistency.ephemeral.distro.combined; - -import com.alibaba.nacos.core.distributed.distro.entity.DistroKey; - -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicLong; - -/** - * Distro http key. - * - * @author xiweng.yy - */ -public class DistroHttpCombinedKey extends DistroKey { - - private static final AtomicLong SEQUENCE = new AtomicLong(0); - - private final List actualResourceTypes = new LinkedList<>(); - - public DistroHttpCombinedKey(String resourceType, String targetServer) { - super(DistroHttpCombinedKey.getSequenceKey(), resourceType, targetServer); - } - - public List getActualResourceTypes() { - return actualResourceTypes; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof DistroHttpCombinedKey)) { - return false; - } - if (!super.equals(o)) { - return false; - } - DistroHttpCombinedKey that = (DistroHttpCombinedKey) o; - return Objects.equals(getResourceKey(), that.getResourceKey()) - && Objects.equals(getResourceType(), that.getResourceType()) - && Objects.equals(getTargetServer(), that.getTargetServer()) - && Objects.equals(actualResourceTypes, that.actualResourceTypes); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), actualResourceTypes); - } - - @Override - public String toString() { - return getResourceKey() + "{" + "actualResourceTypes=" + actualResourceTypes + "} to " + getTargetServer(); - } - - public static String getSequenceKey() { - return DistroHttpCombinedKey.class.getSimpleName() + "-" + SEQUENCE.get(); - } - - public static void incrementSequence() { - SEQUENCE.incrementAndGet(); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpCombinedKeyDelayTask.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpCombinedKeyDelayTask.java deleted file mode 100644 index 5e13e1643..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpCombinedKeyDelayTask.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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.nacos.naming.consistency.ephemeral.distro.combined; - -import com.alibaba.nacos.common.task.AbstractDelayTask; -import com.alibaba.nacos.consistency.DataOperation; -import com.alibaba.nacos.core.distributed.distro.entity.DistroKey; -import com.alibaba.nacos.core.distributed.distro.task.delay.DistroDelayTask; -import com.alibaba.nacos.naming.consistency.KeyBuilder; - -import java.util.HashSet; -import java.util.Set; - -/** - * Distro combined multi keys delay task for http. - * - * @author xiweng.yy - */ -public class DistroHttpCombinedKeyDelayTask extends DistroDelayTask { - - private final int batchSize; - - private final Set actualResourceKeys = new HashSet<>(); - - public DistroHttpCombinedKeyDelayTask(DistroKey distroKey, DataOperation action, long delayTime, int batchSize) { - super(distroKey, action, delayTime); - this.batchSize = batchSize; - } - - public Set getActualResourceKeys() { - return actualResourceKeys; - } - - @Override - public void merge(AbstractDelayTask task) { - actualResourceKeys.addAll(((DistroHttpCombinedKeyDelayTask) task).getActualResourceKeys()); - if (actualResourceKeys.size() >= batchSize) { - DistroHttpCombinedKey.incrementSequence(); - setLastProcessTime(0); - } else { - setLastProcessTime(task.getLastProcessTime()); - } - } - - @Override - public DistroKey getDistroKey() { - DistroKey taskKey = super.getDistroKey(); - DistroHttpCombinedKey result = new DistroHttpCombinedKey(KeyBuilder.INSTANCE_LIST_KEY_PREFIX, - taskKey.getTargetServer()); - result.setResourceKey(taskKey.getResourceKey()); - result.getActualResourceTypes().addAll(actualResourceKeys); - return result; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpCombinedKeyExecuteTask.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpCombinedKeyExecuteTask.java deleted file mode 100644 index b41cda659..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpCombinedKeyExecuteTask.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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.nacos.naming.consistency.ephemeral.distro.combined; - -import com.alibaba.nacos.common.task.AbstractExecuteTask; -import com.alibaba.nacos.consistency.DataOperation; -import com.alibaba.nacos.core.distributed.distro.DistroConfig; -import com.alibaba.nacos.core.distributed.distro.entity.DistroKey; -import com.alibaba.nacos.core.distributed.distro.task.delay.DistroDelayTaskExecuteEngine; -import com.alibaba.nacos.naming.misc.GlobalConfig; -import com.alibaba.nacos.naming.misc.Loggers; - -/** - * Distro http combined key execute task. - * - *

- * In this task, it will generate combined key delay task and add back to delay engine. - *

- * - * @author xiweng.yy - */ -public class DistroHttpCombinedKeyExecuteTask extends AbstractExecuteTask { - - private final GlobalConfig globalConfig; - - private final DistroDelayTaskExecuteEngine distroDelayTaskExecuteEngine; - - private final DistroKey singleDistroKey; - - private final DataOperation taskAction; - - public DistroHttpCombinedKeyExecuteTask(GlobalConfig globalConfig, - DistroDelayTaskExecuteEngine distroDelayTaskExecuteEngine, DistroKey singleDistroKey, - DataOperation taskAction) { - this.globalConfig = globalConfig; - this.distroDelayTaskExecuteEngine = distroDelayTaskExecuteEngine; - this.singleDistroKey = singleDistroKey; - this.taskAction = taskAction; - } - - @Override - public void run() { - try { - DistroKey newKey = new DistroKey(DistroHttpCombinedKey.getSequenceKey(), - DistroHttpCombinedKeyDelayTask.class.getSimpleName(), singleDistroKey.getTargetServer()); - DistroHttpCombinedKeyDelayTask combinedTask = new DistroHttpCombinedKeyDelayTask(newKey, taskAction, - DistroConfig.getInstance().getSyncDelayMillis(), globalConfig.getBatchSyncKeyCount()); - combinedTask.getActualResourceKeys().add(singleDistroKey.getResourceKey()); - distroDelayTaskExecuteEngine.addTask(newKey, combinedTask); - } catch (Exception e) { - Loggers.DISTRO.error("[DISTRO-FAILED] Combined key for http failed. ", e); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpCombinedKeyTaskFailedHandler.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpCombinedKeyTaskFailedHandler.java deleted file mode 100644 index c1019fd6c..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpCombinedKeyTaskFailedHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.nacos.naming.consistency.ephemeral.distro.combined; - -import com.alibaba.nacos.consistency.DataOperation; -import com.alibaba.nacos.core.distributed.distro.DistroConfig; -import com.alibaba.nacos.core.distributed.distro.component.DistroFailedTaskHandler; -import com.alibaba.nacos.core.distributed.distro.entity.DistroKey; -import com.alibaba.nacos.core.distributed.distro.task.DistroTaskEngineHolder; -import com.alibaba.nacos.core.distributed.distro.task.delay.DistroDelayTask; -import com.alibaba.nacos.naming.consistency.KeyBuilder; - -/** - * Distro combined key task failed handler. - * - * @author xiweng.yy - */ -public class DistroHttpCombinedKeyTaskFailedHandler implements DistroFailedTaskHandler { - - private final DistroTaskEngineHolder distroTaskEngineHolder; - - public DistroHttpCombinedKeyTaskFailedHandler(DistroTaskEngineHolder distroTaskEngineHolder) { - this.distroTaskEngineHolder = distroTaskEngineHolder; - } - - @Override - public void retry(DistroKey distroKey, DataOperation action) { - DistroHttpCombinedKey combinedKey = (DistroHttpCombinedKey) distroKey; - for (String each : combinedKey.getActualResourceTypes()) { - DistroKey newKey = new DistroKey(each, KeyBuilder.INSTANCE_LIST_KEY_PREFIX, distroKey.getTargetServer()); - DistroDelayTask newTask = new DistroDelayTask(newKey, action, - DistroConfig.getInstance().getSyncRetryDelayMillis()); - distroTaskEngineHolder.getDelayTaskExecuteEngine().addTask(newKey, newTask); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpDelayTaskProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpDelayTaskProcessor.java deleted file mode 100644 index d4bb5b57d..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/combined/DistroHttpDelayTaskProcessor.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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.nacos.naming.consistency.ephemeral.distro.combined; - -import com.alibaba.nacos.common.task.NacosTask; -import com.alibaba.nacos.common.task.NacosTaskProcessor; -import com.alibaba.nacos.core.distributed.distro.entity.DistroKey; -import com.alibaba.nacos.core.distributed.distro.task.DistroTaskEngineHolder; -import com.alibaba.nacos.core.distributed.distro.task.delay.DistroDelayTask; -import com.alibaba.nacos.naming.misc.GlobalConfig; - -/** - * Distro http task processor. - * - *

- * The reason of create this delay task execute engine is that HTTP will establish and close tcp connection frequently, - * so that cost more memory and cpu. What's more, there may be much 'TIME_WAIT' status tcp connection when service - * change frequently. - *

- * - *

- * For naming usage, the only task is the ephemeral instances change. - *

- * - * @author xiweng.yy - */ -public class DistroHttpDelayTaskProcessor implements NacosTaskProcessor { - - private final GlobalConfig globalConfig; - - private final DistroTaskEngineHolder distroTaskEngineHolder; - - public DistroHttpDelayTaskProcessor(GlobalConfig globalConfig, DistroTaskEngineHolder distroTaskEngineHolder) { - this.globalConfig = globalConfig; - this.distroTaskEngineHolder = distroTaskEngineHolder; - } - - @Override - public boolean process(NacosTask task) { - DistroDelayTask distroDelayTask = (DistroDelayTask) task; - DistroKey distroKey = distroDelayTask.getDistroKey(); - DistroHttpCombinedKeyExecuteTask executeTask = new DistroHttpCombinedKeyExecuteTask(globalConfig, - distroTaskEngineHolder.getDelayTaskExecuteEngine(), distroKey, distroDelayTask.getAction()); - distroTaskEngineHolder.getExecuteWorkersManager().addTask(distroKey, executeTask); - return true; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/component/DistroDataStorageImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/component/DistroDataStorageImpl.java deleted file mode 100644 index 808817f77..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/component/DistroDataStorageImpl.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.nacos.naming.consistency.ephemeral.distro.component; - -import com.alibaba.nacos.consistency.DataOperation; -import com.alibaba.nacos.core.distributed.distro.component.DistroDataStorage; -import com.alibaba.nacos.core.distributed.distro.entity.DistroData; -import com.alibaba.nacos.core.distributed.distro.entity.DistroKey; -import com.alibaba.nacos.naming.cluster.transport.Serializer; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.consistency.ephemeral.distro.DataStore; -import com.alibaba.nacos.naming.consistency.ephemeral.distro.combined.DistroHttpCombinedKey; -import com.alibaba.nacos.naming.core.DistroMapper; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import com.alibaba.nacos.sys.utils.ApplicationUtils; - -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Distro data storage impl. - * - * @author xiweng.yy - */ -public class DistroDataStorageImpl implements DistroDataStorage { - - private final DataStore dataStore; - - private final DistroMapper distroMapper; - - private volatile boolean isFinishInitial; - - public DistroDataStorageImpl(DataStore dataStore, DistroMapper distroMapper) { - this.dataStore = dataStore; - this.distroMapper = distroMapper; - } - - @Override - public void finishInitial() { - isFinishInitial = true; - } - - @Override - public boolean isFinishInitial() { - return isFinishInitial; - } - - @Override - public DistroData getDistroData(DistroKey distroKey) { - Map result = new HashMap<>(2); - if (distroKey instanceof DistroHttpCombinedKey) { - result = dataStore.batchGet(((DistroHttpCombinedKey) distroKey).getActualResourceTypes()); - } else { - Datum datum = dataStore.get(distroKey.getResourceKey()); - result.put(distroKey.getResourceKey(), datum); - } - byte[] dataContent = ApplicationUtils.getBean(Serializer.class).serialize(result); - return new DistroData(distroKey, dataContent); - } - - @Override - public DistroData getDatumSnapshot() { - Map result = dataStore.getDataMap(); - byte[] dataContent = ApplicationUtils.getBean(Serializer.class).serialize(result); - DistroKey distroKey = new DistroKey(KeyBuilder.RESOURCE_KEY_SNAPSHOT, KeyBuilder.INSTANCE_LIST_KEY_PREFIX); - return new DistroData(distroKey, dataContent); - } - - @Override - public List getVerifyData() { - // If upgrade to 2.0.X, do not verify for v1. - if (ApplicationUtils.getBean(UpgradeJudgement.class).isUseGrpcFeatures()) { - return Collections.emptyList(); - } - Map keyChecksums = new HashMap<>(64); - for (String key : dataStore.keys()) { - if (!distroMapper.responsible(KeyBuilder.getServiceName(key))) { - continue; - } - Datum datum = dataStore.get(key); - if (datum == null) { - continue; - } - keyChecksums.put(key, datum.value.getChecksum()); - } - if (keyChecksums.isEmpty()) { - return Collections.emptyList(); - } - DistroKey distroKey = new DistroKey(KeyBuilder.RESOURCE_KEY_CHECKSUM, KeyBuilder.INSTANCE_LIST_KEY_PREFIX); - DistroData data = new DistroData(distroKey, ApplicationUtils.getBean(Serializer.class).serialize(keyChecksums)); - data.setType(DataOperation.VERIFY); - return Collections.singletonList(data); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/component/DistroHttpAgent.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/component/DistroHttpAgent.java deleted file mode 100644 index 9ed017df4..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/component/DistroHttpAgent.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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.nacos.naming.consistency.ephemeral.distro.component; - -import com.alibaba.nacos.core.cluster.ServerMemberManager; -import com.alibaba.nacos.core.distributed.distro.component.DistroCallback; -import com.alibaba.nacos.core.distributed.distro.component.DistroTransportAgent; -import com.alibaba.nacos.core.distributed.distro.entity.DistroData; -import com.alibaba.nacos.core.distributed.distro.entity.DistroKey; -import com.alibaba.nacos.core.distributed.distro.exception.DistroException; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.consistency.ephemeral.distro.combined.DistroHttpCombinedKey; -import com.alibaba.nacos.naming.misc.NamingProxy; - -import java.util.ArrayList; -import java.util.List; - -/** - * Distro http agent. - * - * @author xiweng.yy - */ -public class DistroHttpAgent implements DistroTransportAgent { - - private final ServerMemberManager memberManager; - - public DistroHttpAgent(ServerMemberManager memberManager) { - this.memberManager = memberManager; - } - - @Override - public boolean supportCallbackTransport() { - return false; - } - - @Override - public boolean syncData(DistroData data, String targetServer) { - if (!memberManager.hasMember(targetServer)) { - return true; - } - byte[] dataContent = data.getContent(); - return NamingProxy.syncData(dataContent, data.getDistroKey().getTargetServer()); - } - - @Override - public void syncData(DistroData data, String targetServer, DistroCallback callback) { - throw new UnsupportedOperationException("Http distro agent do not support this method"); - } - - @Override - public boolean syncVerifyData(DistroData verifyData, String targetServer) { - if (!memberManager.hasMember(targetServer)) { - return true; - } - NamingProxy.syncCheckSums(verifyData.getContent(), targetServer); - return true; - } - - @Override - public void syncVerifyData(DistroData verifyData, String targetServer, DistroCallback callback) { - throw new UnsupportedOperationException("Http distro agent do not support this method"); - } - - @Override - public DistroData getData(DistroKey key, String targetServer) { - try { - List toUpdateKeys = null; - if (key instanceof DistroHttpCombinedKey) { - toUpdateKeys = ((DistroHttpCombinedKey) key).getActualResourceTypes(); - } else { - toUpdateKeys = new ArrayList<>(1); - toUpdateKeys.add(key.getResourceKey()); - } - byte[] queriedData = NamingProxy.getData(toUpdateKeys, key.getTargetServer()); - return new DistroData(key, queriedData); - } catch (Exception e) { - throw new DistroException(String.format("Get data from %s failed.", key.getTargetServer()), e); - } - } - - @Override - public DistroData getDatumSnapshot(String targetServer) { - try { - byte[] allDatum = NamingProxy.getAllData(targetServer); - return new DistroData(new DistroKey(KeyBuilder.RESOURCE_KEY_SNAPSHOT, KeyBuilder.INSTANCE_LIST_KEY_PREFIX), allDatum); - } catch (Exception e) { - throw new DistroException(String.format("Get snapshot from %s failed.", targetServer), e); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/v2/DistroClientComponentRegistry.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/v2/DistroClientComponentRegistry.java index 1fd110ab9..d705af691 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/v2/DistroClientComponentRegistry.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/v2/DistroClientComponentRegistry.java @@ -24,7 +24,6 @@ import com.alibaba.nacos.core.distributed.distro.component.DistroTransportAgent; import com.alibaba.nacos.core.distributed.distro.task.DistroTaskEngineHolder; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; @@ -49,19 +48,15 @@ public class DistroClientComponentRegistry { private final ClusterRpcClientProxy clusterRpcClientProxy; - private final UpgradeJudgement upgradeJudgement; - public DistroClientComponentRegistry(ServerMemberManager serverMemberManager, DistroProtocol distroProtocol, DistroComponentHolder componentHolder, DistroTaskEngineHolder taskEngineHolder, - ClientManagerDelegate clientManager, ClusterRpcClientProxy clusterRpcClientProxy, - UpgradeJudgement upgradeJudgement) { + ClientManagerDelegate clientManager, ClusterRpcClientProxy clusterRpcClientProxy) { this.serverMemberManager = serverMemberManager; this.distroProtocol = distroProtocol; this.componentHolder = componentHolder; this.taskEngineHolder = taskEngineHolder; this.clientManager = clientManager; this.clusterRpcClientProxy = clusterRpcClientProxy; - this.upgradeJudgement = upgradeJudgement; } /** @@ -70,8 +65,7 @@ public class DistroClientComponentRegistry { */ @PostConstruct public void doRegister() { - DistroClientDataProcessor dataProcessor = new DistroClientDataProcessor(clientManager, distroProtocol, - upgradeJudgement); + DistroClientDataProcessor dataProcessor = new DistroClientDataProcessor(clientManager, distroProtocol); DistroTransportAgent transportAgent = new DistroClientTransportAgent(clusterRpcClientProxy, serverMemberManager); DistroClientTaskFailedHandler taskFailedHandler = new DistroClientTaskFailedHandler(taskEngineHolder); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/v2/DistroClientDataProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/v2/DistroClientDataProcessor.java index 980f42a44..eca5f7776 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/v2/DistroClientDataProcessor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/v2/DistroClientDataProcessor.java @@ -26,6 +26,7 @@ import com.alibaba.nacos.core.distributed.distro.component.DistroDataStorage; import com.alibaba.nacos.core.distributed.distro.entity.DistroData; import com.alibaba.nacos.core.distributed.distro.entity.DistroKey; import com.alibaba.nacos.naming.cluster.transport.Serializer; +import com.alibaba.nacos.naming.constants.ClientConstants; import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.ClientSyncData; @@ -38,7 +39,6 @@ import com.alibaba.nacos.naming.core.v2.pojo.BatchInstanceData; import com.alibaba.nacos.naming.core.v2.pojo.BatchInstancePublishInfo; import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.utils.ApplicationUtils; @@ -62,15 +62,11 @@ public class DistroClientDataProcessor extends SmartSubscriber implements Distro private final DistroProtocol distroProtocol; - private final UpgradeJudgement upgradeJudgement; - private volatile boolean isFinishInitial; - public DistroClientDataProcessor(ClientManager clientManager, DistroProtocol distroProtocol, - UpgradeJudgement upgradeJudgement) { + public DistroClientDataProcessor(ClientManager clientManager, DistroProtocol distroProtocol) { this.clientManager = clientManager; this.distroProtocol = distroProtocol; - this.upgradeJudgement = upgradeJudgement; NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance()); } @@ -98,9 +94,6 @@ public class DistroClientDataProcessor extends SmartSubscriber implements Distro if (EnvUtil.getStandaloneMode()) { return; } - if (!upgradeJudgement.isUseGrpcFeatures()) { - return; - } if (event instanceof ClientEvent.ClientVerifyFailedEvent) { syncToVerifyFailedServer((ClientEvent.ClientVerifyFailedEvent) event); } else { @@ -158,7 +151,9 @@ public class DistroClientDataProcessor extends SmartSubscriber implements Distro } private void handlerClientSyncData(ClientSyncData clientSyncData) { - Loggers.DISTRO.info("[Client-Add] Received distro client sync data {}", clientSyncData.getClientId()); + Loggers.DISTRO + .info("[Client-Add] Received distro client sync data {}, revision={}", clientSyncData.getClientId(), + clientSyncData.getAttributes().getClientAttribute(ClientConstants.REVISION, 0L)); clientManager.syncClientConnected(clientSyncData.getClientId(), clientSyncData.getAttributes()); Client client = clientManager.getClient(clientSyncData.getClientId()); upgradeClient(client, clientSyncData); @@ -193,23 +188,26 @@ public class DistroClientDataProcessor extends SmartSubscriber implements Distro } } - private static void processBatchInstanceDistroData(Set syncedService, Client client, ClientSyncData clientSyncData) { + private static void processBatchInstanceDistroData(Set syncedService, Client client, + ClientSyncData clientSyncData) { BatchInstanceData batchInstanceData = clientSyncData.getBatchInstanceData(); if (batchInstanceData == null || CollectionUtils.isEmpty(batchInstanceData.getNamespaces())) { - Loggers.DISTRO.info("[processBatchInstanceDistroData] BatchInstanceData is null , clientId is :{}", client.getClientId()); + Loggers.DISTRO.info("[processBatchInstanceDistroData] BatchInstanceData is null , clientId is :{}", + client.getClientId()); return; } List namespaces = batchInstanceData.getNamespaces(); List groupNames = batchInstanceData.getGroupNames(); List serviceNames = batchInstanceData.getServiceNames(); List batchInstancePublishInfos = batchInstanceData.getBatchInstancePublishInfos(); - + for (int i = 0; i < namespaces.size(); i++) { Service service = Service.newService(namespaces.get(i), groupNames.get(i), serviceNames.get(i)); Service singleton = ServiceManager.getInstance().getSingleton(service); syncedService.add(singleton); BatchInstancePublishInfo batchInstancePublishInfo = batchInstancePublishInfos.get(i); - BatchInstancePublishInfo targetInstanceInfo = (BatchInstancePublishInfo) client.getInstancePublishInfo(singleton); + BatchInstancePublishInfo targetInstanceInfo = (BatchInstancePublishInfo) client + .getInstancePublishInfo(singleton); boolean result = false; if (targetInstanceInfo != null) { result = batchInstancePublishInfo.equals(targetInstanceInfo); @@ -220,13 +218,14 @@ public class DistroClientDataProcessor extends SmartSubscriber implements Distro new ClientOperationEvent.ClientRegisterServiceEvent(singleton, client.getClientId())); } } + client.setRevision(clientSyncData.getAttributes().getClientAttribute(ClientConstants.REVISION, 0)); } @Override public boolean processVerifyData(DistroData distroData, String sourceAddress) { DistroClientVerifyInfo verifyData = ApplicationUtils.getBean(Serializer.class) .deserialize(distroData.getContent(), DistroClientVerifyInfo.class); - if (clientManager.verifyClient(verifyData.getClientId())) { + if (clientManager.verifyClient(verifyData)) { return true; } Loggers.DISTRO.info("client {} is invalid, get new client from {}", verifyData.getClientId(), sourceAddress); @@ -271,19 +270,22 @@ public class DistroClientDataProcessor extends SmartSubscriber implements Distro @Override public List getVerifyData() { - List result = new LinkedList<>(); + List result = null; for (String each : clientManager.allClientId()) { Client client = clientManager.getClient(each); if (null == client || !client.isEphemeral()) { continue; } if (clientManager.isResponsibleClient(client)) { - // TODO add revision for client. - DistroClientVerifyInfo verifyData = new DistroClientVerifyInfo(client.getClientId(), 0); + DistroClientVerifyInfo verifyData = new DistroClientVerifyInfo(client.getClientId(), + client.getRevision()); DistroKey distroKey = new DistroKey(client.getClientId(), TYPE); DistroData data = new DistroData(distroKey, ApplicationUtils.getBean(Serializer.class).serialize(verifyData)); data.setType(DataOperation.VERIFY); + if (result == null) { + result = new LinkedList<>(); + } result.add(data); } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/ClusterVersionJudgement.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/ClusterVersionJudgement.java deleted file mode 100644 index d068ed733..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/ClusterVersionJudgement.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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.nacos.naming.consistency.persistent; - -import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.common.utils.VersionUtils; -import com.alibaba.nacos.core.cluster.Member; -import com.alibaba.nacos.core.cluster.MemberMetaDataConstants; -import com.alibaba.nacos.core.cluster.ServerMemberManager; -import com.alibaba.nacos.naming.misc.GlobalExecutor; -import com.alibaba.nacos.sys.env.EnvUtil; -import org.springframework.stereotype.Component; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -/** - * An automated task that determines whether all nodes in the current cluster meet the requirements of a particular - * version. - * - *

This will be removed in a future release, just to smooth the transition. - * - * @author liaochuntao - */ -@Component -public class ClusterVersionJudgement { - - private volatile boolean allMemberIsNewVersion = false; - - private final ServerMemberManager memberManager; - - private final List observers = new CopyOnWriteArrayList<>(); - - public ClusterVersionJudgement(ServerMemberManager memberManager) { - this.memberManager = memberManager; - GlobalExecutor.submitClusterVersionJudge(this::runVersionListener, TimeUnit.SECONDS.toMillis(5)); - } - - /** - * register member version watcher. - * - * @param observer Listens for the latest version of all current nodes - * @param priority The higher the priority, the first to be notified - */ - public void registerObserver(Consumer observer, int priority) { - observers.add(new ConsumerWithPriority(observer, priority)); - } - - protected void runVersionListener() { - // Single machine mode or close upgrade feature, do upgrade operation directly. - if (EnvUtil.getStandaloneMode() || !EnvUtil.isSupportUpgradeFrom1X()) { - notifyAllListener(); - return; - } - boolean finish = false; - try { - finish = judge(); - } finally { - if (!finish) { - GlobalExecutor.submitClusterVersionJudge(this::runVersionListener, TimeUnit.SECONDS.toMillis(5)); - } - } - } - - protected boolean judge() { - boolean finish = false; - Collection members = memberManager.allMembers(); - final String oldVersion = "1.4.0"; - boolean allMemberIsNewVersion = true; - for (Member member : members) { - final String curV = (String) member.getExtendVal(MemberMetaDataConstants.VERSION); - if (StringUtils.isBlank(curV) || VersionUtils.compareVersion(oldVersion, curV) > 0) { - allMemberIsNewVersion = false; - } - } - // can only trigger once - if (allMemberIsNewVersion && !this.allMemberIsNewVersion) { - notifyAllListener(); - finish = true; - } - return finish; - } - - private void notifyAllListener() { - this.allMemberIsNewVersion = true; - Collections.sort(observers); - for (ConsumerWithPriority consumer : observers) { - consumer.consumer.accept(true); - } - } - - public boolean allMemberIsNewVersion() { - return allMemberIsNewVersion; - } - - /** - * Only used for upgrade to 2.0.0 - */ - public void reset() { - allMemberIsNewVersion = false; - } - - private static class ConsumerWithPriority implements Comparable { - - private final Consumer consumer; - - private final int priority; - - public ConsumerWithPriority(Consumer consumer, int priority) { - this.consumer = consumer; - this.priority = priority; - } - - @Override - public int compareTo(ConsumerWithPriority o) { - return o.priority - this.priority; - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/PersistentConsistencyServiceDelegateImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/PersistentConsistencyServiceDelegateImpl.java index 64a6e0321..55c26613b 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/PersistentConsistencyServiceDelegateImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/PersistentConsistencyServiceDelegateImpl.java @@ -23,9 +23,9 @@ import com.alibaba.nacos.naming.consistency.RecordListener; import com.alibaba.nacos.naming.consistency.persistent.impl.BasePersistentServiceProcessor; import com.alibaba.nacos.naming.consistency.persistent.impl.PersistentServiceProcessor; import com.alibaba.nacos.naming.consistency.persistent.impl.StandalonePersistentServiceProcessor; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftConsistencyServiceImpl; import com.alibaba.nacos.naming.pojo.Record; import com.alibaba.nacos.sys.env.EnvUtil; +import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; import java.util.Optional; @@ -35,80 +35,56 @@ import java.util.Optional; * * @author xiweng.yy */ +@DependsOn("ProtocolManager") @Component("persistentConsistencyServiceDelegate") public class PersistentConsistencyServiceDelegateImpl implements PersistentConsistencyService { - private final ClusterVersionJudgement versionJudgement; + private final BasePersistentServiceProcessor persistentServiceProcessor; - private final RaftConsistencyServiceImpl oldPersistentConsistencyService; - - private final BasePersistentServiceProcessor newPersistentConsistencyService; - - private volatile boolean switchNewPersistentService = false; - - public PersistentConsistencyServiceDelegateImpl(ClusterVersionJudgement versionJudgement, - RaftConsistencyServiceImpl oldPersistentConsistencyService, ProtocolManager protocolManager) - throws Exception { - this.versionJudgement = versionJudgement; - this.oldPersistentConsistencyService = oldPersistentConsistencyService; - this.newPersistentConsistencyService = createNewPersistentServiceProcessor(protocolManager, versionJudgement); - init(); - } - - private void init() { - if (EnvUtil.isSupportUpgradeFrom1X()) { - this.versionJudgement.registerObserver(isAllNewVersion -> switchNewPersistentService = isAllNewVersion, -1); - return; - } - this.switchNewPersistentService = true; + public PersistentConsistencyServiceDelegateImpl(ProtocolManager protocolManager) throws Exception { + this.persistentServiceProcessor = createPersistentServiceProcessor(protocolManager); } @Override public void put(String key, Record value) throws NacosException { - switchOne().put(key, value); + persistentServiceProcessor.put(key, value); } @Override public void remove(String key) throws NacosException { - switchOne().remove(key); + persistentServiceProcessor.remove(key); } @Override public Datum get(String key) throws NacosException { - return switchOne().get(key); + return persistentServiceProcessor.get(key); } @Override public void listen(String key, RecordListener listener) throws NacosException { - oldPersistentConsistencyService.listen(key, listener); - newPersistentConsistencyService.listen(key, listener); + persistentServiceProcessor.listen(key, listener); } @Override public void unListen(String key, RecordListener listener) throws NacosException { - newPersistentConsistencyService.unListen(key, listener); - oldPersistentConsistencyService.unListen(key, listener); + persistentServiceProcessor.unListen(key, listener); } @Override public boolean isAvailable() { - return switchOne().isAvailable(); + return persistentServiceProcessor.isAvailable(); } @Override public Optional getErrorMsg() { - return switchOne().getErrorMsg(); + return persistentServiceProcessor.getErrorMsg(); } - private PersistentConsistencyService switchOne() { - return switchNewPersistentService ? newPersistentConsistencyService : oldPersistentConsistencyService; - } - - private BasePersistentServiceProcessor createNewPersistentServiceProcessor(ProtocolManager protocolManager, - ClusterVersionJudgement versionJudgement) throws Exception { + private BasePersistentServiceProcessor createPersistentServiceProcessor(ProtocolManager protocolManager) + throws Exception { final BasePersistentServiceProcessor processor = - EnvUtil.getStandaloneMode() ? new StandalonePersistentServiceProcessor(versionJudgement) - : new PersistentServiceProcessor(protocolManager, versionJudgement); + EnvUtil.getStandaloneMode() ? new StandalonePersistentServiceProcessor() + : new PersistentServiceProcessor(protocolManager); processor.afterConstruct(); return processor; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/PersistentNotifier.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/PersistentNotifier.java index cdf005383..15750b314 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/PersistentNotifier.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/PersistentNotifier.java @@ -20,7 +20,6 @@ import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacos.consistency.DataOperation; -import com.alibaba.nacos.naming.consistency.KeyBuilder; import com.alibaba.nacos.naming.consistency.RecordListener; import com.alibaba.nacos.naming.consistency.ValueChangeEvent; import com.alibaba.nacos.naming.misc.Loggers; @@ -91,22 +90,6 @@ public final class PersistentNotifier extends Subscriber { * @param type */ public void notify(final String key, final DataOperation action, final T value) { - if (listenerMap.containsKey(KeyBuilder.SERVICE_META_KEY_PREFIX)) { - if (KeyBuilder.matchServiceMetaKey(key) && !KeyBuilder.matchSwitchKey(key)) { - for (RecordListener listener : listenerMap.get(KeyBuilder.SERVICE_META_KEY_PREFIX)) { - try { - if (action == DataOperation.CHANGE) { - listener.onChange(key, value); - } - if (action == DataOperation.DELETE) { - listener.onDelete(key); - } - } catch (Throwable e) { - Loggers.RAFT.error("[NACOS-RAFT] error while notifying listener of key: {}", key, e); - } - } - } - } if (!listenerMap.containsKey(key)) { return; diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/BasePersistentServiceProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/BasePersistentServiceProcessor.java index 7a193261e..e8da482f9 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/BasePersistentServiceProcessor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/BasePersistentServiceProcessor.java @@ -20,6 +20,7 @@ import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.ByteUtils; +import com.alibaba.nacos.common.utils.TypeUtils; import com.alibaba.nacos.consistency.DataOperation; import com.alibaba.nacos.consistency.SerializeFactory; import com.alibaba.nacos.consistency.Serializer; @@ -34,15 +35,13 @@ import com.alibaba.nacos.naming.consistency.Datum; import com.alibaba.nacos.naming.consistency.KeyBuilder; import com.alibaba.nacos.naming.consistency.RecordListener; import com.alibaba.nacos.naming.consistency.ValueChangeEvent; -import com.alibaba.nacos.naming.consistency.persistent.ClusterVersionJudgement; import com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyService; import com.alibaba.nacos.naming.consistency.persistent.PersistentNotifier; +import com.alibaba.nacos.naming.constants.Constants; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.alibaba.nacos.naming.pojo.Record; -import com.alibaba.nacos.naming.constants.Constants; import com.google.protobuf.ByteString; -import com.alibaba.nacos.common.utils.TypeUtils; import java.lang.reflect.Type; import java.nio.file.Paths; @@ -106,16 +105,13 @@ public abstract class BasePersistentServiceProcessor extends RequestProcessor4CP protected final ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); - protected final ClusterVersionJudgement versionJudgement; - protected final PersistentNotifier notifier; protected final int queueMaxSize = 16384; protected final int priority = 10; - public BasePersistentServiceProcessor(final ClusterVersionJudgement judgement) throws Exception { - this.versionJudgement = judgement; + public BasePersistentServiceProcessor() throws Exception { this.kvStorage = new NamingKvStorage(Paths.get(UtilsAndCommons.DATA_BASE_DIR, "data").toString()); this.serializer = SerializeFactory.getSerializer("JSON"); this.notifier = new PersistentNotifier(key -> { @@ -132,16 +128,6 @@ public abstract class BasePersistentServiceProcessor extends RequestProcessor4CP @SuppressWarnings("unchecked") public void afterConstruct() { NotifyCenter.registerToPublisher(ValueChangeEvent.class, queueMaxSize); - listenOldRaftClose(); - } - - private void listenOldRaftClose() { - this.versionJudgement.registerObserver(isNewVersion -> { - if (isNewVersion) { - NotifyCenter.registerSubscriber(notifier); - startNotify = true; - } - }, priority); } @Override @@ -195,6 +181,10 @@ public abstract class BasePersistentServiceProcessor extends RequestProcessor4CP final List values = request.getValues(); for (int i = 0; i < keys.size(); i++) { final String key = new String(keys.get(i)); + // Ignore old 1.x version data + if (!KeyBuilder.matchSwitchKey(key)) { + continue; + } final Datum datum = serializer.deserialize(values.get(i), getDatumTypeFromKey(key)); final Record value = null != datum ? datum.value : null; final ValueChangeEvent event = ValueChangeEvent.builder().key(key).value(value) @@ -227,10 +217,6 @@ public abstract class BasePersistentServiceProcessor extends RequestProcessor4CP protected Class getClassOfRecordFromKey(String key) { if (KeyBuilder.matchSwitchKey(key)) { return com.alibaba.nacos.naming.misc.SwitchDomain.class; - } else if (KeyBuilder.matchServiceMetaKey(key)) { - return com.alibaba.nacos.naming.core.Service.class; - } else if (KeyBuilder.matchInstanceListKey(key)) { - return com.alibaba.nacos.naming.core.Instances.class; } return Record.class; } @@ -247,7 +233,7 @@ public abstract class BasePersistentServiceProcessor extends RequestProcessor4CP } /** - * This notify should only notify once during startup. See {@link com.alibaba.nacos.naming.core.ServiceManager#init()} + * This notify should only notify once during startup. */ private void notifierAllServiceMeta(RecordListener listener) throws NacosException { for (byte[] each : kvStorage.allKeys()) { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/NamingKvStorage.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/NamingKvStorage.java index c368f5463..ec82e20b4 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/NamingKvStorage.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/NamingKvStorage.java @@ -16,27 +16,17 @@ package com.alibaba.nacos.naming.consistency.persistent.impl; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; -import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.exception.ErrorCode; import com.alibaba.nacos.core.exception.KvStorageException; import com.alibaba.nacos.core.storage.StorageFactory; import com.alibaba.nacos.core.storage.kv.KvStorage; import com.alibaba.nacos.core.storage.kv.MemoryKvStorage; import com.alibaba.nacos.core.utils.TimerContext; -import com.alibaba.nacos.naming.consistency.KeyBuilder; import com.alibaba.nacos.naming.misc.Loggers; -import java.io.File; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; /** * Kv storage implementation for naming. @@ -53,12 +43,9 @@ public class NamingKvStorage extends MemoryKvStorage { private final KvStorage baseDirStorage; - private final Map namespaceKvStorage; - public NamingKvStorage(final String baseDir) throws Exception { this.baseDir = baseDir; this.baseDirStorage = StorageFactory.createKvStorage(KvStorage.KvType.File, LABEL, baseDir); - this.namespaceKvStorage = new ConcurrentHashMap<>(16); } @Override @@ -67,7 +54,7 @@ public class NamingKvStorage extends MemoryKvStorage { byte[] result = super.get(key); if (null == result) { try { - KvStorage storage = createActualStorageIfAbsent(key); + KvStorage storage = getStorage(); result = null == storage ? null : storage.get(key); if (null != result) { super.put(key, result); @@ -95,7 +82,7 @@ public class NamingKvStorage extends MemoryKvStorage { @Override public void put(byte[] key, byte[] value) throws KvStorageException { try { - KvStorage storage = createActualStorageIfAbsent(key); + KvStorage storage = getStorage(); storage.put(key, value); } catch (Exception e) { throw new KvStorageException(ErrorCode.KVStorageWriteError.getCode(), @@ -120,7 +107,7 @@ public class NamingKvStorage extends MemoryKvStorage { @Override public void delete(byte[] key) throws KvStorageException { try { - KvStorage storage = createActualStorageIfAbsent(key); + KvStorage storage = getStorage(); if (null != storage) { storage.delete(key); } @@ -150,7 +137,6 @@ public class NamingKvStorage extends MemoryKvStorage { try { baseDirStorage.snapshotLoad(path); loadSnapshotFromActualStorage(baseDirStorage); - loadNamespaceSnapshot(); } finally { TimerContext.end(LOAD_SNAPSHOT, Loggers.RAFT); } @@ -163,31 +149,6 @@ public class NamingKvStorage extends MemoryKvStorage { } } - private void loadNamespaceSnapshot() { - for (String each : getAllNamespaceDirs()) { - try { - KvStorage kvStorage = createActualStorageIfAbsent(each); - loadSnapshotFromActualStorage(kvStorage); - } catch (Exception e) { - Loggers.RAFT.error("load snapshot for namespace {} failed", each, e); - } - } - } - - private List getAllNamespaceDirs() { - File[] files = new File(baseDir).listFiles(); - List result = Collections.emptyList(); - if (null != files) { - result = new ArrayList<>(files.length); - for (File each : files) { - if (each.isDirectory()) { - result.add(each.getName()); - } - } - } - return Collections.unmodifiableList(result); - } - @Override public List allKeys() throws KvStorageException { return super.allKeys(); @@ -196,33 +157,10 @@ public class NamingKvStorage extends MemoryKvStorage { @Override public void shutdown() { baseDirStorage.shutdown(); - for (KvStorage each : namespaceKvStorage.values()) { - each.shutdown(); - } - namespaceKvStorage.clear(); super.shutdown(); } - private KvStorage createActualStorageIfAbsent(byte[] key) throws Exception { - String keyString = new String(key); - String namespace = KeyBuilder.getNamespace(keyString); - return createActualStorageIfAbsent(namespace); - } - - private KvStorage createActualStorageIfAbsent(String namespace) throws Exception { - if (StringUtils.isBlank(namespace)) { - return baseDirStorage; - } - - Function kvStorageBuilder = key -> { - try { - String namespacePath = Paths.get(baseDir, key).toString(); - return StorageFactory.createKvStorage(KvType.File, LABEL, namespacePath); - } catch (Exception e) { - throw new NacosRuntimeException(NacosException.SERVER_ERROR, e); - } - }; - namespaceKvStorage.computeIfAbsent(namespace, kvStorageBuilder); - return namespaceKvStorage.get(namespace); + private KvStorage getStorage() throws Exception { + return baseDirStorage; } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/PersistentServiceProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/PersistentServiceProcessor.java index db032d761..f8768506a 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/PersistentServiceProcessor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/PersistentServiceProcessor.java @@ -30,10 +30,9 @@ import com.alibaba.nacos.core.distributed.ProtocolManager; import com.alibaba.nacos.core.exception.ErrorCode; import com.alibaba.nacos.naming.consistency.Datum; import com.alibaba.nacos.naming.consistency.RecordListener; -import com.alibaba.nacos.naming.consistency.persistent.ClusterVersionJudgement; +import com.alibaba.nacos.naming.constants.Constants; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.pojo.Record; -import com.alibaba.nacos.naming.constants.Constants; import com.alibaba.nacos.sys.env.EnvUtil; import com.google.protobuf.ByteString; @@ -58,9 +57,7 @@ public class PersistentServiceProcessor extends BasePersistentServiceProcessor { */ private volatile boolean hasLeader = false; - public PersistentServiceProcessor(ProtocolManager protocolManager, ClusterVersionJudgement versionJudgement) - throws Exception { - super(versionJudgement); + public PersistentServiceProcessor(ProtocolManager protocolManager) throws Exception { this.protocol = protocolManager.getCpProtocol(); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/StandalonePersistentServiceProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/StandalonePersistentServiceProcessor.java index a070972d3..47af6ea0b 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/StandalonePersistentServiceProcessor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/impl/StandalonePersistentServiceProcessor.java @@ -24,9 +24,8 @@ import com.alibaba.nacos.consistency.entity.WriteRequest; import com.alibaba.nacos.core.exception.ErrorCode; import com.alibaba.nacos.naming.consistency.Datum; import com.alibaba.nacos.naming.consistency.RecordListener; -import com.alibaba.nacos.naming.consistency.persistent.ClusterVersionJudgement; -import com.alibaba.nacos.naming.pojo.Record; import com.alibaba.nacos.naming.constants.Constants; +import com.alibaba.nacos.naming.pojo.Record; import com.google.protobuf.ByteString; import java.util.Collections; @@ -41,8 +40,7 @@ import java.util.Optional; @SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule") public class StandalonePersistentServiceProcessor extends BasePersistentServiceProcessor { - public StandalonePersistentServiceProcessor(final ClusterVersionJudgement judgement) throws Exception { - super(judgement); + public StandalonePersistentServiceProcessor() throws Exception { } @Override diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/BaseRaftEvent.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/BaseRaftEvent.java deleted file mode 100644 index 84104801a..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/BaseRaftEvent.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.nacos.naming.consistency.persistent.raft; - -import org.springframework.context.ApplicationEvent; - -/** - * Base raft event. - * - * @deprecated will remove in 1.4.x - * @author pbting - * @date 2019-07-01 8:46 PM - */ -@Deprecated -public abstract class BaseRaftEvent extends ApplicationEvent { - - private final RaftPeer raftPeer; - - private final RaftPeer local; - - public BaseRaftEvent(Object source, RaftPeer raftPeer, RaftPeer local) { - super(source); - this.raftPeer = raftPeer; - this.local = local; - } - - public RaftPeer getRaftPeer() { - return raftPeer; - } - - public RaftPeer getLocal() { - return local; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/LeaderElectFinishedEvent.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/LeaderElectFinishedEvent.java deleted file mode 100644 index a97eec9e9..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/LeaderElectFinishedEvent.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.nacos.naming.consistency.persistent.raft; - -/** - * Leader election finished event. - * - * @deprecated will remove in 1.4.x - * @author pbting - * @date 2019-07-01 8:25 PM - */ -@Deprecated -public class LeaderElectFinishedEvent extends BaseRaftEvent { - - public LeaderElectFinishedEvent(Object source, RaftPeer raftPeer, RaftPeer local) { - super(source, raftPeer, local); - } - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/MakeLeaderEvent.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/MakeLeaderEvent.java deleted file mode 100644 index 9d69b3dab..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/MakeLeaderEvent.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.nacos.naming.consistency.persistent.raft; - -/** - * Make leader event. - * - * @deprecated will remove in 1.4.x - * @author pbting - * @date 2019-07-01 8:45 PM - */ -@Deprecated -public class MakeLeaderEvent extends BaseRaftEvent { - - public MakeLeaderEvent(Object source, RaftPeer raftPeer, RaftPeer local) { - super(source, raftPeer, local); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftConsistencyServiceImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftConsistencyServiceImpl.java deleted file mode 100644 index 7e068613b..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftConsistencyServiceImpl.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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.nacos.naming.consistency.persistent.raft; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; -import com.alibaba.nacos.naming.cluster.ServerStatus; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.consistency.RecordListener; -import com.alibaba.nacos.naming.consistency.persistent.ClusterVersionJudgement; -import com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyService; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.pojo.Record; -import com.alibaba.nacos.naming.constants.Constants; -import com.alibaba.nacos.sys.env.EnvUtil; -import org.springframework.context.annotation.DependsOn; -import org.springframework.stereotype.Service; - -import javax.annotation.PostConstruct; -import java.util.Optional; - -/** - * Use simplified Raft protocol to maintain the consistency status of Nacos cluster. - * - * @author nkorange - * @since 1.0.0 - * @deprecated will remove in 1.4.x - */ -@Deprecated -@DependsOn("ProtocolManager") -@Service -public class RaftConsistencyServiceImpl implements PersistentConsistencyService { - - private final RaftCore raftCore; - - private final RaftPeerSet peers; - - private final SwitchDomain switchDomain; - - private volatile boolean stopWork = false; - - private String errorMsg; - - public RaftConsistencyServiceImpl(ClusterVersionJudgement versionJudgement, RaftCore raftCore, - SwitchDomain switchDomain) { - this.raftCore = raftCore; - this.peers = raftCore.getPeerSet(); - this.switchDomain = switchDomain; - versionJudgement.registerObserver(isAllNewVersion -> { - stopWork = isAllNewVersion; - if (stopWork) { - try { - this.raftCore.shutdown(); - } catch (NacosException e) { - throw new NacosRuntimeException(NacosException.SERVER_ERROR, e); - } - } - }, 1000); - } - - @PostConstruct - protected void init() throws Exception { - if (EnvUtil.getProperty(Constants.NACOS_NAMING_USE_NEW_RAFT_FIRST, Boolean.class, false)) { - this.raftCore.shutdown(); - } - } - - @Override - public void put(String key, Record value) throws NacosException { - checkIsStopWork(); - try { - raftCore.signalPublish(key, value); - } catch (Exception e) { - Loggers.RAFT.error("Raft put failed.", e); - throw new NacosException(NacosException.SERVER_ERROR, "Raft put failed, key:" + key + ", value:" + value, - e); - } - } - - @Override - public void remove(String key) throws NacosException { - checkIsStopWork(); - try { - if (KeyBuilder.matchInstanceListKey(key) && !raftCore.isLeader()) { - raftCore.onDelete(key, peers.getLeader()); - } else { - raftCore.signalDelete(key); - } - raftCore.unListenAll(key); - } catch (Exception e) { - Loggers.RAFT.error("Raft remove failed.", e); - throw new NacosException(NacosException.SERVER_ERROR, "Raft remove failed, key:" + key, e); - } - } - - @Override - public Datum get(String key) throws NacosException { - checkIsStopWork(); - return raftCore.getDatum(key); - } - - @Override - public void listen(String key, RecordListener listener) throws NacosException { - raftCore.listen(key, listener); - } - - @Override - public void unListen(String key, RecordListener listener) throws NacosException { - raftCore.unListen(key, listener); - } - - @Override - public boolean isAvailable() { - return raftCore.isInitialized() || ServerStatus.UP.name().equals(switchDomain.getOverriddenServerStatus()); - } - - @Override - public Optional getErrorMsg() { - String errorMsg; - if (!raftCore.isInitialized() && !ServerStatus.UP.name().equals(switchDomain.getOverriddenServerStatus())) { - errorMsg = "The old raft protocol node is not initialized"; - } else { - errorMsg = null; - } - return Optional.ofNullable(errorMsg); - } - - /** - * Put a new datum from other server. - * - * @param datum datum - * @param source source server - * @throws NacosException nacos exception - */ - public void onPut(Datum datum, RaftPeer source) throws NacosException { - try { - raftCore.onPublish(datum, source); - } catch (Exception e) { - Loggers.RAFT.error("Raft onPut failed.", e); - throw new NacosException(NacosException.SERVER_ERROR, - "Raft onPut failed, datum:" + datum + ", source: " + source, e); - } - } - - /** - * Remove a new datum from other server. - * - * @param datum datum - * @param source source server - * @throws NacosException nacos exception - */ - public void onRemove(Datum datum, RaftPeer source) throws NacosException { - try { - raftCore.onDelete(datum.key, source); - } catch (Exception e) { - Loggers.RAFT.error("Raft onRemove failed.", e); - throw new NacosException(NacosException.SERVER_ERROR, - "Raft onRemove failed, datum:" + datum + ", source: " + source, e); - } - } - - private void checkIsStopWork() { - if (stopWork) { - throw new IllegalStateException("old raft protocol already stop work"); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftCore.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftCore.java deleted file mode 100644 index 188442d5b..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftCore.java +++ /dev/null @@ -1,1104 +0,0 @@ -/* - * 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.nacos.naming.consistency.persistent.raft; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; -import com.alibaba.nacos.common.http.Callback; -import com.alibaba.nacos.common.lifecycle.Closeable; -import com.alibaba.nacos.common.model.RestResult; -import com.alibaba.nacos.common.notify.EventPublisher; -import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.utils.ConcurrentHashSet; -import com.alibaba.nacos.common.utils.InternetAddressUtil; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.common.utils.NumberUtils; -import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.consistency.DataOperation; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.consistency.RecordListener; -import com.alibaba.nacos.naming.consistency.ValueChangeEvent; -import com.alibaba.nacos.naming.consistency.persistent.ClusterVersionJudgement; -import com.alibaba.nacos.naming.consistency.persistent.PersistentNotifier; -import com.alibaba.nacos.naming.core.Instances; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.misc.GlobalConfig; -import com.alibaba.nacos.naming.misc.GlobalExecutor; -import com.alibaba.nacos.naming.misc.HttpClient; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.NetUtils; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.naming.monitor.MetricsMonitor; -import com.alibaba.nacos.naming.pojo.Record; -import com.alibaba.nacos.sys.env.EnvUtil; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.springframework.context.annotation.DependsOn; -import org.springframework.http.HttpMethod; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.zip.GZIPOutputStream; - -import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; - -/** - * Raft core code. - * - * @author nacos - * @deprecated will remove in 1.4.x - */ -@Deprecated -@DependsOn("ProtocolManager") -@Component -public class RaftCore implements Closeable { - - public static final String API_VOTE = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/vote"; - - public static final String API_BEAT = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/beat"; - - public static final String API_PUB = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/datum"; - - public static final String API_DEL = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/datum"; - - public static final String API_GET = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/datum"; - - public static final String API_ON_PUB = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/datum/commit"; - - public static final String API_ON_DEL = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/datum/commit"; - - public static final String API_GET_PEER = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/peer"; - - public static final Lock OPERATE_LOCK = new ReentrantLock(); - - public static final int PUBLISH_TERM_INCREASE_COUNT = 100; - - private volatile ConcurrentMap> listeners = new ConcurrentHashMap<>(); - - private volatile ConcurrentMap datums = new ConcurrentHashMap<>(); - - private RaftPeerSet peers; - - private final SwitchDomain switchDomain; - - private final GlobalConfig globalConfig; - - private final RaftProxy raftProxy; - - private final RaftStore raftStore; - - private final ClusterVersionJudgement versionJudgement; - - public final PersistentNotifier notifier; - - private final EventPublisher publisher; - - private final RaftListener raftListener; - - private boolean initialized = false; - - private volatile boolean stopWork = false; - - private ScheduledFuture masterTask = null; - - private ScheduledFuture heartbeatTask = null; - - public RaftCore(RaftPeerSet peers, SwitchDomain switchDomain, GlobalConfig globalConfig, RaftProxy raftProxy, - RaftStore raftStore, ClusterVersionJudgement versionJudgement, RaftListener raftListener) { - this.peers = peers; - this.switchDomain = switchDomain; - this.globalConfig = globalConfig; - this.raftProxy = raftProxy; - this.raftStore = raftStore; - this.versionJudgement = versionJudgement; - this.notifier = new PersistentNotifier(key -> null == getDatum(key) ? null : getDatum(key).value); - this.publisher = NotifyCenter.registerToPublisher(ValueChangeEvent.class, 16384); - this.raftListener = raftListener; - } - - /** - * Init raft core. - * - * @throws Exception any exception during init - */ - @PostConstruct - public void init() throws Exception { - Loggers.RAFT.info("initializing Raft sub-system"); - final long start = System.currentTimeMillis(); - if (!EnvUtil.isSupportUpgradeFrom1X()) { - Loggers.RAFT.info("Upgrade from 1X feature has closed, " - + "If you want open this feature, please set `nacos.core.support.upgrade.from.1x=true` in application.properties"); - initialized = true; - stopWork = true; - return; - } - - raftStore.loadDatums(notifier, datums); - - setTerm(NumberUtils.toLong(raftStore.loadMeta().getProperty("term"), 0L)); - - Loggers.RAFT.info("cache loaded, datum count: {}, current term: {}", datums.size(), peers.getTerm()); - - initialized = true; - - Loggers.RAFT.info("finish to load data from disk, cost: {} ms.", (System.currentTimeMillis() - start)); - - masterTask = GlobalExecutor.registerMasterElection(new MasterElection()); - heartbeatTask = GlobalExecutor.registerHeartbeat(new HeartBeat()); - - versionJudgement.registerObserver(isAllNewVersion -> { - stopWork = isAllNewVersion; - if (stopWork) { - try { - shutdown(); - raftListener.removeOldRaftMetadata(); - } catch (NacosException e) { - throw new NacosRuntimeException(NacosException.SERVER_ERROR, e); - } - } - }, 100); - - NotifyCenter.registerSubscriber(notifier); - - Loggers.RAFT.info("timer started: leader timeout ms: {}, heart-beat timeout ms: {}", - GlobalExecutor.LEADER_TIMEOUT_MS, GlobalExecutor.HEARTBEAT_INTERVAL_MS); - } - - public Map> getListeners() { - return notifier.getListeners(); - } - - /** - * Signal publish new record. If not leader, signal to leader. If leader, try to commit publish. - * - * @param key key - * @param value value - * @throws Exception any exception during publish - */ - public void signalPublish(String key, Record value) throws Exception { - if (stopWork) { - throw new IllegalStateException("old raft protocol already stop work"); - } - if (!isLeader()) { - ObjectNode params = JacksonUtils.createEmptyJsonNode(); - params.put("key", key); - params.replace("value", JacksonUtils.transferToJsonNode(value)); - Map parameters = new HashMap<>(1); - parameters.put("key", key); - - final RaftPeer leader = getLeader(); - - raftProxy.proxyPostLarge(leader.ip, API_PUB, params.toString(), parameters); - return; - } - - OPERATE_LOCK.lock(); - try { - final long start = System.currentTimeMillis(); - final Datum datum = new Datum(); - datum.key = key; - datum.value = value; - if (getDatum(key) == null) { - datum.timestamp.set(1L); - } else { - datum.timestamp.set(getDatum(key).timestamp.incrementAndGet()); - } - - ObjectNode json = JacksonUtils.createEmptyJsonNode(); - json.replace("datum", JacksonUtils.transferToJsonNode(datum)); - json.replace("source", JacksonUtils.transferToJsonNode(peers.local())); - - onPublish(datum, peers.local()); - - final String content = json.toString(); - - final CountDownLatch latch = new CountDownLatch(peers.majorityCount()); - for (final String server : peers.allServersIncludeMyself()) { - if (isLeader(server)) { - latch.countDown(); - continue; - } - final String url = buildUrl(server, API_ON_PUB); - HttpClient.asyncHttpPostLarge(url, Arrays.asList("key", key), content, new Callback() { - @Override - public void onReceive(RestResult result) { - if (!result.ok()) { - Loggers.RAFT - .warn("[RAFT] failed to publish data to peer, datumId={}, peer={}, http code={}", - datum.key, server, result.getCode()); - return; - } - latch.countDown(); - } - - @Override - public void onError(Throwable throwable) { - Loggers.RAFT.error("[RAFT] failed to publish data to peer", throwable); - } - - @Override - public void onCancel() { - - } - }); - - } - - if (!latch.await(UtilsAndCommons.RAFT_PUBLISH_TIMEOUT, TimeUnit.MILLISECONDS)) { - // only majority servers return success can we consider this update success - Loggers.RAFT.error("data publish failed, caused failed to notify majority, key={}", key); - throw new IllegalStateException("data publish failed, caused failed to notify majority, key=" + key); - } - - long end = System.currentTimeMillis(); - Loggers.RAFT.info("signalPublish cost {} ms, key: {}", (end - start), key); - } finally { - OPERATE_LOCK.unlock(); - } - } - - /** - * Signal delete record. If not leader, signal leader delete. If leader, try to commit delete. - * - * @param key key - * @throws Exception any exception during delete - */ - public void signalDelete(final String key) throws Exception { - if (stopWork) { - throw new IllegalStateException("old raft protocol already stop work"); - } - OPERATE_LOCK.lock(); - try { - - if (!isLeader()) { - Map params = new HashMap<>(1); - params.put("key", URLEncoder.encode(key, "UTF-8")); - raftProxy.proxy(getLeader().ip, API_DEL, params, HttpMethod.DELETE); - return; - } - - // construct datum: - Datum datum = new Datum(); - datum.key = key; - ObjectNode json = JacksonUtils.createEmptyJsonNode(); - json.replace("datum", JacksonUtils.transferToJsonNode(datum)); - json.replace("source", JacksonUtils.transferToJsonNode(peers.local())); - - onDelete(datum.key, peers.local()); - - for (final String server : peers.allServersWithoutMySelf()) { - String url = buildUrl(server, API_ON_DEL); - HttpClient.asyncHttpDeleteLarge(url, null, json.toString(), new Callback() { - @Override - public void onReceive(RestResult result) { - if (!result.ok()) { - Loggers.RAFT - .warn("[RAFT] failed to delete data from peer, datumId={}, peer={}, http code={}", - key, server, result.getCode()); - return; - } - - RaftPeer local = peers.local(); - - local.resetLeaderDue(); - } - - @Override - public void onError(Throwable throwable) { - Loggers.RAFT.error("[RAFT] failed to delete data from peer", throwable); - } - - @Override - public void onCancel() { - - } - }); - } - } finally { - OPERATE_LOCK.unlock(); - } - } - - /** - * Do publish. If leader, commit publish to store. If not leader, stop publish because should signal to leader. - * - * @param datum datum - * @param source source raft peer - * @throws Exception any exception during publish - */ - public void onPublish(Datum datum, RaftPeer source) throws Exception { - if (stopWork) { - throw new IllegalStateException("old raft protocol already stop work"); - } - RaftPeer local = peers.local(); - if (datum.value == null) { - Loggers.RAFT.warn("received empty datum"); - throw new IllegalStateException("received empty datum"); - } - - if (!peers.isLeader(source.ip)) { - Loggers.RAFT - .warn("peer {} tried to publish data but wasn't leader, leader: {}", JacksonUtils.toJson(source), - JacksonUtils.toJson(getLeader())); - throw new IllegalStateException("peer(" + source.ip + ") tried to publish " + "data but wasn't leader"); - } - - if (source.term.get() < local.term.get()) { - Loggers.RAFT.warn("out of date publish, pub-term: {}, cur-term: {}", JacksonUtils.toJson(source), - JacksonUtils.toJson(local)); - throw new IllegalStateException( - "out of date publish, pub-term:" + source.term.get() + ", cur-term: " + local.term.get()); - } - - local.resetLeaderDue(); - - // if data should be persisted, usually this is true: - if (KeyBuilder.matchPersistentKey(datum.key)) { - raftStore.write(datum); - } - - datums.put(datum.key, datum); - - if (isLeader()) { - local.term.addAndGet(PUBLISH_TERM_INCREASE_COUNT); - } else { - if (local.term.get() + PUBLISH_TERM_INCREASE_COUNT > source.term.get()) { - //set leader term: - getLeader().term.set(source.term.get()); - local.term.set(getLeader().term.get()); - } else { - local.term.addAndGet(PUBLISH_TERM_INCREASE_COUNT); - } - } - raftStore.updateTerm(local.term.get()); - NotifyCenter.publishEvent(ValueChangeEvent.builder().key(datum.key).action(DataOperation.CHANGE).build()); - Loggers.RAFT.info("data added/updated, key={}, term={}", datum.key, local.term); - } - - /** - * Do delete. If leader, commit delete to store. If not leader, stop delete because should signal to leader. - * - * @param datumKey datum key - * @param source source raft peer - * @throws Exception any exception during delete - */ - public void onDelete(String datumKey, RaftPeer source) throws Exception { - if (stopWork) { - throw new IllegalStateException("old raft protocol already stop work"); - } - RaftPeer local = peers.local(); - - if (!peers.isLeader(source.ip)) { - Loggers.RAFT - .warn("peer {} tried to publish data but wasn't leader, leader: {}", JacksonUtils.toJson(source), - JacksonUtils.toJson(getLeader())); - throw new IllegalStateException("peer(" + source.ip + ") tried to publish data but wasn't leader"); - } - - if (source.term.get() < local.term.get()) { - Loggers.RAFT.warn("out of date publish, pub-term: {}, cur-term: {}", JacksonUtils.toJson(source), - JacksonUtils.toJson(local)); - throw new IllegalStateException( - "out of date publish, pub-term:" + source.term + ", cur-term: " + local.term); - } - - local.resetLeaderDue(); - - // do apply - String key = datumKey; - deleteDatum(key); - - if (KeyBuilder.matchServiceMetaKey(key)) { - - if (local.term.get() + PUBLISH_TERM_INCREASE_COUNT > source.term.get()) { - //set leader term: - getLeader().term.set(source.term.get()); - local.term.set(getLeader().term.get()); - } else { - local.term.addAndGet(PUBLISH_TERM_INCREASE_COUNT); - } - - raftStore.updateTerm(local.term.get()); - } - - Loggers.RAFT.info("data removed, key={}, term={}", datumKey, local.term); - - } - - @Override - public void shutdown() throws NacosException { - this.stopWork = true; - this.raftStore.shutdown(); - this.peers.shutdown(); - Loggers.RAFT.warn("start to close old raft protocol!!!"); - Loggers.RAFT.warn("stop old raft protocol task for notifier"); - NotifyCenter.deregisterSubscriber(notifier); - if (masterTask != null) { - Loggers.RAFT.warn("stop old raft protocol task for master task"); - masterTask.cancel(true); - } - - if (heartbeatTask != null) { - Loggers.RAFT.warn("stop old raft protocol task for heartbeat task"); - heartbeatTask.cancel(true); - } - - Loggers.RAFT.warn("clean old cache datum for old raft"); - datums.clear(); - } - - public class MasterElection implements Runnable { - - @Override - public void run() { - try { - if (stopWork) { - return; - } - if (!peers.isReady()) { - return; - } - - RaftPeer local = peers.local(); - local.leaderDueMs -= GlobalExecutor.TICK_PERIOD_MS; - - if (local.leaderDueMs > 0) { - return; - } - - // reset timeout - local.resetLeaderDue(); - local.resetHeartbeatDue(); - - sendVote(); - } catch (Exception e) { - Loggers.RAFT.warn("[RAFT] error while master election {}", e); - } - - } - - private void sendVote() { - - RaftPeer local = peers.get(NetUtils.localServer()); - Loggers.RAFT.info("leader timeout, start voting,leader: {}, term: {}", JacksonUtils.toJson(getLeader()), - local.term); - - peers.reset(); - - local.term.incrementAndGet(); - local.voteFor = local.ip; - local.state = RaftPeer.State.CANDIDATE; - - Map params = new HashMap<>(1); - params.put("vote", JacksonUtils.toJson(local)); - for (final String server : peers.allServersWithoutMySelf()) { - final String url = buildUrl(server, API_VOTE); - try { - HttpClient.asyncHttpPost(url, null, params, new Callback() { - @Override - public void onReceive(RestResult result) { - if (!result.ok()) { - Loggers.RAFT.error("NACOS-RAFT vote failed: {}, url: {}", result.getCode(), url); - return; - } - - RaftPeer peer = JacksonUtils.toObj(result.getData(), RaftPeer.class); - - Loggers.RAFT.info("received approve from peer: {}", JacksonUtils.toJson(peer)); - - peers.decideLeader(peer); - - } - - @Override - public void onError(Throwable throwable) { - Loggers.RAFT.error("error while sending vote to server: {}", server, throwable); - } - - @Override - public void onCancel() { - - } - }); - } catch (Exception e) { - Loggers.RAFT.warn("error while sending vote to server: {}", server); - } - } - } - } - - /** - * Received vote. - * - * @param remote remote raft peer of vote information - * @return self-peer information - */ - public synchronized RaftPeer receivedVote(RaftPeer remote) { - if (stopWork) { - throw new IllegalStateException("old raft protocol already stop work"); - } - if (!peers.contains(remote)) { - throw new IllegalStateException("can not find peer: " + remote.ip); - } - - RaftPeer local = peers.get(NetUtils.localServer()); - if (remote.term.get() <= local.term.get()) { - String msg = "received illegitimate vote" + ", voter-term:" + remote.term + ", votee-term:" + local.term; - - Loggers.RAFT.info(msg); - if (StringUtils.isEmpty(local.voteFor)) { - local.voteFor = local.ip; - } - - return local; - } - - local.resetLeaderDue(); - - local.state = RaftPeer.State.FOLLOWER; - local.voteFor = remote.ip; - local.term.set(remote.term.get()); - - Loggers.RAFT.info("vote {} as leader, term: {}", remote.ip, remote.term); - - return local; - } - - public class HeartBeat implements Runnable { - - @Override - public void run() { - try { - if (stopWork) { - return; - } - if (!peers.isReady()) { - return; - } - - RaftPeer local = peers.local(); - local.heartbeatDueMs -= GlobalExecutor.TICK_PERIOD_MS; - if (local.heartbeatDueMs > 0) { - return; - } - - local.resetHeartbeatDue(); - - sendBeat(); - } catch (Exception e) { - Loggers.RAFT.warn("[RAFT] error while sending beat {}", e); - } - - } - - private void sendBeat() throws IOException, InterruptedException { - RaftPeer local = peers.local(); - if (EnvUtil.getStandaloneMode() || local.state != RaftPeer.State.LEADER) { - return; - } - if (Loggers.RAFT.isDebugEnabled()) { - Loggers.RAFT.debug("[RAFT] send beat with {} keys.", datums.size()); - } - - local.resetLeaderDue(); - - // build data - ObjectNode packet = JacksonUtils.createEmptyJsonNode(); - packet.replace("peer", JacksonUtils.transferToJsonNode(local)); - - ArrayNode array = JacksonUtils.createEmptyArrayNode(); - - if (switchDomain.isSendBeatOnly()) { - Loggers.RAFT.info("[SEND-BEAT-ONLY] {}", switchDomain.isSendBeatOnly()); - } - - if (!switchDomain.isSendBeatOnly()) { - for (Datum datum : datums.values()) { - - ObjectNode element = JacksonUtils.createEmptyJsonNode(); - - if (KeyBuilder.matchServiceMetaKey(datum.key)) { - element.put("key", KeyBuilder.briefServiceMetaKey(datum.key)); - } else if (KeyBuilder.matchInstanceListKey(datum.key)) { - element.put("key", KeyBuilder.briefInstanceListkey(datum.key)); - } - element.put("timestamp", datum.timestamp.get()); - - array.add(element); - } - } - - packet.replace("datums", array); - // broadcast - Map params = new HashMap(1); - params.put("beat", JacksonUtils.toJson(packet)); - - String content = JacksonUtils.toJson(params); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - GZIPOutputStream gzip = new GZIPOutputStream(out); - gzip.write(content.getBytes(StandardCharsets.UTF_8)); - gzip.close(); - - byte[] compressedBytes = out.toByteArray(); - String compressedContent = new String(compressedBytes, StandardCharsets.UTF_8); - - if (Loggers.RAFT.isDebugEnabled()) { - Loggers.RAFT.debug("raw beat data size: {}, size of compressed data: {}", content.length(), - compressedContent.length()); - } - - for (final String server : peers.allServersWithoutMySelf()) { - try { - final String url = buildUrl(server, API_BEAT); - if (Loggers.RAFT.isDebugEnabled()) { - Loggers.RAFT.debug("send beat to server " + server); - } - HttpClient.asyncHttpPostLarge(url, null, compressedBytes, new Callback() { - @Override - public void onReceive(RestResult result) { - if (!result.ok()) { - Loggers.RAFT.error("NACOS-RAFT beat failed: {}, peer: {}", result.getCode(), server); - MetricsMonitor.getLeaderSendBeatFailedException().increment(); - return; - } - - peers.update(JacksonUtils.toObj(result.getData(), RaftPeer.class)); - if (Loggers.RAFT.isDebugEnabled()) { - Loggers.RAFT.debug("receive beat response from: {}", url); - } - } - - @Override - public void onError(Throwable throwable) { - Loggers.RAFT.error("NACOS-RAFT error while sending heart-beat to peer: {} {}", server, - throwable); - MetricsMonitor.getLeaderSendBeatFailedException().increment(); - } - - @Override - public void onCancel() { - - } - }); - } catch (Exception e) { - Loggers.RAFT.error("error while sending heart-beat to peer: {} {}", server, e); - MetricsMonitor.getLeaderSendBeatFailedException().increment(); - } - } - - } - } - - /** - * Received beat from leader. // TODO split method to multiple smaller method. - * - * @param beat beat information from leader - * @return self-peer information - * @throws Exception any exception during handle - */ - public RaftPeer receivedBeat(JsonNode beat) throws Exception { - if (stopWork) { - throw new IllegalStateException("old raft protocol already stop work"); - } - final RaftPeer local = peers.local(); - final RaftPeer remote = new RaftPeer(); - JsonNode peer = beat.get("peer"); - remote.ip = peer.get("ip").asText(); - remote.state = RaftPeer.State.valueOf(peer.get("state").asText()); - remote.term.set(peer.get("term").asLong()); - remote.heartbeatDueMs = peer.get("heartbeatDueMs").asLong(); - remote.leaderDueMs = peer.get("leaderDueMs").asLong(); - remote.voteFor = peer.get("voteFor").asText(); - - if (remote.state != RaftPeer.State.LEADER) { - Loggers.RAFT.info("[RAFT] invalid state from master, state: {}, remote peer: {}", remote.state, - JacksonUtils.toJson(remote)); - throw new IllegalArgumentException("invalid state from master, state: " + remote.state); - } - - if (local.term.get() > remote.term.get()) { - Loggers.RAFT - .info("[RAFT] out of date beat, beat-from-term: {}, beat-to-term: {}, remote peer: {}, and leaderDueMs: {}", - remote.term.get(), local.term.get(), JacksonUtils.toJson(remote), local.leaderDueMs); - throw new IllegalArgumentException( - "out of date beat, beat-from-term: " + remote.term.get() + ", beat-to-term: " + local.term.get()); - } - - if (local.state != RaftPeer.State.FOLLOWER) { - - Loggers.RAFT.info("[RAFT] make remote as leader, remote peer: {}", JacksonUtils.toJson(remote)); - // mk follower - local.state = RaftPeer.State.FOLLOWER; - local.voteFor = remote.ip; - } - - final JsonNode beatDatums = beat.get("datums"); - local.resetLeaderDue(); - local.resetHeartbeatDue(); - - peers.makeLeader(remote); - - if (!switchDomain.isSendBeatOnly()) { - - Map receivedKeysMap = new HashMap<>(datums.size()); - - for (Map.Entry entry : datums.entrySet()) { - receivedKeysMap.put(entry.getKey(), 0); - } - - // now check datums - List batch = new ArrayList<>(); - - int processedCount = 0; - if (Loggers.RAFT.isDebugEnabled()) { - Loggers.RAFT - .debug("[RAFT] received beat with {} keys, RaftCore.datums' size is {}, remote server: {}, term: {}, local term: {}", - beatDatums.size(), datums.size(), remote.ip, remote.term, local.term); - } - for (Object object : beatDatums) { - processedCount = processedCount + 1; - - JsonNode entry = (JsonNode) object; - String key = entry.get("key").asText(); - final String datumKey; - - if (KeyBuilder.matchServiceMetaKey(key)) { - datumKey = KeyBuilder.detailServiceMetaKey(key); - } else if (KeyBuilder.matchInstanceListKey(key)) { - datumKey = KeyBuilder.detailInstanceListkey(key); - } else { - // ignore corrupted key: - continue; - } - - long timestamp = entry.get("timestamp").asLong(); - - receivedKeysMap.put(datumKey, 1); - - try { - if (datums.containsKey(datumKey) && datums.get(datumKey).timestamp.get() >= timestamp - && processedCount < beatDatums.size()) { - continue; - } - - if (!(datums.containsKey(datumKey) && datums.get(datumKey).timestamp.get() >= timestamp)) { - batch.add(datumKey); - } - - if (batch.size() < 50 && processedCount < beatDatums.size()) { - continue; - } - - String keys = StringUtils.join(batch, ","); - - if (batch.size() <= 0) { - continue; - } - - Loggers.RAFT.info("get datums from leader: {}, batch size is {}, processedCount is {}" - + ", datums' size is {}, RaftCore.datums' size is {}", getLeader().ip, batch.size(), - processedCount, beatDatums.size(), datums.size()); - - // update datum entry - String url = buildUrl(remote.ip, API_GET); - Map queryParam = new HashMap<>(1); - queryParam.put("keys", URLEncoder.encode(keys, "UTF-8")); - HttpClient.asyncHttpGet(url, null, queryParam, new Callback() { - @Override - public void onReceive(RestResult result) { - if (!result.ok()) { - return; - } - - List datumList = JacksonUtils - .toObj(result.getData(), new TypeReference>() { - }); - - for (JsonNode datumJson : datumList) { - Datum newDatum = null; - OPERATE_LOCK.lock(); - try { - - Datum oldDatum = getDatum(datumJson.get("key").asText()); - - if (oldDatum != null && datumJson.get("timestamp").asLong() <= oldDatum.timestamp - .get()) { - Loggers.RAFT - .info("[NACOS-RAFT] timestamp is smaller than that of mine, key: {}, remote: {}, local: {}", - datumJson.get("key").asText(), - datumJson.get("timestamp").asLong(), oldDatum.timestamp); - continue; - } - - if (KeyBuilder.matchServiceMetaKey(datumJson.get("key").asText())) { - Datum serviceDatum = new Datum<>(); - serviceDatum.key = datumJson.get("key").asText(); - serviceDatum.timestamp.set(datumJson.get("timestamp").asLong()); - serviceDatum.value = JacksonUtils - .toObj(datumJson.get("value").toString(), Service.class); - newDatum = serviceDatum; - } - - if (KeyBuilder.matchInstanceListKey(datumJson.get("key").asText())) { - Datum instancesDatum = new Datum<>(); - instancesDatum.key = datumJson.get("key").asText(); - instancesDatum.timestamp.set(datumJson.get("timestamp").asLong()); - instancesDatum.value = JacksonUtils - .toObj(datumJson.get("value").toString(), Instances.class); - newDatum = instancesDatum; - } - - if (newDatum == null || newDatum.value == null) { - Loggers.RAFT.error("receive null datum: {}", datumJson); - continue; - } - - raftStore.write(newDatum); - - datums.put(newDatum.key, newDatum); - notifier.notify(newDatum.key, DataOperation.CHANGE, newDatum.value); - - local.resetLeaderDue(); - - if (local.term.get() + 100 > remote.term.get()) { - getLeader().term.set(remote.term.get()); - local.term.set(getLeader().term.get()); - } else { - local.term.addAndGet(100); - } - - raftStore.updateTerm(local.term.get()); - - Loggers.RAFT.info("data updated, key: {}, timestamp: {}, from {}, local term: {}", - newDatum.key, newDatum.timestamp, JacksonUtils.toJson(remote), local.term); - - } catch (Throwable e) { - Loggers.RAFT - .error("[RAFT-BEAT] failed to sync datum from leader, datum: {}", newDatum, - e); - } finally { - OPERATE_LOCK.unlock(); - } - } - try { - TimeUnit.MILLISECONDS.sleep(200); - } catch (InterruptedException e) { - Loggers.RAFT.error("[RAFT-BEAT] Interrupted error ", e); - } - return; - } - - @Override - public void onError(Throwable throwable) { - Loggers.RAFT.error("[RAFT-BEAT] failed to sync datum from leader", throwable); - } - - @Override - public void onCancel() { - - } - - }); - - batch.clear(); - - } catch (Exception e) { - Loggers.RAFT.error("[NACOS-RAFT] failed to handle beat entry, key: {}", datumKey); - } - - } - - List deadKeys = new ArrayList<>(); - for (Map.Entry entry : receivedKeysMap.entrySet()) { - if (entry.getValue() == 0) { - deadKeys.add(entry.getKey()); - } - } - - for (String deadKey : deadKeys) { - try { - deleteDatum(deadKey); - } catch (Exception e) { - Loggers.RAFT.error("[NACOS-RAFT] failed to remove entry, key={} {}", deadKey, e); - } - } - - } - - return local; - } - - /** - * Add listener for target key. - * - * @param key key - * @param listener new listener - */ - public void listen(String key, RecordListener listener) { - notifier.registerListener(key, listener); - - Loggers.RAFT.info("add listener: {}", key); - // if data present, notify immediately - for (Datum datum : datums.values()) { - if (!listener.interests(datum.key)) { - continue; - } - - try { - listener.onChange(datum.key, datum.value); - } catch (Exception e) { - Loggers.RAFT.error("NACOS-RAFT failed to notify listener", e); - } - } - } - - /** - * Remove listener for key. - * - * @param key key - * @param listener listener - */ - public void unListen(String key, RecordListener listener) { - notifier.deregisterListener(key, listener); - } - - public void unListenAll(String key) { - notifier.deregisterAllListener(key); - } - - public void setTerm(long term) { - peers.setTerm(term); - } - - public boolean isLeader(String ip) { - return peers.isLeader(ip); - } - - public boolean isLeader() { - return peers.isLeader(NetUtils.localServer()); - } - - /** - * Build api url. - * - * @param ip ip of api - * @param api api path - * @return api url - */ - public static String buildUrl(String ip, String api) { - if (!InternetAddressUtil.containsPort(ip)) { - ip = ip + InternetAddressUtil.IP_PORT_SPLITER + EnvUtil.getPort(); - } - return HTTP_PREFIX + ip + EnvUtil.getContextPath() + api; - } - - public Datum getDatum(String key) { - return datums.get(key); - } - - public RaftPeer getLeader() { - return peers.getLeader(); - } - - public List getPeers() { - return new ArrayList<>(peers.allPeers()); - } - - public RaftPeerSet getPeerSet() { - return peers; - } - - public void setPeerSet(RaftPeerSet peerSet) { - peers = peerSet; - } - - public int datumSize() { - return datums.size(); - } - - public void addDatum(Datum datum) { - datums.put(datum.key, datum); - NotifyCenter.publishEvent(ValueChangeEvent.builder().key(datum.key).action(DataOperation.CHANGE).build()); - } - - /** - * Load datum. - * - * @param key datum key - */ - public void loadDatum(String key) { - try { - Datum datum = raftStore.load(key); - if (datum == null) { - return; - } - datums.put(key, datum); - } catch (Exception e) { - Loggers.RAFT.error("load datum failed: " + key, e); - } - - } - - private void deleteDatum(String key) { - Datum deleted; - try { - deleted = datums.remove(URLDecoder.decode(key, "UTF-8")); - if (deleted != null) { - raftStore.delete(deleted); - Loggers.RAFT.info("datum deleted, key: {}", key); - } - NotifyCenter.publishEvent( - ValueChangeEvent.builder().key(URLDecoder.decode(key, "UTF-8")).action(DataOperation.DELETE) - .build()); - } catch (UnsupportedEncodingException e) { - Loggers.RAFT.warn("datum key decode failed: {}", key); - } - } - - public boolean isInitialized() { - return initialized || !globalConfig.isDataWarmup(); - } - - @Deprecated - public int getNotifyTaskCount() { - return (int) publisher.currentEventSize(); - } - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftListener.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftListener.java deleted file mode 100644 index 694de47f2..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftListener.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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.nacos.naming.consistency.persistent.raft; - -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.core.cluster.Member; -import com.alibaba.nacos.core.cluster.ServerMemberManager; -import com.alibaba.nacos.naming.consistency.persistent.ClusterVersionJudgement; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.constants.Constants; -import org.springframework.context.ApplicationEvent; -import org.springframework.context.event.SmartApplicationListener; -import org.springframework.stereotype.Component; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -/** - * Inject the raft information from the naming module into the outlier information of the node. - * - * @deprecated will remove in 1.4.x - * @author liaochuntao - */ -@Deprecated -@Component -public class RaftListener implements SmartApplicationListener { - - private final ServerMemberManager memberManager; - - private final ClusterVersionJudgement versionJudgement; - - private volatile boolean stopUpdate = false; - - /** - * Avoid multithreading mode. Old Raft information data cannot be properly removed. - */ - private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - - private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); - - private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); - - public RaftListener(ServerMemberManager memberManager, ClusterVersionJudgement versionJudgement) { - this.memberManager = memberManager; - this.versionJudgement = versionJudgement; - this.init(); - } - - private void init() { - this.versionJudgement.registerObserver(isAllNewVersion -> { - final Lock lock = this.writeLock; - lock.lock(); - try { - stopUpdate = isAllNewVersion; - if (stopUpdate) { - removeOldRaftMetadata(); - } - } finally { - lock.unlock(); - } - }, -2); - } - - @Override - public boolean supportsEventType(Class eventType) { - return BaseRaftEvent.class.isAssignableFrom(eventType); - } - - @Override - public void onApplicationEvent(ApplicationEvent event) { - final Lock lock = readLock; - lock.lock(); - try { - if (event instanceof BaseRaftEvent && !stopUpdate) { - BaseRaftEvent raftEvent = (BaseRaftEvent) event; - RaftPeer local = raftEvent.getLocal(); - String json = JacksonUtils.toJson(local); - Map map = JacksonUtils.toObj(json, HashMap.class); - Member self = memberManager.getSelf(); - self.setExtendVal(Constants.OLD_NAMING_RAFT_GROUP, map); - memberManager.update(self); - } - if (stopUpdate) { - removeOldRaftMetadata(); - } - } finally { - lock.unlock(); - } - } - - void removeOldRaftMetadata() { - Loggers.RAFT.warn("start to move old raft protocol metadata"); - Member self = memberManager.getSelf(); - self.delExtendVal(Constants.OLD_NAMING_RAFT_GROUP); - memberManager.update(self); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftPeer.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftPeer.java deleted file mode 100644 index c11cc20fc..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftPeer.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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.nacos.naming.consistency.persistent.raft; - -import com.alibaba.nacos.naming.misc.GlobalExecutor; -import com.alibaba.nacos.common.utils.RandomUtils; -import com.alibaba.nacos.common.utils.StringUtils; - -import java.util.Objects; -import java.util.concurrent.atomic.AtomicLong; - -/** - * Raft peer. - * - * @deprecated will remove in 1.4.x - * @author nacos - */ -@Deprecated -public class RaftPeer { - - public String ip; - - public String voteFor; - - public AtomicLong term = new AtomicLong(0L); - - public volatile long leaderDueMs = RandomUtils.nextLong(0, GlobalExecutor.LEADER_TIMEOUT_MS); - - public volatile long heartbeatDueMs = RandomUtils.nextLong(0, GlobalExecutor.HEARTBEAT_INTERVAL_MS); - - public volatile State state = State.FOLLOWER; - - public void resetLeaderDue() { - leaderDueMs = GlobalExecutor.LEADER_TIMEOUT_MS + RandomUtils.nextLong(0, GlobalExecutor.RANDOM_MS); - } - - public void resetHeartbeatDue() { - heartbeatDueMs = GlobalExecutor.HEARTBEAT_INTERVAL_MS; - } - - public enum State { - /** - * Leader of the cluster, only one leader stands in a cluster. - */ - LEADER, - /** - * Follower of the cluster, report to and copy from leader. - */ - FOLLOWER, - /** - * Candidate leader to be elected. - */ - CANDIDATE - } - - @Override - public int hashCode() { - return Objects.hash(ip); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - - if (!(obj instanceof RaftPeer)) { - return false; - } - - RaftPeer other = (RaftPeer) obj; - - return StringUtils.equals(ip, other.ip); - } - - @Override - public String toString() { - return "RaftPeer{" + "ip='" + ip + '\'' + ", voteFor='" + voteFor + '\'' + ", term=" + term + ", leaderDueMs=" - + leaderDueMs + ", heartbeatDueMs=" + heartbeatDueMs + ", state=" + state + '}'; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftPeerSet.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftPeerSet.java deleted file mode 100644 index 5ee6b9aa3..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftPeerSet.java +++ /dev/null @@ -1,379 +0,0 @@ -/* - * 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.nacos.naming.consistency.persistent.raft; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.common.http.Callback; -import com.alibaba.nacos.common.lifecycle.Closeable; -import com.alibaba.nacos.common.model.RestResult; -import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.core.cluster.Member; -import com.alibaba.nacos.core.cluster.MemberChangeListener; -import com.alibaba.nacos.core.cluster.MembersChangeEvent; -import com.alibaba.nacos.core.cluster.ServerMemberManager; -import com.alibaba.nacos.naming.misc.HttpClient; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.NetUtils; -import com.alibaba.nacos.sys.env.EnvUtil; -import com.alibaba.nacos.sys.utils.ApplicationUtils; -import org.apache.commons.collections.SortedBag; -import org.apache.commons.collections.bag.TreeBag; -import com.alibaba.nacos.common.utils.StringUtils; -import org.springframework.context.annotation.DependsOn; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; - -/** - * Sets of raft peers. - * - * @author nacos - * @deprecated will remove in 1.4.x - */ -@Deprecated -@Component -@DependsOn("ProtocolManager") -public class RaftPeerSet extends MemberChangeListener implements Closeable { - - private final ServerMemberManager memberManager; - - private AtomicLong localTerm = new AtomicLong(0L); - - private RaftPeer leader = null; - - private volatile Map peers = new HashMap<>(8); - - private Set sites = new HashSet<>(); - - private volatile boolean ready = false; - - private Set oldMembers = new HashSet<>(); - - public RaftPeerSet(ServerMemberManager memberManager) { - this.memberManager = memberManager; - } - - /** - * Init method. - */ - @PostConstruct - public void init() { - if (!EnvUtil.isSupportUpgradeFrom1X()) { - return; - } - NotifyCenter.registerSubscriber(this); - changePeers(memberManager.allMembers()); - } - - @Override - public void shutdown() throws NacosException { - this.localTerm.set(-1); - this.leader = null; - this.peers.clear(); - this.sites.clear(); - this.ready = false; - this.oldMembers.clear(); - } - - public RaftPeer getLeader() { - if (EnvUtil.getStandaloneMode()) { - return local(); - } - return leader; - } - - public Set allSites() { - return sites; - } - - public boolean isReady() { - return ready; - } - - /** - * Remove raft node. - * - * @param servers node address need to be removed - */ - public void remove(List servers) { - for (String server : servers) { - peers.remove(server); - } - } - - /** - * Update raft peer. - * - * @param peer new peer. - * @return new peer - */ - public RaftPeer update(RaftPeer peer) { - peers.put(peer.ip, peer); - return peer; - } - - /** - * Judge whether input address is leader. - * - * @param ip peer address - * @return true if is leader or stand alone, otherwise false - */ - public boolean isLeader(String ip) { - if (EnvUtil.getStandaloneMode()) { - return true; - } - - if (leader == null) { - Loggers.RAFT.warn("[IS LEADER] no leader is available now!"); - return false; - } - - return StringUtils.equals(leader.ip, ip); - } - - public Set allServersIncludeMyself() { - return peers.keySet(); - } - - /** - * Get all servers excludes current peer. - * - * @return all servers excludes current peer - */ - public Set allServersWithoutMySelf() { - Set servers = new HashSet(peers.keySet()); - - // exclude myself - servers.remove(local().ip); - - return servers; - } - - public Collection allPeers() { - return peers.values(); - } - - public int size() { - return peers.size(); - } - - /** - * Calculate and decide which peer is leader. If has new peer has more than half vote, change leader to new peer. - * - * @param candidate new candidate - * @return new leader if new candidate has more than half vote, otherwise old leader - */ - public RaftPeer decideLeader(RaftPeer candidate) { - peers.put(candidate.ip, candidate); - - SortedBag ips = new TreeBag(); - int maxApproveCount = 0; - String maxApprovePeer = null; - for (RaftPeer peer : peers.values()) { - if (StringUtils.isEmpty(peer.voteFor)) { - continue; - } - - ips.add(peer.voteFor); - if (ips.getCount(peer.voteFor) > maxApproveCount) { - maxApproveCount = ips.getCount(peer.voteFor); - maxApprovePeer = peer.voteFor; - } - } - - if (maxApproveCount >= majorityCount()) { - RaftPeer peer = peers.get(maxApprovePeer); - peer.state = RaftPeer.State.LEADER; - - if (!Objects.equals(leader, peer)) { - leader = peer; - ApplicationUtils.publishEvent(new LeaderElectFinishedEvent(this, leader, local())); - Loggers.RAFT.info("{} has become the LEADER", leader.ip); - } - } - - return leader; - } - - /** - * Set leader as new candidate. - * - * @param candidate new candidate - * @return new leader - */ - public RaftPeer makeLeader(RaftPeer candidate) { - if (!Objects.equals(leader, candidate)) { - leader = candidate; - ApplicationUtils.publishEvent(new MakeLeaderEvent(this, leader, local())); - Loggers.RAFT - .info("{} has become the LEADER, local: {}, leader: {}", leader.ip, JacksonUtils.toJson(local()), - JacksonUtils.toJson(leader)); - } - - for (final RaftPeer peer : peers.values()) { - Map params = new HashMap<>(1); - if (!Objects.equals(peer, candidate) && peer.state == RaftPeer.State.LEADER) { - try { - String url = RaftCore.buildUrl(peer.ip, RaftCore.API_GET_PEER); - HttpClient.asyncHttpGet(url, null, params, new Callback() { - @Override - public void onReceive(RestResult result) { - if (!result.ok()) { - Loggers.RAFT - .error("[NACOS-RAFT] get peer failed: {}, peer: {}", result.getCode(), peer.ip); - peer.state = RaftPeer.State.FOLLOWER; - return; - } - - update(JacksonUtils.toObj(result.getData(), RaftPeer.class)); - } - - @Override - public void onError(Throwable throwable) { - - } - - @Override - public void onCancel() { - - } - }); - } catch (Exception e) { - peer.state = RaftPeer.State.FOLLOWER; - Loggers.RAFT.error("[NACOS-RAFT] error while getting peer from peer: {}", peer.ip); - } - } - } - - return update(candidate); - } - - /** - * Get local raft peer. - * - * @return local raft peer - */ - public RaftPeer local() { - RaftPeer peer = peers.get(EnvUtil.getLocalAddress()); - if (peer == null && EnvUtil.getStandaloneMode()) { - RaftPeer localPeer = new RaftPeer(); - localPeer.ip = NetUtils.localServer(); - localPeer.term.set(localTerm.get()); - peers.put(localPeer.ip, localPeer); - return localPeer; - } - if (peer == null) { - throw new IllegalStateException( - "unable to find local peer: " + NetUtils.localServer() + ", all peers: " + Arrays - .toString(peers.keySet().toArray())); - } - - return peer; - } - - public RaftPeer get(String server) { - return peers.get(server); - } - - public int majorityCount() { - return peers.size() / 2 + 1; - } - - /** - * Reset set. - */ - public void reset() { - - leader = null; - - for (RaftPeer peer : peers.values()) { - peer.voteFor = null; - } - } - - public void setTerm(long term) { - localTerm.set(term); - } - - public long getTerm() { - return localTerm.get(); - } - - public boolean contains(RaftPeer remote) { - return peers.containsKey(remote.ip); - } - - @Override - public void onEvent(MembersChangeEvent event) { - Collection members = event.getMembers(); - Collection newMembers = new HashSet<>(members); - newMembers.removeAll(oldMembers); - - // If an IP change occurs, the change starts - if (!newMembers.isEmpty()) { - changePeers(members); - } - - oldMembers.clear(); - oldMembers.addAll(members); - } - - protected void changePeers(Collection members) { - Map tmpPeers = new HashMap<>(members.size()); - - for (Member member : members) { - - final String address = member.getAddress(); - if (peers.containsKey(address)) { - tmpPeers.put(address, peers.get(address)); - continue; - } - - RaftPeer raftPeer = new RaftPeer(); - raftPeer.ip = address; - - // first time meet the local server: - if (EnvUtil.getLocalAddress().equals(address)) { - raftPeer.term.set(localTerm.get()); - } - - tmpPeers.put(address, raftPeer); - } - - // replace raft peer set: - peers = tmpPeers; - - ready = true; - Loggers.RAFT.info("raft peers changed: " + members); - } - - @Override - public String toString() { - return "RaftPeerSet{" + "localTerm=" + localTerm + ", leader=" + leader + ", peers=" + peers + ", sites=" - + sites + '}'; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftProxy.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftProxy.java deleted file mode 100644 index c45eced68..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftProxy.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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.nacos.naming.consistency.persistent.raft; - -import com.alibaba.nacos.common.model.RestResult; -import com.alibaba.nacos.common.utils.InternetAddressUtil; -import com.alibaba.nacos.naming.misc.HttpClient; -import com.alibaba.nacos.sys.env.EnvUtil; -import org.springframework.http.HttpMethod; -import org.springframework.stereotype.Component; - -import java.util.Map; - -import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; - -/** - * Raft http proxy. - * - * @deprecated will remove in 1.4.x - * @author nacos - */ -@Deprecated -@Component -public class RaftProxy { - - /** - * Proxy get method. - * - * @param server target server - * @param api api path - * @param params parameters - * @throws Exception any exception during request - */ - public void proxyGet(String server, String api, Map params) throws Exception { - // do proxy - if (!InternetAddressUtil.containsPort(server)) { - server = server + InternetAddressUtil.IP_PORT_SPLITER + EnvUtil.getPort(); - } - String url = HTTP_PREFIX + server + EnvUtil.getContextPath() + api; - - RestResult result = HttpClient.httpGet(url, null, params); - if (!result.ok()) { - throw new IllegalStateException("leader failed, caused by: " + result.getMessage()); - } - } - - /** - * Proxy specified method. - * - * @param server target server - * @param api api path - * @param params parameters - * @param method http method - * @throws Exception any exception during request - */ - public void proxy(String server, String api, Map params, HttpMethod method) throws Exception { - // do proxy - if (!InternetAddressUtil.containsPort(server)) { - server = server + InternetAddressUtil.IP_PORT_SPLITER + EnvUtil.getPort(); - } - String url = HTTP_PREFIX + server + EnvUtil.getContextPath() + api; - RestResult result; - switch (method) { - case GET: - result = HttpClient.httpGet(url, null, params); - break; - case POST: - result = HttpClient.httpPost(url, null, params); - break; - case DELETE: - result = HttpClient.httpDelete(url, null, params); - break; - default: - throw new RuntimeException("unsupported method:" + method); - } - - if (!result.ok()) { - throw new IllegalStateException("leader failed, caused by: " + result.getMessage()); - } - } - - /** - * Proxy post method with large body. - * - * @param server target server - * @param api api path - * @param content body - * @param headers headers - * @throws Exception any exception during request - */ - public void proxyPostLarge(String server, String api, String content, Map headers) - throws Exception { - // do proxy - if (!InternetAddressUtil.containsPort(server)) { - server = server + InternetAddressUtil.IP_PORT_SPLITER + EnvUtil.getPort(); - } - String url = HTTP_PREFIX + server + EnvUtil.getContextPath() + api; - - RestResult result = HttpClient.httpPostLarge(url, headers, content); - if (!result.ok()) { - throw new IllegalStateException("leader failed, caused by: " + result.getMessage()); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftStore.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftStore.java deleted file mode 100644 index 916aab47f..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftStore.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * 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.nacos.naming.consistency.persistent.raft; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.common.lifecycle.Closeable; -import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.consistency.DataOperation; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.consistency.ValueChangeEvent; -import com.alibaba.nacos.naming.consistency.persistent.PersistentNotifier; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.core.Instances; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.monitor.MetricsMonitor; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; -import com.alibaba.nacos.common.utils.StringUtils; -import org.springframework.stereotype.Component; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import static com.alibaba.nacos.naming.misc.UtilsAndCommons.DATA_BASE_DIR; - -/** - * Raft store. - * - * @author nacos - * @deprecated will remove in 1.4.x - */ -@Deprecated -@Component -public class RaftStore implements Closeable { - - private final Properties meta = new Properties(); - - private static final String META_FILE_NAME = DATA_BASE_DIR + File.separator + "meta.properties"; - - private static final String CACHE_DIR = DATA_BASE_DIR + File.separator + "data"; - - /** - * Load datum from cache file. - * - * @param notifier raft notifier - * @param datums cached datum map - * @throws Exception any exception during load - */ - public synchronized void loadDatums(PersistentNotifier notifier, Map datums) throws Exception { - - Datum datum; - long start = System.currentTimeMillis(); - for (File cache : listCaches()) { - if (cache.isDirectory() && cache.listFiles() != null) { - for (File datumFile : cache.listFiles()) { - datum = readDatum(datumFile, cache.getName()); - if (datum != null) { - datums.put(datum.key, datum); - if (notifier != null) { - NotifyCenter.publishEvent( - ValueChangeEvent.builder().key(datum.key).action(DataOperation.CHANGE).build()); - } - } - } - continue; - } - datum = readDatum(cache, StringUtils.EMPTY); - if (datum != null) { - datums.put(datum.key, datum); - } - } - - Loggers.RAFT.info("finish loading all datums, size: {} cost {} ms.", datums.size(), - (System.currentTimeMillis() - start)); - } - - /** - * Load Metadata from cache file. - * - * @return metadata - * @throws Exception any exception during load - */ - public synchronized Properties loadMeta() throws Exception { - File metaFile = new File(META_FILE_NAME); - if (!metaFile.exists() && !metaFile.getParentFile().mkdirs() && !metaFile.createNewFile()) { - throw new IllegalStateException("failed to create meta file: " + metaFile.getAbsolutePath()); - } - - try (FileInputStream inStream = new FileInputStream(metaFile)) { - meta.load(inStream); - } - return meta; - } - - /** - * Load datum from cache file by key. - * - * @param key datum key - * @return datum - * @throws Exception any exception during load - */ - public synchronized Datum load(String key) throws Exception { - long start = System.currentTimeMillis(); - // load data - for (File cache : listCaches()) { - if (!cache.isFile()) { - Loggers.RAFT.warn("warning: encountered directory in cache dir: {}", cache.getAbsolutePath()); - } - - if (!StringUtils.equals(cache.getName(), encodeDatumKey(key))) { - continue; - } - - Loggers.RAFT.info("finish loading datum, key: {} cost {} ms.", key, (System.currentTimeMillis() - start)); - return readDatum(cache, StringUtils.EMPTY); - } - - return null; - } - - private synchronized Datum readDatum(File file, String namespaceId) throws IOException { - if (!KeyBuilder.isDatumCacheFile(file.getName())) { - return null; - } - ByteBuffer buffer; - try (FileChannel fc = new FileInputStream(file).getChannel()) { - buffer = ByteBuffer.allocate((int) file.length()); - fc.read(buffer); - - String json = new String(buffer.array(), StandardCharsets.UTF_8); - if (StringUtils.isBlank(json)) { - return null; - } - - final String fileName = file.getName(); - - if (KeyBuilder.matchSwitchKey(fileName)) { - return JacksonUtils.toObj(json, new TypeReference>() { - }); - } - - if (KeyBuilder.matchServiceMetaKey(fileName)) { - - Datum serviceDatum; - - try { - serviceDatum = JacksonUtils.toObj(json.replace("\\", ""), new TypeReference>() { - }); - } catch (Exception e) { - JsonNode jsonObject = JacksonUtils.toObj(json); - - serviceDatum = new Datum<>(); - serviceDatum.timestamp.set(jsonObject.get("timestamp").asLong()); - serviceDatum.key = jsonObject.get("key").asText(); - serviceDatum.value = JacksonUtils.toObj(jsonObject.get("value").toString(), Service.class); - } - - if (StringUtils.isBlank(serviceDatum.value.getGroupName())) { - serviceDatum.value.setGroupName(Constants.DEFAULT_GROUP); - } - if (!serviceDatum.value.getName().contains(Constants.SERVICE_INFO_SPLITER)) { - serviceDatum.value.setName( - Constants.DEFAULT_GROUP + Constants.SERVICE_INFO_SPLITER + serviceDatum.value.getName()); - } - - return serviceDatum; - } - - if (KeyBuilder.matchInstanceListKey(fileName)) { - - Datum instancesDatum; - - try { - instancesDatum = JacksonUtils.toObj(json, new TypeReference>() { - }); - } catch (Exception e) { - JsonNode jsonObject = JacksonUtils.toObj(json); - instancesDatum = new Datum<>(); - instancesDatum.timestamp.set(jsonObject.get("timestamp").asLong()); - - String key = jsonObject.get("key").asText(); - String serviceName = KeyBuilder.getServiceName(key); - key = key.substring(0, key.indexOf(serviceName)) + Constants.DEFAULT_GROUP - + Constants.SERVICE_INFO_SPLITER + serviceName; - - instancesDatum.key = key; - instancesDatum.value = new Instances(); - instancesDatum.value.setInstanceList( - JacksonUtils.toObj(jsonObject.get("value").toString(), new TypeReference>() { - })); - if (!instancesDatum.value.getInstanceList().isEmpty()) { - for (Instance instance : instancesDatum.value.getInstanceList()) { - instance.setEphemeral(false); - } - } - } - - return instancesDatum; - } - - return JacksonUtils.toObj(json, Datum.class); - - } catch (Exception e) { - Loggers.RAFT.warn("waning: failed to deserialize key: {}", file.getName()); - throw e; - } - } - - private String cacheFileName(String namespaceId, String datumKey) { - String fileName; - if (StringUtils.isNotBlank(namespaceId)) { - fileName = CACHE_DIR + File.separator + namespaceId + File.separator + encodeDatumKey(datumKey); - } else { - fileName = CACHE_DIR + File.separator + encodeDatumKey(datumKey); - } - return fileName; - } - - /** - * Write datum to cache file. - * - * @param datum datum - * @throws Exception any exception during writing - */ - public synchronized void write(final Datum datum) throws Exception { - - String namespaceId = KeyBuilder.getNamespace(datum.key); - - File cacheFile = new File(cacheFileName(namespaceId, datum.key)); - - if (!cacheFile.exists() && !cacheFile.getParentFile().mkdirs() && !cacheFile.createNewFile()) { - MetricsMonitor.getDiskException().increment(); - - throw new IllegalStateException("can not make cache file: " + cacheFile.getName()); - } - - ByteBuffer data; - - data = ByteBuffer.wrap(JacksonUtils.toJson(datum).getBytes(StandardCharsets.UTF_8)); - - try (FileChannel fc = new FileOutputStream(cacheFile, false).getChannel()) { - fc.write(data, data.position()); - fc.force(true); - } catch (Exception e) { - MetricsMonitor.getDiskException().increment(); - throw e; - } - - // remove old format file: - if (StringUtils.isNoneBlank(namespaceId)) { - if (datum.key.contains(Constants.DEFAULT_GROUP + Constants.SERVICE_INFO_SPLITER)) { - String oldDatumKey = datum.key - .replace(Constants.DEFAULT_GROUP + Constants.SERVICE_INFO_SPLITER, StringUtils.EMPTY); - - cacheFile = new File(cacheFileName(namespaceId, oldDatumKey)); - if (cacheFile.exists() && !cacheFile.delete()) { - Loggers.RAFT.error("[RAFT-DELETE] failed to delete old format datum: {}, value: {}", datum.key, - datum.value); - throw new IllegalStateException("failed to delete old format datum: " + datum.key); - } - } - } - } - - private File[] listCaches() throws Exception { - File cacheDir = new File(CACHE_DIR); - if (!cacheDir.exists() && !cacheDir.mkdirs()) { - throw new IllegalStateException("cloud not make out directory: " + cacheDir.getName()); - } - - return cacheDir.listFiles(); - } - - /** - * Delete datum from cache file. - * - * @param datum datum - */ - public void delete(Datum datum) { - - // datum key contains namespace info: - String namespaceId = KeyBuilder.getNamespace(datum.key); - - if (StringUtils.isNotBlank(namespaceId)) { - - File cacheFile = new File(cacheFileName(namespaceId, datum.key)); - if (cacheFile.exists() && !cacheFile.delete()) { - Loggers.RAFT.error("[RAFT-DELETE] failed to delete datum: {}, value: {}", datum.key, datum.value); - throw new IllegalStateException("failed to delete datum: " + datum.key); - } - } - } - - /** - * Update term Metadata. - * - * @param term term - * @throws Exception any exception during update - */ - public void updateTerm(long term) throws Exception { - File file = new File(META_FILE_NAME); - if (!file.exists() && !file.getParentFile().mkdirs() && !file.createNewFile()) { - throw new IllegalStateException("failed to create meta file"); - } - - try (FileOutputStream outStream = new FileOutputStream(file)) { - // write meta - meta.setProperty("term", String.valueOf(term)); - meta.store(outStream, null); - } - } - - private static String encodeDatumKey(String datumKey) { - return datumKey.replace(':', '#'); - } - - private static String decodeDatumKey(String datumKey) { - return datumKey.replace("#", ":"); - } - - @Override - public void shutdown() throws NacosException { - - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/constants/ClientConstants.java b/naming/src/main/java/com/alibaba/nacos/naming/constants/ClientConstants.java index 1a0724ff5..cab996df6 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/constants/ClientConstants.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/constants/ClientConstants.java @@ -35,6 +35,8 @@ public class ClientConstants { public static final String PERSISTENT_IP_PORT = "persistentIpPort"; + public static final String REVISION = "revision"; + public static final String PERSISTENT_SUFFIX = "false"; public static final String CLIENT_EXPIRED_TIME_CONFIG_KEY = "nacos.naming.client.expired.time"; diff --git a/naming/src/main/java/com/alibaba/nacos/naming/constants/Constants.java b/naming/src/main/java/com/alibaba/nacos/naming/constants/Constants.java index eac076553..5100c0bb9 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/constants/Constants.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/constants/Constants.java @@ -29,8 +29,6 @@ public final class Constants { public static final String INSTANCE_METADATA = "naming_instance_metadata"; - public static final String OLD_NAMING_RAFT_GROUP = "naming"; - public static final String NAMING_PERSISTENT_SERVICE_GROUP = "naming_persistent_service"; public static final String NAMING_PERSISTENT_SERVICE_GROUP_V2 = "naming_persistent_service_v2"; @@ -57,21 +55,6 @@ public final class Constants { */ public static final String EXPIRED_METADATA_EXPIRED_TIME = "nacos.naming.clean.expired-metadata.expired-time"; - /** - * Task time interval between twice processing, unit is millisecond. default: 2000 ms. - */ - public static final String DISTRO_TASK_DISPATCH_PERIOD = "nacos.naming.distro.taskDispatchPeriod"; - - /** - * The batch size of the key that distro combined delay task for http. default: 1000. - */ - public static final String DISTRO_BATCH_SYNC_KEY_COUNT = "nacos.naming.distro.batchSyncKeyCount"; - - /** - * Task time interval between twice processing, unit is millisecond. default: 5000ms - */ - public static final String DISTRO_SYNC_RETRY_DELAY = "nacos.naming.distro.syncRetryDelay"; - /** * default: false. */ @@ -82,11 +65,6 @@ public final class Constants { */ public static final String EXPIRE_INSTANCE = "nacos.naming.expireInstance"; - /** - * default 60000L. - */ - public static final String LOAD_DATA_RETRY_DELAY_MILLIS = "nacos.naming.clean.loadDataRetryDelayMillis"; - /** * UDP max retry times. */ @@ -97,11 +75,6 @@ public final class Constants { */ public static final long ACK_TIMEOUT_NANOS = TimeUnit.SECONDS.toNanos(10L); - /** - * The Milliseconds for push timeout. - */ - public static final long DEFAULT_PUSH_TIMEOUT_MILLS = TimeUnit.SECONDS.toNanos(3L); - /** * The custom instance id key. */ diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/CatalogController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/CatalogController.java index e2c46f68b..ab2df9010 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/CatalogController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/CatalogController.java @@ -18,23 +18,15 @@ package com.alibaba.nacos.naming.controllers; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.CommonParams; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.core.utils.WebUtils; import com.alibaba.nacos.naming.core.CatalogService; -import com.alibaba.nacos.naming.core.CatalogServiceV1Impl; import com.alibaba.nacos.naming.core.CatalogServiceV2Impl; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import com.alibaba.nacos.naming.healthcheck.HealthCheckTask; import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; @@ -42,9 +34,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import javax.servlet.http.HttpServletRequest; import java.util.List; -import java.util.Map; /** * Catalog controller. @@ -55,18 +45,9 @@ import java.util.Map; @RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.NACOS_NAMING_CATALOG_CONTEXT) public class CatalogController { - @Autowired - protected ServiceManager serviceManager; - - @Autowired - private CatalogServiceV1Impl catalogServiceV1; - @Autowired private CatalogServiceV2Impl catalogServiceV2; - @Autowired - private UpgradeJudgement upgradeJudgement; - /** * Get service detail. * @@ -156,42 +137,7 @@ public class CatalogController { .pageListService(namespaceId, groupName, serviceName, pageNo, pageSize, containedInstance, hasIpCount); } - /** - * Get response time of service. - * - * @param request http request - * @return response time information - */ - @RequestMapping("/rt/service") - public ObjectNode rt4Service(HttpServletRequest request) throws NacosException { - - String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); - - String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); - - Service service = serviceManager.getService(namespaceId, serviceName); - - serviceManager.checkServiceIsNull(service, namespaceId, serviceName); - - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - - ArrayNode clusters = JacksonUtils.createEmptyArrayNode(); - for (Map.Entry entry : service.getClusterMap().entrySet()) { - ObjectNode packet = JacksonUtils.createEmptyJsonNode(); - HealthCheckTask task = entry.getValue().getHealthCheckTask(); - - packet.put("name", entry.getKey()); - packet.put("checkRTBest", task.getCheckRtBest()); - packet.put("checkRTWorst", task.getCheckRtWorst()); - packet.put("checkRTNormalized", task.getCheckRtNormalized()); - - clusters.add(packet); - } - result.replace("clusters", clusters); - return result; - } - private CatalogService judgeCatalogService() { - return upgradeJudgement.isUseGrpcFeatures() ? catalogServiceV2 : catalogServiceV1; + return catalogServiceV2; } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java index 2cb43270d..35450797f 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java @@ -26,10 +26,8 @@ import com.alibaba.nacos.common.utils.NumberUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.utils.WebUtils; import com.alibaba.nacos.naming.core.ClusterOperator; -import com.alibaba.nacos.naming.core.ClusterOperatorV1Impl; import com.alibaba.nacos.naming.core.ClusterOperatorV2Impl; import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import org.springframework.web.bind.annotation.PutMapping; @@ -47,16 +45,9 @@ import javax.servlet.http.HttpServletRequest; @RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.NACOS_NAMING_CLUSTER_CONTEXT) public class ClusterController { - private final UpgradeJudgement upgradeJudgement; - - private final ClusterOperatorV1Impl clusterOperatorV1; - private final ClusterOperatorV2Impl clusterOperatorV2; - public ClusterController(UpgradeJudgement upgradeJudgement, ClusterOperatorV1Impl clusterOperatorV1, - ClusterOperatorV2Impl clusterOperatorV2) { - this.upgradeJudgement = upgradeJudgement; - this.clusterOperatorV1 = clusterOperatorV1; + public ClusterController(ClusterOperatorV2Impl clusterOperatorV2) { this.clusterOperatorV2 = clusterOperatorV2; } @@ -89,6 +80,6 @@ public class ClusterController { } private ClusterOperator judgeClusterOperator() { - return upgradeJudgement.isUseGrpcFeatures() ? clusterOperatorV2 : clusterOperatorV1; + return clusterOperatorV2; } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/DistroController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/DistroController.java deleted file mode 100644 index 13dbd1f6b..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/DistroController.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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.nacos.naming.controllers; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroHttpData; -import com.alibaba.nacos.naming.consistency.ephemeral.distro.combined.DistroHttpCombinedKey; -import com.alibaba.nacos.core.distributed.distro.DistroProtocol; -import com.alibaba.nacos.core.distributed.distro.entity.DistroData; -import com.alibaba.nacos.core.distributed.distro.entity.DistroKey; -import com.alibaba.nacos.naming.core.Instances; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.fasterxml.jackson.databind.JsonNode; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import java.util.Map; - -/** - * Restful methods for Partition protocol. - * - * @author nkorange - * @since 1.0.0 - */ -@RestController -@RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.NACOS_NAMING_PARTITION_CONTEXT) -public class DistroController { - - @Autowired - private DistroProtocol distroProtocol; - - @Autowired - private ServiceManager serviceManager; - - @Autowired - private SwitchDomain switchDomain; - - /** - * Synchronize datum. - * - * @param dataMap data map - * @return 'ok' if success - * @throws Exception if failed - */ - @PutMapping("/datum") - public ResponseEntity onSyncDatum(@RequestBody Map> dataMap) throws Exception { - - if (dataMap.isEmpty()) { - Loggers.DISTRO.error("[onSync] receive empty entity!"); - throw new NacosException(NacosException.INVALID_PARAM, "receive empty entity!"); - } - - for (Map.Entry> entry : dataMap.entrySet()) { - if (KeyBuilder.matchEphemeralInstanceListKey(entry.getKey())) { - String namespaceId = KeyBuilder.getNamespace(entry.getKey()); - String serviceName = KeyBuilder.getServiceName(entry.getKey()); - if (!serviceManager.containService(namespaceId, serviceName) && switchDomain - .isDefaultInstanceEphemeral()) { - serviceManager.createEmptyService(namespaceId, serviceName, true); - } - DistroHttpData distroHttpData = new DistroHttpData(createDistroKey(entry.getKey()), entry.getValue()); - distroProtocol.onReceive(distroHttpData); - } - } - return ResponseEntity.ok("ok"); - } - - /** - * Checksum. - * - * @param source source server - * @param dataMap checksum map - * @return 'ok' - */ - @PutMapping("/checksum") - public ResponseEntity syncChecksum(@RequestParam String source, @RequestBody Map dataMap) { - DistroHttpData distroHttpData = new DistroHttpData(createDistroKey(source), dataMap); - distroProtocol.onVerify(distroHttpData, source); - return ResponseEntity.ok("ok"); - } - - /** - * Get datum. - * - * @param body keys of data - * @return datum - * @throws Exception if failed - */ - @GetMapping("/datum") - public ResponseEntity get(@RequestBody String body) throws Exception { - - JsonNode bodyNode = JacksonUtils.toObj(body); - String keys = bodyNode.get("keys").asText(); - String keySplitter = ","; - DistroHttpCombinedKey distroKey = new DistroHttpCombinedKey(KeyBuilder.INSTANCE_LIST_KEY_PREFIX, ""); - for (String key : keys.split(keySplitter)) { - distroKey.getActualResourceTypes().add(key); - } - DistroData distroData = distroProtocol.onQuery(distroKey); - return ResponseEntity.ok(distroData.getContent()); - } - - /** - * Get all datums. - * - * @return all datums - */ - @GetMapping("/datums") - public ResponseEntity getAllDatums() { - DistroData distroData = distroProtocol.onSnapshot(KeyBuilder.INSTANCE_LIST_KEY_PREFIX); - return ResponseEntity.ok(distroData.getContent()); - } - - private DistroKey createDistroKey(String resourceKey) { - return new DistroKey(resourceKey, KeyBuilder.INSTANCE_LIST_KEY_PREFIX); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/HealthController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/HealthController.java index 293586d07..434d1920c 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/HealthController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/HealthController.java @@ -27,9 +27,7 @@ import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.utils.WebUtils; import com.alibaba.nacos.naming.core.HealthOperator; -import com.alibaba.nacos.naming.core.HealthOperatorV1Impl; import com.alibaba.nacos.naming.core.HealthOperatorV2Impl; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.alibaba.nacos.naming.monitor.MetricsMonitor; @@ -65,15 +63,9 @@ import static com.alibaba.nacos.naming.constants.RequestConstant.VALID_KEY; @RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.NACOS_NAMING_HEALTH_CONTEXT) public class HealthController { - @Autowired - private HealthOperatorV1Impl healthOperatorV1; - @Autowired private HealthOperatorV2Impl healthOperatorV2; - @Autowired - private UpgradeJudgement upgradeJudgement; - /** * Just a health check. * @@ -137,6 +129,6 @@ public class HealthController { } private HealthOperator getHealthOperator() { - return upgradeJudgement.isUseGrpcFeatures() ? healthOperatorV2 : healthOperatorV1; + return healthOperatorV2; } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceController.java index a40723cea..de09bf9df 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceController.java @@ -33,9 +33,7 @@ import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.utils.WebUtils; import com.alibaba.nacos.naming.core.InstanceOperator; import com.alibaba.nacos.naming.core.InstanceOperatorClientImpl; -import com.alibaba.nacos.naming.core.InstanceOperatorServiceImpl; import com.alibaba.nacos.naming.core.InstancePatchObject; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; import com.alibaba.nacos.naming.healthcheck.RsInfo; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.SwitchDomain; @@ -85,12 +83,6 @@ public class InstanceController { @Autowired private InstanceOperatorClientImpl instanceServiceV2; - @Autowired - private InstanceOperatorServiceImpl instanceServiceV1; - - @Autowired - private UpgradeJudgement upgradeJudgement; - private static final String METADATA = "metadata"; public InstanceController() { @@ -119,9 +111,9 @@ public class InstanceController { .setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build(); getInstanceOperator().registerInstance(namespaceId, serviceName, instance); - NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(), "", - false, namespaceId, NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName), - instance.getIp(), instance.getPort())); + NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(), "", false, namespaceId, + NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName), instance.getIp(), + instance.getPort())); return "ok"; } @@ -143,8 +135,8 @@ public class InstanceController { NamingUtils.checkServiceNameFormat(serviceName); getInstanceOperator().removeInstance(namespaceId, serviceName, instance); - NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(System.currentTimeMillis(), "", - false, DeregisterInstanceReason.REQUEST, namespaceId, NamingUtils.getGroupName(serviceName), + NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(System.currentTimeMillis(), "", false, + DeregisterInstanceReason.REQUEST, namespaceId, NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName), instance.getIp(), instance.getPort())); return "ok"; } @@ -282,10 +274,6 @@ public class InstanceController { if (StringUtils.isNotBlank(metadata)) { patchObject.setMetadata(UtilsAndCommons.parseMetadata(metadata)); } - String app = WebUtils.optional(request, "app", StringUtils.EMPTY); - if (StringUtils.isNotBlank(app)) { - patchObject.setApp(app); - } String weight = WebUtils.optional(request, "weight", StringUtils.EMPTY); if (StringUtils.isNotBlank(weight)) { patchObject.setWeight(Double.parseDouble(weight)); @@ -324,7 +312,7 @@ public class InstanceController { int udpPort = Integer.parseInt(WebUtils.optional(request, "udpPort", "0")); boolean healthyOnly = Boolean.parseBoolean(WebUtils.optional(request, "healthyOnly", "false")); String app = WebUtils.optional(request, "app", StringUtils.EMPTY); - + Subscriber subscriber = new Subscriber(clientIP + ":" + udpPort, agent, app, clientIP, namespaceId, serviceName, udpPort, clusters); return getInstanceOperator().listInstance(namespaceId, serviceName, subscriber, clusters, healthyOnly); @@ -447,6 +435,6 @@ public class InstanceController { } private InstanceOperator getInstanceOperator() { - return upgradeJudgement.isUseGrpcFeatures() ? instanceServiceV2 : instanceServiceV1; + return instanceServiceV2; } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/OperatorController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/OperatorController.java index 15a36926c..63de63b10 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/OperatorController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/OperatorController.java @@ -16,23 +16,15 @@ package com.alibaba.nacos.naming.controllers; -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.auth.annotation.Secured; -import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.core.cluster.Member; -import com.alibaba.nacos.core.cluster.NodeState; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.core.utils.WebUtils; -import com.alibaba.nacos.naming.cluster.ServerListManager; import com.alibaba.nacos.naming.cluster.ServerStatusManager; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftCore; import com.alibaba.nacos.naming.constants.ClientConstants; import com.alibaba.nacos.naming.core.DistroMapper; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.ServiceManager; +import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; import com.alibaba.nacos.naming.misc.Loggers; @@ -41,20 +33,17 @@ import com.alibaba.nacos.naming.misc.SwitchEntry; import com.alibaba.nacos.naming.misc.SwitchManager; import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.alibaba.nacos.naming.monitor.MetricsMonitor; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.sys.env.EnvUtil; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; /** * Operation for operators. @@ -68,10 +57,6 @@ public class OperatorController { private final SwitchManager switchManager; - private final ServerListManager serverListManager; - - private final ServiceManager serviceManager; - private final ServerMemberManager memberManager; private final ServerStatusManager serverStatusManager; @@ -80,21 +65,16 @@ public class OperatorController { private final DistroMapper distroMapper; - private final RaftCore raftCore; - private final ClientManager clientManager; - public OperatorController(SwitchManager switchManager, ServerListManager serverListManager, - ServiceManager serviceManager, ServerMemberManager memberManager, ServerStatusManager serverStatusManager, - SwitchDomain switchDomain, DistroMapper distroMapper, RaftCore raftCore, ClientManager clientManager) { + public OperatorController(SwitchManager switchManager, ServerMemberManager memberManager, + ServerStatusManager serverStatusManager, SwitchDomain switchDomain, DistroMapper distroMapper, + ClientManager clientManager) { this.switchManager = switchManager; - this.serverListManager = serverListManager; - this.serviceManager = serviceManager; this.memberManager = memberManager; this.serverStatusManager = serverStatusManager; this.switchDomain = switchDomain; this.distroMapper = distroMapper; - this.raftCore = raftCore; this.clientManager = clientManager; } @@ -139,13 +119,7 @@ public class OperatorController { */ @GetMapping("/switches") public SwitchDomain switches(HttpServletRequest request) { - if (EnvUtil.isSupportUpgradeFrom1X()) { - return switchDomain; - } - SwitchDomain result = new SwitchDomain(); - result.update(result); - result.setDoubleWriteEnabled(false); - return result; + return switchDomain; } /** @@ -186,6 +160,7 @@ public class OperatorController { int ephemeralIpPortClient = 0; int persistentIpPortClient = 0; int responsibleClientCount = 0; + int responsibleIpCount = 0; for (String clientId : allClientId) { if (clientId.contains(IpPortBasedClient.ID_DELIMITER)) { if (clientId.endsWith(ClientConstants.PERSISTENT_SUFFIX)) { @@ -193,21 +168,18 @@ public class OperatorController { } else { ephemeralIpPortClient += 1; } - } else { + } else { connectionBasedClient += 1; } - if (clientManager.isResponsibleClient(clientManager.getClient(clientId))) { + Client client = clientManager.getClient(clientId); + if (clientManager.isResponsibleClient(client)) { responsibleClientCount += 1; + responsibleIpCount += client.getAllPublishedService().size(); } } - - int responsibleDomCount = serviceManager.getResponsibleServiceCount(); - int responsibleIpCount = serviceManager.getResponsibleInstanceCount(); result.put("serviceCount", MetricsMonitor.getDomCountMonitor().get()); result.put("instanceCount", MetricsMonitor.getIpCountMonitor().get()); result.put("subscribeCount", MetricsMonitor.getSubscriberCount().get()); - result.put("raftNotifyTaskCount", raftCore.getNotifyTaskCount()); - result.put("responsibleServiceCount", responsibleDomCount); result.put("responsibleInstanceCount", responsibleIpCount); result.put("clientCount", allClientId.size()); result.put("connectionBasedClientCount", connectionBasedClient); @@ -220,19 +192,6 @@ public class OperatorController { return result; } - @GetMapping("/distro/server") - public ObjectNode getResponsibleServer4Service( - @RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @RequestParam String serviceName) throws NacosException { - Service service = serviceManager.getService(namespaceId, serviceName); - - serviceManager.checkServiceIsNull(service, namespaceId, serviceName); - - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - result.put("responsibleServer", distroMapper.mapSrv(serviceName)); - return result; - } - @GetMapping("/distro/client") public ObjectNode getResponsibleServer4Client(@RequestParam String ip, @RequestParam String port) { ObjectNode result = JacksonUtils.createEmptyJsonNode(); @@ -241,58 +200,9 @@ public class OperatorController { return result; } - /** - * This interface will be removed in a future release. - * - * @param healthy whether only query health server. - * @return "ok" - * @deprecated 1.3.0 This function will be deleted sometime after version 1.3.0 - */ - @GetMapping("/servers") - public ObjectNode getHealthyServerList(@RequestParam(required = false) boolean healthy) { - - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - if (healthy) { - List healthyMember = memberManager.allMembers().stream() - .filter(member -> member.getState() == NodeState.UP) - .collect(ArrayList::new, ArrayList::add, ArrayList::addAll); - result.replace("servers", JacksonUtils.transferToJsonNode(healthyMember)); - } else { - result.replace("servers", JacksonUtils.transferToJsonNode(memberManager.allMembers())); - } - - return result; - } - - /** - * This interface will be removed in a future release. - * - * @param serverStatus server status - * @return "ok" - * @deprecated 1.3.0 This function will be deleted sometime after version 1.3.0 - */ - @Deprecated - @RequestMapping("/server/status") - public String serverStatus(@RequestParam String serverStatus) { - serverListManager.onReceiveServerStatus(serverStatus); - return "ok"; - } - @PutMapping("/log") public String setLogLevel(@RequestParam String logName, @RequestParam String logLevel) { Loggers.setLogLevel(logName, logLevel); return "ok"; } - - /** - * This interface will be removed in a future release. - * - * @return {@link JsonNode} - * @deprecated 1.3.0 This function will be deleted sometime after version 1.3.0 - */ - @Deprecated - @RequestMapping(value = "/cluster/state", method = RequestMethod.GET) - public JsonNode getClusterStates() { - return JacksonUtils.transferToJsonNode(serviceManager.getMySelfClusterState()); - } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/RaftController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/RaftController.java deleted file mode 100644 index b9ae427c9..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/RaftController.java +++ /dev/null @@ -1,407 +0,0 @@ -/* - * 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.nacos.naming.controllers; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.common.utils.ConcurrentHashSet; -import com.alibaba.nacos.common.utils.IoUtils; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.core.utils.WebUtils; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.consistency.RecordListener; -import com.alibaba.nacos.naming.consistency.persistent.ClusterVersionJudgement; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftConsistencyServiceImpl; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftCore; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeer; -import com.alibaba.nacos.naming.core.Instances; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.misc.NetUtils; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.alibaba.nacos.common.utils.StringUtils; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * Methods for Raft consistency protocol. These methods should only be invoked by Nacos server itself. - * - * @author nkorange - * @since 1.0.0 - * @deprecated will remove in 1.4.x - */ -@Deprecated -@RestController -@RequestMapping({UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.NACOS_NAMING_RAFT_CONTEXT, - UtilsAndCommons.NACOS_SERVER_CONTEXT + UtilsAndCommons.NACOS_NAMING_CONTEXT - + UtilsAndCommons.NACOS_NAMING_RAFT_CONTEXT}) -public class RaftController { - - private final RaftConsistencyServiceImpl raftConsistencyService; - - private final ServiceManager serviceManager; - - private final RaftCore raftCore; - - private final ClusterVersionJudgement versionJudgement; - - public RaftController(RaftConsistencyServiceImpl raftConsistencyService, ServiceManager serviceManager, - RaftCore raftCore, ClusterVersionJudgement versionJudgement) { - this.raftConsistencyService = raftConsistencyService; - this.serviceManager = serviceManager; - this.raftCore = raftCore; - this.versionJudgement = versionJudgement; - } - - /** - * Raft vote api. - * - * @param request http request - * @param response http response - * @return peer information - * @throws Exception exception - */ - @PostMapping("/vote") - public JsonNode vote(HttpServletRequest request, HttpServletResponse response) throws Exception { - if (versionJudgement.allMemberIsNewVersion()) { - throw new IllegalStateException("old raft protocol already stop"); - } - RaftPeer peer = raftCore.receivedVote(JacksonUtils.toObj(WebUtils.required(request, "vote"), RaftPeer.class)); - - return JacksonUtils.transferToJsonNode(peer); - } - - /** - * Beat api. - * - * @param request http request - * @param response http response - * @return peer information - * @throws Exception exception - */ - @PostMapping("/beat") - public JsonNode beat(HttpServletRequest request, HttpServletResponse response) throws Exception { - if (versionJudgement.allMemberIsNewVersion()) { - throw new IllegalStateException("old raft protocol already stop"); - } - String entity = new String(IoUtils.tryDecompress(request.getInputStream()), StandardCharsets.UTF_8); - String value = URLDecoder.decode(entity, "UTF-8"); - value = URLDecoder.decode(value, "UTF-8"); - - JsonNode json = JacksonUtils.toObj(value); - - RaftPeer peer = raftCore.receivedBeat(JacksonUtils.toObj(json.get("beat").asText())); - - return JacksonUtils.transferToJsonNode(peer); - } - - /** - * Get peer information. - * - * @param request http request - * @param response http response - * @return peer information - */ - @GetMapping("/peer") - public JsonNode getPeer(HttpServletRequest request, HttpServletResponse response) { - if (versionJudgement.allMemberIsNewVersion()) { - throw new IllegalStateException("old raft protocol already stop"); - } - List peers = raftCore.getPeers(); - RaftPeer peer = null; - - for (RaftPeer peer1 : peers) { - if (StringUtils.equals(peer1.ip, NetUtils.localServer())) { - peer = peer1; - } - } - - if (peer == null) { - peer = new RaftPeer(); - peer.ip = NetUtils.localServer(); - } - - return JacksonUtils.transferToJsonNode(peer); - } - - /** - * Datum reload request. - * - * @param request http request - * @param response http response - * @return 'ok' if success - * @throws Exception exception - */ - @PutMapping("/datum/reload") - public String reloadDatum(HttpServletRequest request, HttpServletResponse response) throws Exception { - if (versionJudgement.allMemberIsNewVersion()) { - throw new IllegalStateException("old raft protocol already stop"); - } - String key = WebUtils.required(request, "key"); - raftCore.loadDatum(key); - return "ok"; - } - - /** - * Publish datum. - * - * @param request http request - * @param response http response - * @return 'ok' if success - * @throws Exception exception - */ - @PostMapping("/datum") - public String publish(HttpServletRequest request, HttpServletResponse response) throws Exception { - if (versionJudgement.allMemberIsNewVersion()) { - throw new IllegalStateException("old raft protocol already stop"); - } - response.setHeader("Content-Type", "application/json; charset=" + getAcceptEncoding(request)); - response.setHeader("Cache-Control", "no-cache"); - response.setHeader("Content-Encode", "gzip"); - - String entity = IoUtils.toString(request.getInputStream(), "UTF-8"); - String value = URLDecoder.decode(entity, "UTF-8"); - JsonNode json = JacksonUtils.toObj(value); - - String key = json.get("key").asText(); - if (KeyBuilder.matchInstanceListKey(key)) { - raftConsistencyService.put(key, JacksonUtils.toObj(json.get("value").toString(), Instances.class)); - return "ok"; - } - - if (KeyBuilder.matchSwitchKey(key)) { - raftConsistencyService.put(key, JacksonUtils.toObj(json.get("value").toString(), SwitchDomain.class)); - return "ok"; - } - - if (KeyBuilder.matchServiceMetaKey(key)) { - raftConsistencyService.put(key, JacksonUtils.toObj(json.get("value").toString(), Service.class)); - return "ok"; - } - - throw new NacosException(NacosException.INVALID_PARAM, "unknown type publish key: " + key); - } - - /** - * Remove datum. - * - * @param request http request - * @param response http response - * @return 'ok' if success - * @throws Exception exception - */ - @DeleteMapping("/datum") - public String delete(HttpServletRequest request, HttpServletResponse response) throws Exception { - if (versionJudgement.allMemberIsNewVersion()) { - throw new IllegalStateException("old raft protocol already stop"); - } - response.setHeader("Content-Type", "application/json; charset=" + getAcceptEncoding(request)); - response.setHeader("Cache-Control", "no-cache"); - response.setHeader("Content-Encode", "gzip"); - raftConsistencyService.remove(WebUtils.required(request, "key")); - return "ok"; - } - - /** - * Get datum. - * - * @param request http request - * @param response http response - * @return datum - * @throws Exception exception - */ - @GetMapping("/datum") - public String get(HttpServletRequest request, HttpServletResponse response) throws Exception { - if (versionJudgement.allMemberIsNewVersion()) { - throw new IllegalStateException("old raft protocol already stop"); - } - response.setHeader("Content-Type", "application/json; charset=" + getAcceptEncoding(request)); - response.setHeader("Cache-Control", "no-cache"); - response.setHeader("Content-Encode", "gzip"); - String keysString = WebUtils.required(request, "keys"); - keysString = URLDecoder.decode(keysString, "UTF-8"); - String[] keys = keysString.split(","); - List datums = new ArrayList(); - - for (String key : keys) { - Datum datum = raftCore.getDatum(key); - datums.add(datum); - } - - return JacksonUtils.toJson(datums); - } - - /** - * Get state of raft peer. - * - * @param request http request - * @param response http response - * @return datum - * @throws Exception exception - */ - @GetMapping("/state") - public JsonNode state(HttpServletRequest request, HttpServletResponse response) throws Exception { - if (versionJudgement.allMemberIsNewVersion()) { - throw new IllegalStateException("old raft protocol already stop"); - } - response.setHeader("Content-Type", "application/json; charset=" + getAcceptEncoding(request)); - response.setHeader("Cache-Control", "no-cache"); - response.setHeader("Content-Encode", "gzip"); - - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - result.put("services", serviceManager.getServiceCount()); - result.replace("peers", JacksonUtils.transferToJsonNode(raftCore.getPeers())); - - return result; - } - - /** - * Commit publish datum. - * - * @param request http request - * @param response http response - * @return 'ok' if success - * @throws Exception exception - */ - @PostMapping("/datum/commit") - public String onPublish(HttpServletRequest request, HttpServletResponse response) throws Exception { - if (versionJudgement.allMemberIsNewVersion()) { - throw new IllegalStateException("old raft protocol already stop"); - } - response.setHeader("Content-Type", "application/json; charset=" + getAcceptEncoding(request)); - response.setHeader("Cache-Control", "no-cache"); - response.setHeader("Content-Encode", "gzip"); - - String entity = IoUtils.toString(request.getInputStream(), "UTF-8"); - String value = URLDecoder.decode(entity, "UTF-8"); - - JsonNode jsonObject = JacksonUtils.toObj(value); - String key = "key"; - - RaftPeer source = JacksonUtils.toObj(jsonObject.get("source").toString(), RaftPeer.class); - JsonNode datumJson = jsonObject.get("datum"); - - Datum datum = null; - if (KeyBuilder.matchInstanceListKey(datumJson.get(key).asText())) { - datum = JacksonUtils.toObj(jsonObject.get("datum").toString(), new TypeReference>() { - }); - } else if (KeyBuilder.matchSwitchKey(datumJson.get(key).asText())) { - datum = JacksonUtils.toObj(jsonObject.get("datum").toString(), new TypeReference>() { - }); - } else if (KeyBuilder.matchServiceMetaKey(datumJson.get(key).asText())) { - datum = JacksonUtils.toObj(jsonObject.get("datum").toString(), new TypeReference>() { - }); - } - - raftConsistencyService.onPut(datum, source); - return "ok"; - } - - /** - * Commit delete datum. - * - * @param request http request - * @param response http response - * @return 'ok' if success - * @throws Exception exception - */ - @DeleteMapping("/datum/commit") - public String onDelete(HttpServletRequest request, HttpServletResponse response) throws Exception { - if (versionJudgement.allMemberIsNewVersion()) { - throw new IllegalStateException("old raft protocol already stop"); - } - response.setHeader("Content-Type", "application/json; charset=" + getAcceptEncoding(request)); - response.setHeader("Cache-Control", "no-cache"); - response.setHeader("Content-Encode", "gzip"); - - String entity = IoUtils.toString(request.getInputStream(), "UTF-8"); - String value = URLDecoder.decode(entity, "UTF-8"); - value = URLDecoder.decode(value, "UTF-8"); - - JsonNode jsonObject = JacksonUtils.toObj(value); - - Datum datum = JacksonUtils.toObj(jsonObject.get("datum").toString(), Datum.class); - RaftPeer source = JacksonUtils.toObj(jsonObject.get("source").toString(), RaftPeer.class); - - raftConsistencyService.onRemove(datum, source); - return "ok"; - } - - /** - * Elect leader api. - * - * @param request http request - * @param response http response - * @return leader peer information - */ - @GetMapping("/leader") - public JsonNode getLeader(HttpServletRequest request, HttpServletResponse response) { - if (versionJudgement.allMemberIsNewVersion()) { - throw new IllegalStateException("old raft protocol already stop"); - } - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - result.put("leader", JacksonUtils.toJson(raftCore.getLeader())); - return result; - } - - /** - * Get all listeners. - * - * @param request http request - * @param response http response - * @return all listener information - */ - @GetMapping("/listeners") - public JsonNode getAllListeners(HttpServletRequest request, HttpServletResponse response) { - if (versionJudgement.allMemberIsNewVersion()) { - throw new IllegalStateException("old raft protocol already stop"); - } - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - Map> listeners = raftCore.getListeners(); - - ArrayNode listenerArray = JacksonUtils.createEmptyArrayNode(); - for (String key : listeners.keySet()) { - listenerArray.add(key); - } - result.replace("listeners", listenerArray); - - return result; - } - - public static String getAcceptEncoding(HttpServletRequest req) { - String encode = StringUtils.defaultIfEmpty(req.getHeader("Accept-Charset"), "UTF-8"); - encode = encode.contains(",") ? encode.substring(0, encode.indexOf(",")) : encode; - return encode.contains(";") ? encode.substring(0, encode.indexOf(";")) : encode; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceController.java index 6500c3152..0830ae0cb 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceController.java @@ -27,20 +27,14 @@ import com.alibaba.nacos.common.model.RestResultUtils; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.trace.event.naming.DeregisterServiceTraceEvent; import com.alibaba.nacos.common.trace.event.naming.RegisterServiceTraceEvent; -import com.alibaba.nacos.common.utils.IoUtils; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.NumberUtils; import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.core.utils.WebUtils; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.ServiceManager; import com.alibaba.nacos.naming.core.ServiceOperator; -import com.alibaba.nacos.naming.core.ServiceOperatorV1Impl; import com.alibaba.nacos.naming.core.ServiceOperatorV2Impl; import com.alibaba.nacos.naming.core.SubscribeManager; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.alibaba.nacos.naming.pojo.Subscriber; @@ -77,24 +71,12 @@ import java.util.Optional; @RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.NACOS_NAMING_SERVICE_CONTEXT) public class ServiceController { - @Autowired - protected ServiceManager serviceManager; - - @Autowired - private ServerMemberManager memberManager; - @Autowired private SubscribeManager subscribeManager; - @Autowired - private ServiceOperatorV1Impl serviceOperatorV1; - @Autowired private ServiceOperatorV2Impl serviceOperatorV2; - @Autowired - private UpgradeJudgement upgradeJudgement; - @Autowired private SelectorManager selectorManager; @@ -122,8 +104,8 @@ public class ServiceController { serviceMetadata.setExtendData(UtilsAndCommons.parseMetadata(metadata)); serviceMetadata.setEphemeral(false); getServiceOperator().create(namespaceId, serviceName, serviceMetadata); - NotifyCenter.publishEvent(new RegisterServiceTraceEvent(System.currentTimeMillis(), - namespaceId, NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName))); + NotifyCenter.publishEvent(new RegisterServiceTraceEvent(System.currentTimeMillis(), namespaceId, + NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName))); return "ok"; } @@ -141,8 +123,8 @@ public class ServiceController { @RequestParam String serviceName) throws Exception { getServiceOperator().delete(namespaceId, serviceName); - NotifyCenter.publishEvent(new DeregisterServiceTraceEvent(System.currentTimeMillis(), - namespaceId, "", serviceName)); + NotifyCenter.publishEvent( + new DeregisterServiceTraceEvent(System.currentTimeMillis(), namespaceId, "", serviceName)); return "ok"; } @@ -212,25 +194,23 @@ public class ServiceController { /** * Search service names. * - * @param namespaceId namespace - * @param expr search pattern - * @param responsibleOnly whether only search responsible service + * @param namespaceId namespace + * @param expr search pattern * @return search result */ @RequestMapping("/names") @Secured(action = ActionTypes.READ) public ObjectNode searchService(@RequestParam(defaultValue = StringUtils.EMPTY) String namespaceId, - @RequestParam(defaultValue = StringUtils.EMPTY) String expr, - @RequestParam(required = false) boolean responsibleOnly) throws NacosException { + @RequestParam(defaultValue = StringUtils.EMPTY) String expr) throws NacosException { Map> serviceNameMap = new HashMap<>(16); int totalCount = 0; if (StringUtils.isNotBlank(namespaceId)) { - Collection names = getServiceOperator().searchServiceName(namespaceId, expr, responsibleOnly); + Collection names = getServiceOperator().searchServiceName(namespaceId, expr); serviceNameMap.put(namespaceId, names); totalCount = names.size(); } else { for (String each : getServiceOperator().listAllNamespace()) { - Collection names = getServiceOperator().searchServiceName(each, expr, responsibleOnly); + Collection names = getServiceOperator().searchServiceName(each, expr); serviceNameMap.put(each, names); totalCount += names.size(); } @@ -241,94 +221,6 @@ public class ServiceController { return result; } - /** - * Check service status whether latest. - * - * @param request http request - * @return 'ok' if service status if latest, otherwise 'fail' or exception - * @throws Exception exception - * @deprecated will removed after v2.1 - */ - @PostMapping("/status") - @Deprecated - public String serviceStatus(HttpServletRequest request) throws Exception { - - String entity = IoUtils.toString(request.getInputStream(), "UTF-8"); - String value = URLDecoder.decode(entity, "UTF-8"); - JsonNode json = JacksonUtils.toObj(value); - - //format: service1@@checksum@@@service2@@checksum - String statuses = json.get("statuses").asText(); - String serverIp = json.get("clientIP").asText(); - - if (!memberManager.hasMember(serverIp)) { - throw new NacosException(NacosException.INVALID_PARAM, "ip: " + serverIp + " is not in serverlist"); - } - - try { - ServiceManager.ServiceChecksum checksums = JacksonUtils - .toObj(statuses, ServiceManager.ServiceChecksum.class); - if (checksums == null) { - Loggers.SRV_LOG.warn("[DOMAIN-STATUS] receive malformed data: null"); - return "fail"; - } - - for (Map.Entry entry : checksums.serviceName2Checksum.entrySet()) { - if (entry == null || StringUtils.isEmpty(entry.getKey()) || StringUtils.isEmpty(entry.getValue())) { - continue; - } - String serviceName = entry.getKey(); - String checksum = entry.getValue(); - Service service = serviceManager.getService(checksums.namespaceId, serviceName); - - if (service == null) { - continue; - } - - service.recalculateChecksum(); - - if (!checksum.equals(service.getChecksum())) { - if (Loggers.SRV_LOG.isDebugEnabled()) { - Loggers.SRV_LOG.debug("checksum of {} is not consistent, remote: {}, checksum: {}, local: {}", - serviceName, serverIp, checksum, service.getChecksum()); - } - serviceManager.addUpdatedServiceToQueue(checksums.namespaceId, serviceName, serverIp, checksum); - } - } - } catch (Exception e) { - Loggers.SRV_LOG.warn("[DOMAIN-STATUS] receive malformed data: " + statuses, e); - } - - return "ok"; - } - - /** - * Get checksum of one service. - * - * @param request http request - * @return checksum of one service - * @throws Exception exception - * @deprecated will removed after v2.1 - */ - @PutMapping("/checksum") - @Deprecated - public ObjectNode checksum(HttpServletRequest request) throws NacosException { - - String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); - String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); - Service service = serviceManager.getService(namespaceId, serviceName); - - serviceManager.checkServiceIsNull(service, namespaceId, serviceName); - - service.recalculateChecksum(); - - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - - result.put("checksum", service.getChecksum()); - - return result; - } - /** * get subscriber list. * @@ -405,6 +297,6 @@ public class ServiceController { } private ServiceOperator getServiceOperator() { - return upgradeJudgement.isUseGrpcFeatures() ? serviceOperatorV2 : serviceOperatorV1; + return serviceOperatorV2; } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceControllerV2.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceControllerV2.java deleted file mode 100644 index 11bc1d68f..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceControllerV2.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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.nacos.naming.controllers; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.CommonParams; -import com.alibaba.nacos.api.selector.Selector; -import com.alibaba.nacos.auth.annotation.Secured; -import com.alibaba.nacos.common.Beta; -import com.alibaba.nacos.common.model.RestResult; -import com.alibaba.nacos.common.model.RestResultUtils; -import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.trace.event.naming.DeregisterServiceTraceEvent; -import com.alibaba.nacos.common.trace.event.naming.RegisterServiceTraceEvent; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.common.utils.NumberUtils; -import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.core.utils.WebUtils; -import com.alibaba.nacos.naming.core.ServiceOperatorV2Impl; -import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.naming.pojo.ServiceDetailInfo; -import com.alibaba.nacos.naming.pojo.ServiceNameView; -import com.alibaba.nacos.naming.selector.NoneSelector; -import com.alibaba.nacos.naming.selector.SelectorManager; -import com.alibaba.nacos.naming.utils.ServiceUtil; -import com.alibaba.nacos.plugin.auth.constant.ActionTypes; -import com.fasterxml.jackson.databind.JsonNode; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import javax.servlet.http.HttpServletRequest; -import java.net.URLDecoder; -import java.util.Collection; -import java.util.Objects; -import java.util.Optional; - -/** - * Service operation controller. - * - * @author nkorange - */ -@Beta -@RestController -@RequestMapping(UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_SERVICE_CONTEXT) -public class ServiceControllerV2 { - - private final ServiceOperatorV2Impl serviceOperatorV2; - - private final SelectorManager selectorManager; - - public ServiceControllerV2(ServiceOperatorV2Impl serviceOperatorV2, SelectorManager selectorManager) { - this.serviceOperatorV2 = serviceOperatorV2; - this.selectorManager = selectorManager; - } - - /** - * Create a new service. This API will create a persistence service. - * - * @param namespaceId namespace id - * @param serviceName service name - * @param protectThreshold protect threshold - * @param metadata service metadata - * @param selector selector - * @return 'ok' if success - * @throws Exception exception - */ - @PostMapping(value = "/{serviceName}") - @Secured(action = ActionTypes.WRITE) - public RestResult create(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @PathVariable String serviceName, @RequestParam(defaultValue = Constants.DEFAULT_GROUP) String groupName, - @RequestParam(required = false, defaultValue = "false") boolean ephemeral, - @RequestParam(required = false, defaultValue = "0.0F") float protectThreshold, - @RequestParam(defaultValue = StringUtils.EMPTY) String metadata, - @RequestParam(defaultValue = StringUtils.EMPTY) String selector) throws Exception { - ServiceMetadata serviceMetadata = new ServiceMetadata(); - serviceMetadata.setProtectThreshold(protectThreshold); - serviceMetadata.setSelector(parseSelector(selector)); - serviceMetadata.setExtendData(UtilsAndCommons.parseMetadata(metadata)); - serviceMetadata.setEphemeral(ephemeral); - serviceOperatorV2.create(Service.newService(namespaceId, groupName, serviceName, ephemeral), serviceMetadata); - NotifyCenter.publishEvent(new RegisterServiceTraceEvent(System.currentTimeMillis(), - namespaceId, groupName, serviceName)); - return RestResultUtils.success("ok"); - } - - /** - * Remove service. - * - * @param namespaceId namespace - * @param serviceName service name - * @return 'ok' if success - * @throws Exception exception - */ - @DeleteMapping(value = "/{serviceName}") - @Secured(action = ActionTypes.WRITE) - public RestResult remove(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @PathVariable String serviceName, @RequestParam(defaultValue = Constants.DEFAULT_GROUP) String groupName) - throws Exception { - serviceOperatorV2.delete(Service.newService(namespaceId, groupName, serviceName)); - NotifyCenter.publishEvent(new DeregisterServiceTraceEvent(System.currentTimeMillis(), - namespaceId, groupName, serviceName)); - return RestResultUtils.success("ok"); - } - - /** - * Get detail of service. - * - * @param namespaceId namespace - * @param serviceName service name - * @return detail information of service - * @throws NacosException nacos exception - */ - @GetMapping(value = "/{serviceName}") - @Secured(action = ActionTypes.READ) - public RestResult detail( - @RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @PathVariable String serviceName, @RequestParam(defaultValue = Constants.DEFAULT_GROUP) String groupName) - throws Exception { - ServiceDetailInfo result = serviceOperatorV2 - .queryService(Service.newService(namespaceId, groupName, serviceName)); - return RestResultUtils.success(result); - } - - /** - * List all service names. - * - * @param request http request - * @return all service names - * @throws Exception exception - */ - @GetMapping("/list") - @Secured(action = ActionTypes.READ) - public RestResult list(HttpServletRequest request) throws Exception { - final int pageNo = NumberUtils.toInt(WebUtils.required(request, "pageNo")); - final int pageSize = NumberUtils.toInt(WebUtils.required(request, "pageSize")); - String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); - String groupName = WebUtils.optional(request, CommonParams.GROUP_NAME, Constants.DEFAULT_GROUP); - String selectorString = WebUtils.optional(request, "selector", StringUtils.EMPTY); - ServiceNameView result = new ServiceNameView(); - Collection serviceNameList = serviceOperatorV2.listService(namespaceId, groupName, selectorString); - result.setCount(serviceNameList.size()); - result.setServices(ServiceUtil.pageServiceName(pageNo, pageSize, serviceNameList)); - return RestResultUtils.success(result); - - } - - /** - * Update service. - * - * @param namespaceId namespace id - * @param serviceName service name - * @param protectThreshold protect threshold - * @param metadata service metadata - * @param selector selector - * @return 'ok' if success - * @throws Exception exception - */ - @PutMapping(value = "/{serviceName}") - @Secured(action = ActionTypes.WRITE) - public RestResult update(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @PathVariable String serviceName, @RequestParam(defaultValue = Constants.DEFAULT_GROUP) String groupName, - @RequestParam(required = false, defaultValue = "0.0F") float protectThreshold, - @RequestParam(defaultValue = StringUtils.EMPTY) String metadata, - @RequestParam(defaultValue = StringUtils.EMPTY) String selector) throws Exception { - ServiceMetadata serviceMetadata = new ServiceMetadata(); - serviceMetadata.setProtectThreshold(protectThreshold); - serviceMetadata.setExtendData(UtilsAndCommons.parseMetadata(metadata)); - serviceMetadata.setSelector(parseSelector(selector)); - Service service = Service.newService(namespaceId, groupName, serviceName); - serviceOperatorV2.update(service, serviceMetadata); - return RestResultUtils.success("ok"); - } - - private Selector parseSelector(String selectorJsonString) throws Exception { - if (StringUtils.isBlank(selectorJsonString)) { - return new NoneSelector(); - } - - JsonNode selectorJson = JacksonUtils.toObj(URLDecoder.decode(selectorJsonString, "UTF-8")); - String type = Optional.ofNullable(selectorJson.get("type")) - .orElseThrow(() -> new NacosException(NacosException.INVALID_PARAM, "not match any type of selector!")) - .asText(); - String expression = Optional.ofNullable(selectorJson.get("expression")).map(JsonNode::asText).orElse(null); - Selector selector = selectorManager.parseSelector(type, expression); - if (Objects.isNull(selector)) { - throw new NacosException(NacosException.INVALID_PARAM, "not match any type of selector!"); - } - return selector; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/UpgradeOpsController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/UpgradeOpsController.java deleted file mode 100644 index 1089d35a2..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/UpgradeOpsController.java +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright (c) 1999-2021 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.nacos.naming.controllers; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.CommonParams; -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.alibaba.nacos.api.naming.pojo.ServiceInfo; -import com.alibaba.nacos.api.naming.utils.NamingUtils; -import com.alibaba.nacos.api.selector.Selector; -import com.alibaba.nacos.auth.annotation.Secured; -import com.alibaba.nacos.common.utils.ConvertUtils; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.common.utils.NumberUtils; -import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.core.utils.WebUtils; -import com.alibaba.nacos.naming.core.InstanceOperator; -import com.alibaba.nacos.naming.core.InstanceOperatorClientImpl; -import com.alibaba.nacos.naming.core.InstanceOperatorServiceImpl; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.core.ServiceOperator; -import com.alibaba.nacos.naming.core.ServiceOperatorV1Impl; -import com.alibaba.nacos.naming.core.ServiceOperatorV2Impl; -import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; -import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteDelayTaskEngine; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.naming.monitor.MetricsMonitor; -import com.alibaba.nacos.naming.pojo.Subscriber; -import com.alibaba.nacos.naming.pojo.instance.HttpRequestInstanceBuilder; -import com.alibaba.nacos.naming.selector.NoneSelector; -import com.alibaba.nacos.naming.selector.SelectorManager; -import com.alibaba.nacos.naming.utils.ServiceUtil; -import com.alibaba.nacos.naming.web.CanDistro; -import com.alibaba.nacos.plugin.auth.constant.ActionTypes; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.apache.commons.collections.CollectionUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import javax.servlet.http.HttpServletRequest; -import java.net.URLDecoder; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP; -import static com.alibaba.nacos.api.common.Constants.SERVICE_INFO_SPLITER; -import static com.alibaba.nacos.naming.misc.UtilsAndCommons.NAMESPACE_SERVICE_CONNECTOR; - -/** - * Some API for v2 data ops when upgrading to v2. - * - *

Helping to resolve some unexpected problems when upgrading. - * - * @author gengtuo.ygt on 2021/5/14 - * @deprecated will be removed at 2.1.x - */ -@RestController -@RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/upgrade/ops") -public class UpgradeOpsController { - - private final SwitchDomain switchDomain; - - private final ServiceManager serviceManager; - - private final ServiceOperatorV1Impl serviceOperatorV1; - - private final ServiceOperatorV2Impl serviceOperatorV2; - - private final InstanceOperatorServiceImpl instanceServiceV1; - - private final InstanceOperatorClientImpl instanceServiceV2; - - private final ServiceStorage serviceStorage; - - private final DoubleWriteDelayTaskEngine doubleWriteDelayTaskEngine; - - private final UpgradeJudgement upgradeJudgement; - - @Autowired - private SelectorManager selectorManager; - - public UpgradeOpsController(SwitchDomain switchDomain, ServiceManager serviceManager, - ServiceOperatorV1Impl serviceOperatorV1, ServiceOperatorV2Impl serviceOperatorV2, - InstanceOperatorServiceImpl instanceServiceV1, InstanceOperatorClientImpl instanceServiceV2, - ServiceStorage serviceStorage, DoubleWriteDelayTaskEngine doubleWriteDelayTaskEngine, - UpgradeJudgement upgradeJudgement) { - this.switchDomain = switchDomain; - this.serviceManager = serviceManager; - this.serviceOperatorV1 = serviceOperatorV1; - this.serviceOperatorV2 = serviceOperatorV2; - this.instanceServiceV1 = instanceServiceV1; - this.instanceServiceV2 = instanceServiceV2; - this.serviceStorage = serviceStorage; - this.doubleWriteDelayTaskEngine = doubleWriteDelayTaskEngine; - this.upgradeJudgement = upgradeJudgement; - } - - /** - * Get metrics information for upgrading view. - * - * @param json return json format - * @return metrics about services and instances - */ - @GetMapping("/metrics") - public String metrics(@RequestParam(required = false, defaultValue = "false") boolean json) throws NacosException { - ObjectNode result = getMetrics(); - if (json) { - return JacksonUtils.toJson(result); - } else { - StringBuilder sb = new StringBuilder(); - Iterator> fields = result.fields(); - fields.forEachRemaining(e -> { - sb.append(String.format("%-30s = ", e.getKey())); - JsonNode value = e.getValue(); - if (value.isIntegralNumber()) { - sb.append(String.format("%5d", value.longValue())); - } else if (value.isFloatingPointNumber()) { - sb.append(String.format("%.3f", value.doubleValue())); - } else if (value.isTextual()) { - sb.append(value.textValue()); - } else { - sb.append(value.toString()); - } - sb.append('\n'); - }); - return sb.toString(); - } - } - - private ObjectNode getMetrics() throws NacosException { - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - Set serviceNamesV2 = new HashSet<>(); - Set persistentServiceNamesV2 = new HashSet<>(); - Set ephemeralServiceNamesV2 = new HashSet<>(); - int persistentInstanceCountV2 = 0; - int ephemeralInstanceCountV2 = 0; - Set allNamespaces = com.alibaba.nacos.naming.core.v2.ServiceManager.getInstance().getAllNamespaces(); - for (String ns : allNamespaces) { - Set services = com.alibaba.nacos.naming.core.v2.ServiceManager - .getInstance().getSingletons(ns); - for (com.alibaba.nacos.naming.core.v2.pojo.Service service : services) { - String nameWithNs = - service.getNamespace() + NAMESPACE_SERVICE_CONNECTOR + service.getGroupedServiceName(); - serviceNamesV2.add(nameWithNs); - if (service.isEphemeral()) { - ephemeralServiceNamesV2.add(nameWithNs); - } else { - persistentServiceNamesV2.add(nameWithNs); - } - ServiceInfo data = serviceStorage.getPushData(service); - for (com.alibaba.nacos.api.naming.pojo.Instance instance : data.getHosts()) { - if (instance.isEphemeral()) { - ephemeralInstanceCountV2 += 1; - } else { - persistentInstanceCountV2 += 1; - } - } - } - } - result.put("upgraded", upgradeJudgement.isUseGrpcFeatures()); - result.put("isAll20XVersion", upgradeJudgement.isAll20XVersion()); - result.put("isDoubleWriteEnabled", switchDomain.isDoubleWriteEnabled()); - result.put("doubleWriteDelayTaskCount", doubleWriteDelayTaskEngine.size()); - result.put("serviceCountV1", serviceManager.getServiceCount()); - result.put("instanceCountV1", serviceManager.getInstanceCount()); - result.put("serviceCountV2", MetricsMonitor.getDomCountMonitor().get()); - result.put("instanceCountV2", MetricsMonitor.getIpCountMonitor().get()); - result.put("subscribeCountV2", MetricsMonitor.getSubscriberCount().get()); - result.put("responsibleServiceCountV1", serviceManager.getResponsibleServiceCount()); - result.put("responsibleInstanceCountV1", serviceManager.getResponsibleInstanceCount()); - result.put("ephemeralServiceCountV2", ephemeralServiceNamesV2.size()); - result.put("persistentServiceCountV2", persistentServiceNamesV2.size()); - result.put("ephemeralInstanceCountV2", ephemeralInstanceCountV2); - result.put("persistentInstanceCountV2", persistentInstanceCountV2); - - Set serviceNamesV1 = serviceManager.getAllServiceNames().entrySet().stream() - .flatMap(e -> e.getValue().stream().map(name -> { - if (!name.contains(SERVICE_INFO_SPLITER)) { - name = NamingUtils.getGroupedName(name, DEFAULT_GROUP); - } - return e.getKey() + NAMESPACE_SERVICE_CONNECTOR + name; - })).collect(Collectors.toSet()); - result.put("service.V1.not.in.V2", - String.join("\n", (Collection) CollectionUtils.subtract(serviceNamesV1, serviceNamesV2))); - result.put("service.V2.not.in.V1", - String.join("\n", (Collection) CollectionUtils.subtract(serviceNamesV2, serviceNamesV1))); - return result; - } - - /** - * Create a new service. This API will create a persistence service. - * - * @param namespaceId namespace id - * @param serviceName service name - * @param protectThreshold protect threshold - * @param metadata service metadata - * @param selector selector - * @return 'ok' if success - * @throws Exception exception - */ - @PostMapping("/service") - @Secured(action = ActionTypes.WRITE) - public String createService(@RequestParam(defaultValue = "v2", required = false) String ver, - HttpServletRequest request, @RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @RequestParam String serviceName, - @RequestParam(required = false, defaultValue = "0.0F") float protectThreshold, - @RequestParam(defaultValue = StringUtils.EMPTY) String metadata, - @RequestParam(defaultValue = StringUtils.EMPTY) String selector) throws Exception { - ServiceMetadata serviceMetadata = new ServiceMetadata(); - serviceMetadata.setProtectThreshold(protectThreshold); - serviceMetadata.setSelector(parseSelector(selector)); - serviceMetadata.setExtendData(UtilsAndCommons.parseMetadata(metadata)); - boolean ephemeral = ConvertUtils.toBoolean( - WebUtils.optional(request, "ephemeral", String.valueOf(switchDomain.isDefaultInstanceEphemeral()))); - serviceMetadata.setEphemeral(ephemeral); - getServiceOperator(ver).create(namespaceId, serviceName, serviceMetadata); - return "ok"; - } - - /** - * Remove service. - * - * @param namespaceId namespace - * @param serviceName service name - * @return 'ok' if success - * @throws Exception exception - */ - @DeleteMapping("/service") - @Secured(action = ActionTypes.WRITE) - public String removeService(@RequestParam(defaultValue = "v2", required = false) String ver, - @RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @RequestParam String serviceName) throws Exception { - getServiceOperator(ver).delete(namespaceId, serviceName); - return "ok"; - } - - private ServiceOperator getServiceOperator(String ver) { - return "v2".equals(ver) ? serviceOperatorV2 : serviceOperatorV1; - } - - /** - * Get detail of service. - * - * @param namespaceId namespace - * @param serviceName service name - * @return detail information of service - * @throws NacosException nacos exception - */ - @GetMapping("/service") - @Secured(action = ActionTypes.READ) - public ObjectNode detailService(@RequestParam(defaultValue = "v2", required = false) String ver, - @RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @RequestParam String serviceName) throws NacosException { - return getServiceOperator(ver).queryService(namespaceId, serviceName); - } - - /** - * List all service names. - * - * @param request http request - * @return all service names - * @throws Exception exception - */ - @GetMapping("/service/list") - @Secured(action = ActionTypes.READ) - public ObjectNode listService(@RequestParam(defaultValue = "v2", required = false) String ver, - HttpServletRequest request) throws Exception { - final int pageNo = NumberUtils.toInt(WebUtils.required(request, "pageNo")); - final int pageSize = NumberUtils.toInt(WebUtils.required(request, "pageSize")); - String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); - String groupName = WebUtils.optional(request, CommonParams.GROUP_NAME, Constants.DEFAULT_GROUP); - String selectorString = WebUtils.optional(request, "selector", StringUtils.EMPTY); - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - Collection serviceNameList = getServiceOperator(ver) - .listService(namespaceId, groupName, selectorString); - result.put("count", serviceNameList.size()); - result.replace("doms", - JacksonUtils.transferToJsonNode(ServiceUtil.pageServiceName(pageNo, pageSize, serviceNameList))); - return result; - } - - /** - * Update service. - * - * @param request http request - * @return 'ok' if success - * @throws Exception exception - */ - @PutMapping("/service") - @Secured(action = ActionTypes.WRITE) - public String updateService(@RequestParam(defaultValue = "v2", required = false) String ver, - HttpServletRequest request) throws Exception { - String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); - String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); - ServiceMetadata serviceMetadata = new ServiceMetadata(); - serviceMetadata.setProtectThreshold(NumberUtils.toFloat(WebUtils.required(request, "protectThreshold"))); - serviceMetadata.setExtendData( - UtilsAndCommons.parseMetadata(WebUtils.optional(request, "metadata", StringUtils.EMPTY))); - serviceMetadata.setSelector(parseSelector(WebUtils.optional(request, "selector", StringUtils.EMPTY))); - com.alibaba.nacos.naming.core.v2.pojo.Service service = com.alibaba.nacos.naming.core.v2.pojo.Service - .newService(namespaceId, NamingUtils.getGroupName(serviceName), - NamingUtils.getServiceName(serviceName)); - getServiceOperator(ver).update(service, serviceMetadata); - return "ok"; - } - - /** - * Search service names. - * - * @param namespaceId namespace - * @param expr search pattern - * @param responsibleOnly whether only search responsible service - * @return search result - */ - @RequestMapping("/service/names") - @Secured(action = ActionTypes.READ) - public ObjectNode searchService(@RequestParam(defaultValue = "v2", required = false) String ver, - @RequestParam(defaultValue = StringUtils.EMPTY) String namespaceId, - @RequestParam(defaultValue = StringUtils.EMPTY) String expr, - @RequestParam(required = false) boolean responsibleOnly) throws NacosException { - Map> serviceNameMap = new HashMap<>(16); - int totalCount = 0; - ServiceOperator serviceOperator = getServiceOperator(ver); - if (StringUtils.isNotBlank(namespaceId)) { - Collection names = serviceOperator.searchServiceName(namespaceId, expr, responsibleOnly); - serviceNameMap.put(namespaceId, names); - totalCount = names.size(); - } else { - for (String each : serviceOperator.listAllNamespace()) { - Collection names = serviceOperator.searchServiceName(each, expr, responsibleOnly); - serviceNameMap.put(each, names); - totalCount += names.size(); - } - } - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - result.replace("services", JacksonUtils.transferToJsonNode(serviceNameMap)); - result.put("count", totalCount); - return result; - } - - private Selector parseSelector(String selectorJsonString) throws Exception { - if (StringUtils.isBlank(selectorJsonString)) { - return new NoneSelector(); - } - - JsonNode selectorJson = JacksonUtils.toObj(URLDecoder.decode(selectorJsonString, "UTF-8")); - String type = Optional.ofNullable(selectorJson.get("type")) - .orElseThrow(() -> new NacosException(NacosException.INVALID_PARAM, "not match any type of selector!")) - .asText(); - String expression = Optional.ofNullable(selectorJson.get("expression")).map(JsonNode::asText).orElse(null); - Selector selector = selectorManager.parseSelector(type, expression); - if (Objects.isNull(selector)) { - throw new NacosException(NacosException.INVALID_PARAM, "not match any type of selector!"); - } - return selector; - } - - - /** - * Register new instance. - * - * @param request http request - * @return 'ok' if success - * @throws Exception any error during register - */ - @CanDistro - @PostMapping("/instance") - @Secured(action = ActionTypes.WRITE) - public String registerInstance(@RequestParam(defaultValue = "v2", required = false) String ver, - HttpServletRequest request) throws Exception { - - final String namespaceId = WebUtils - .optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); - final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); - NamingUtils.checkServiceNameFormat(serviceName); - - final Instance instance = HttpRequestInstanceBuilder.newBuilder() - .setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build(); - - getInstanceOperator(ver).registerInstance(namespaceId, serviceName, instance); - return "ok"; - } - - /** - * Deregister instances. - * - * @param request http request - * @return 'ok' if success - * @throws Exception any error during deregister - */ - @CanDistro - @DeleteMapping("/instance") - @Secured(action = ActionTypes.WRITE) - public String deregisterInstance(@RequestParam(defaultValue = "v2", required = false) String ver, - HttpServletRequest request) throws Exception { - Instance instance = HttpRequestInstanceBuilder.newBuilder() - .setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build(); - String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); - String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); - NamingUtils.checkServiceNameFormat(serviceName); - - getInstanceOperator(ver).removeInstance(namespaceId, serviceName, instance); - return "ok"; - } - - /** - * Update instance. - * - * @param request http request - * @return 'ok' if success - * @throws Exception any error during update - */ - @CanDistro - @PutMapping("/instance") - @Secured(action = ActionTypes.WRITE) - public String updateInstance(@RequestParam(defaultValue = "v2", required = false) String ver, - HttpServletRequest request) throws Exception { - String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); - String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); - NamingUtils.checkServiceNameFormat(serviceName); - Instance instance = HttpRequestInstanceBuilder.newBuilder() - .setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build(); - getInstanceOperator(ver).updateInstance(namespaceId, serviceName, instance); - return "ok"; - } - - - /** - * Get all instance of input service. - * - * @param request http request - * @return list of instance - * @throws Exception any error during list - */ - @GetMapping("/instance/list") - @Secured(action = ActionTypes.READ) - public Object listInstance(@RequestParam(defaultValue = "v2", required = false) String ver, - HttpServletRequest request) throws Exception { - - String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); - String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); - NamingUtils.checkServiceNameFormat(serviceName); - - String agent = WebUtils.getUserAgent(request); - String clusters = WebUtils.optional(request, "clusters", StringUtils.EMPTY); - String clientIP = WebUtils.optional(request, "clientIP", StringUtils.EMPTY); - int udpPort = Integer.parseInt(WebUtils.optional(request, "udpPort", "0")); - boolean healthyOnly = Boolean.parseBoolean(WebUtils.optional(request, "healthyOnly", "false")); - - boolean isCheck = Boolean.parseBoolean(WebUtils.optional(request, "isCheck", "false")); - - String app = WebUtils.optional(request, "app", StringUtils.EMPTY); - String env = WebUtils.optional(request, "env", StringUtils.EMPTY); - String tenant = WebUtils.optional(request, "tid", StringUtils.EMPTY); - - Subscriber subscriber = new Subscriber(clientIP + ":" + udpPort, agent, app, clientIP, namespaceId, serviceName, - udpPort, clusters); - return getInstanceOperator(ver).listInstance(namespaceId, serviceName, subscriber, clusters, healthyOnly); - } - - /** - * Get detail information of specified instance. - * - * @param request http request - * @return detail information of instance - * @throws Exception any error during get - */ - @GetMapping("/instance") - @Secured(action = ActionTypes.READ) - public ObjectNode detailInstance(@RequestParam(defaultValue = "v2", required = false) String ver, - HttpServletRequest request) throws Exception { - - String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); - String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); - NamingUtils.checkServiceNameFormat(serviceName); - String cluster = WebUtils.optional(request, CommonParams.CLUSTER_NAME, UtilsAndCommons.DEFAULT_CLUSTER_NAME); - String ip = WebUtils.required(request, "ip"); - int port = Integer.parseInt(WebUtils.required(request, "port")); - - com.alibaba.nacos.api.naming.pojo.Instance instance = getInstanceOperator(ver) - .getInstance(namespaceId, serviceName, cluster, ip, port); - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - result.put("service", serviceName); - result.put("ip", ip); - result.put("port", port); - result.put("clusterName", cluster); - result.put("weight", instance.getWeight()); - result.put("healthy", instance.isHealthy()); - result.put("instanceId", instance.getInstanceId()); - result.set("metadata", JacksonUtils.transferToJsonNode(instance.getMetadata())); - return result; - } - - private InstanceOperator getInstanceOperator(String ver) { - return "v2".equals(ver) ? instanceServiceV2 : instanceServiceV1; - } - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/ClientInfoControllerV2.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/ClientInfoControllerV2.java new file mode 100644 index 000000000..8f2abcf07 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/ClientInfoControllerV2.java @@ -0,0 +1,252 @@ +/* + * Copyright 1999-2022 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.nacos.naming.controllers.v2; + +import com.alibaba.nacos.api.annotation.NacosApi; +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.core.remote.Connection; +import com.alibaba.nacos.core.remote.ConnectionManager; +import com.alibaba.nacos.core.remote.ConnectionMeta; +import com.alibaba.nacos.naming.core.v2.client.Client; +import com.alibaba.nacos.naming.core.v2.client.impl.ConnectionBasedClient; +import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; +import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; +import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.pojo.Subscriber; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * ClientInfoControllerV2. + * @author dongyafei + * @date 2022/9/20 + */ + +@NacosApi +@RestController +@RequestMapping(UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_CLIENT_CONTEXT) +public class ClientInfoControllerV2 { + + private final ClientManager clientManager; + + private final ConnectionManager connectionManager; + + private final ClientServiceIndexesManager clientServiceIndexesManager; + + public ClientInfoControllerV2(ClientManager clientManager, ConnectionManager connectionManager, + ClientServiceIndexesManager clientServiceIndexesManager) { + this.clientManager = clientManager; + this.connectionManager = connectionManager; + this.clientServiceIndexesManager = clientServiceIndexesManager; + } + + /** + * Query all clients. + */ + @GetMapping("/list") + public Result> getClientList() throws NacosApiException { + return Result.success(new ArrayList<>(clientManager.allClientId())); + } + + /** + * Query client by clientId. + * @param clientId clientId + */ + @GetMapping() + public Result getClientDetail(@RequestParam("clientId") String clientId) throws NacosApiException { + checkClientId(clientId); + Client client = clientManager.getClient(clientId); + + ObjectNode result = JacksonUtils.createEmptyJsonNode(); + result.put("clientId", client.getClientId()); + result.put("ephemeral", client.isEphemeral()); + result.put("lastUpdatedTime", client.getLastUpdatedTime()); + + if (client instanceof ConnectionBasedClient) { + // 2.x client + result.put("clientType", "connection"); + Connection connection = connectionManager.getConnection(clientId); + ConnectionMeta connectionMetaInfo = connection.getMetaInfo(); + result.put("connectType", connectionMetaInfo.getConnectType()); + result.put("appName", connectionMetaInfo.getAppName()); + result.put("version", connectionMetaInfo.getVersion()); + result.put("clientIp", connectionMetaInfo.getClientIp()); + result.put("clientPort", clientId.substring(clientId.lastIndexOf('_') + 1)); + } else if (client instanceof IpPortBasedClient) { + // 1.x client + result.put("clientType", "ipPort"); + IpPortBasedClient ipPortBasedClient = (IpPortBasedClient) client; + String responsibleId = ipPortBasedClient.getResponsibleId(); + int idx = responsibleId.lastIndexOf(':'); + result.put("clientIp", responsibleId.substring(0, idx)); + result.put("clientPort", responsibleId.substring(idx + 1)); + } + return Result.success(result); + } + + /** + * Query the services registered by the specified client. + * @param clientId clientId + */ + @GetMapping("/publish/list") + public Result> getPublishedServiceList(@RequestParam("clientId") String clientId) throws NacosApiException { + checkClientId(clientId); + Client client = clientManager.getClient(clientId); + Collection allPublishedService = client.getAllPublishedService(); + ArrayList res = new ArrayList<>(); + for (Service service : allPublishedService) { + ObjectNode item = JacksonUtils.createEmptyJsonNode(); + item.put("namespace", service.getNamespace()); + item.put("group", service.getGroup()); + item.put("serviceName", service.getName()); + InstancePublishInfo instancePublishInfo = client.getInstancePublishInfo(service); + ObjectNode instanceInfo = JacksonUtils.createEmptyJsonNode(); + instanceInfo.put("ip", instancePublishInfo.getIp()); + instanceInfo.put("port", instancePublishInfo.getPort()); + instanceInfo.put("cluster", instancePublishInfo.getCluster()); + item.set("registeredInstance", instanceInfo); + res.add(item); + } + return Result.success(res); + } + + /** + * Query the services to which the specified client subscribes. + * @param clientId clientId. + */ + @GetMapping("/subscribe/list") + public Result> getSubscribeServiceList(@RequestParam("clientId") String clientId) throws NacosApiException { + checkClientId(clientId); + Client client = clientManager.getClient(clientId); + Collection allSubscribeService = client.getAllSubscribeService(); + ArrayList res = new ArrayList<>(); + for (Service service : allSubscribeService) { + ObjectNode item = JacksonUtils.createEmptyJsonNode(); + item.put("namespace", service.getNamespace()); + item.put("group", service.getGroup()); + item.put("serviceName", service.getName()); + Subscriber subscriber = client.getSubscriber(service); + ObjectNode subscriberInfo = JacksonUtils.createEmptyJsonNode(); + subscriberInfo.put("app", subscriber.getApp()); + subscriberInfo.put("agent", subscriber.getAgent()); + subscriberInfo.put("addr", subscriber.getAddrStr()); + item.set("subscriberInfo", subscriberInfo); + res.add(item); + } + return Result.success(res); + } + + /** + * Query the clients that have registered the specified service. + * @param namespaceId namespaceId + * @param groupName groupName + * @param ephemeral ephemeral + * @param serviceName serviceName + * @param ip ip + * @param port port + * @return client info + */ + @GetMapping("/service/publisher/list") + public Result> getPublishedClientList( + @RequestParam(value = "namespaceId", required = false, defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, + @RequestParam(value = "groupName", required = false, defaultValue = Constants.DEFAULT_GROUP) String groupName, + @RequestParam(value = "ephemeral", required = false, defaultValue = "true") Boolean ephemeral, + @RequestParam("serviceName") String serviceName, @RequestParam(value = "ip", required = false) String ip, + @RequestParam(value = "port", required = false) Integer port) { + Service service = Service.newService(namespaceId, groupName, serviceName, ephemeral); + Collection allClientsRegisteredService = clientServiceIndexesManager + .getAllClientsRegisteredService(service); + ArrayList res = new ArrayList<>(); + for (String clientId : allClientsRegisteredService) { + Client client = clientManager.getClient(clientId); + InstancePublishInfo instancePublishInfo = client.getInstancePublishInfo(service); + if ((!StringUtils.isBlank(ip)) && (!ip.equals(instancePublishInfo.getIp()))) { + continue; + } + if ((port != null) && (!port.equals(instancePublishInfo.getPort()))) { + continue; + } + ObjectNode item = JacksonUtils.createEmptyJsonNode(); + item.put("clientId", clientId); + item.put("ip", instancePublishInfo.getIp()); + item.put("port", instancePublishInfo.getPort()); + res.add(item); + } + return Result.success(res); + } + + /** + * Query the clients that are subscribed to the specified service. + * @param namespaceId namespaceId + * @param groupName groupName + * @param ephemeral ephemeral + * @param serviceName serviceName + * @param ip ip + * @param port port + * @return client info + */ + @GetMapping("/service/subscriber/list") + public Result> getSubscribeClientList( + @RequestParam(value = "namespaceId", required = false, defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, + @RequestParam(value = "groupName", required = false, defaultValue = Constants.DEFAULT_GROUP) String groupName, + @RequestParam(value = "ephemeral", required = false, defaultValue = "true") Boolean ephemeral, + @RequestParam("serviceName") String serviceName, @RequestParam(value = "ip", required = false) String ip, + @RequestParam(value = "port", required = false) Integer port) { + Service service = Service.newService(namespaceId, groupName, serviceName, ephemeral); + Collection allClientsSubscribeService = clientServiceIndexesManager + .getAllClientsSubscribeService(service); + ArrayList res = new ArrayList<>(); + for (String clientId : allClientsSubscribeService) { + Client client = clientManager.getClient(clientId); + Subscriber subscriber = client.getSubscriber(service); + if ((!StringUtils.isBlank(ip)) && (!ip.equals(subscriber.getIp()))) { + continue; + } + if ((port != null) && (!port.equals(subscriber.getPort()))) { + continue; + } + ObjectNode item = JacksonUtils.createEmptyJsonNode(); + item.put("clientId", clientId); + item.put("ip", subscriber.getIp()); + item.put("port", subscriber.getPort()); + res.add(item); + } + return Result.success(res); + } + + private void checkClientId(String clientId) throws NacosApiException { + if (!clientManager.contains(clientId)) { + throw new NacosApiException(HttpStatus.NOT_FOUND.value(), ErrorCode.RESOURCE_NOT_FOUND, "clientId [ " + clientId + " ] not exist"); + } + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/HealthControllerV2.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/HealthControllerV2.java new file mode 100644 index 000000000..4173b4d75 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/HealthControllerV2.java @@ -0,0 +1,67 @@ +/* + * Copyright 1999-2022 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.nacos.naming.controllers.v2; + +import com.alibaba.nacos.api.annotation.NacosApi; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.naming.core.HealthOperatorV2Impl; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.model.form.UpdateHealthForm; +import com.alibaba.nacos.naming.web.CanDistro; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * HealthControllerV2. + * @author dongyafei + * @date 2022/9/15 + */ +@NacosApi +@RestController +@RequestMapping(UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_HEALTH_CONTEXT) +public class HealthControllerV2 { + + @Autowired + private HealthOperatorV2Impl healthOperatorV2; + + /** + * Update health check for instance. + * + * @param updateHealthForm updateHealthForm + * @return 'ok' if success + */ + @CanDistro + @PutMapping(value = {"", "/instance"}) + @Secured(action = ActionTypes.WRITE) + public Result update(UpdateHealthForm updateHealthForm) throws NacosException { + updateHealthForm.validate(); + healthOperatorV2.updateHealthStatusForPersistentInstance(updateHealthForm.getNamespaceId(), buildCompositeServiceName(updateHealthForm), + updateHealthForm.getClusterName(), updateHealthForm.getIp(), updateHealthForm.getPort(), + updateHealthForm.getHealthy()); + return Result.success("ok"); + } + + private String buildCompositeServiceName(UpdateHealthForm updateHealthForm) { + return NamingUtils.getGroupedName(updateHealthForm.getServiceName(), updateHealthForm.getGroupName()); + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceControllerV2.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/InstanceControllerV2.java similarity index 53% rename from naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceControllerV2.java rename to naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/InstanceControllerV2.java index c09f11f55..709ca3fc1 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceControllerV2.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/InstanceControllerV2.java @@ -14,22 +14,27 @@ * limitations under the License. */ -package com.alibaba.nacos.naming.controllers; +package com.alibaba.nacos.naming.controllers.v2; +import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.api.naming.CommonParams; import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.pojo.builder.InstanceBuilder; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.trace.DeregisterInstanceReason; import com.alibaba.nacos.common.trace.event.naming.DeregisterInstanceTraceEvent; import com.alibaba.nacos.common.trace.event.naming.RegisterInstanceTraceEvent; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.core.utils.WebUtils; import com.alibaba.nacos.naming.core.InstanceOperatorClientImpl; import com.alibaba.nacos.naming.core.InstancePatchObject; import com.alibaba.nacos.naming.healthcheck.RsInfo; @@ -37,6 +42,10 @@ import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.naming.misc.SwitchEntry; import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.model.form.InstanceForm; +import com.alibaba.nacos.naming.model.form.InstanceMetadataBatchOperationForm; +import com.alibaba.nacos.naming.model.vo.InstanceDetailInfoVo; +import com.alibaba.nacos.naming.model.vo.InstanceMetadataBatchOperationVo; import com.alibaba.nacos.naming.pojo.InstanceOperationInfo; import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.pojo.instance.BeatInfoInstanceBuilder; @@ -47,17 +56,19 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -69,6 +80,7 @@ import static com.alibaba.nacos.naming.misc.UtilsAndCommons.DEFAULT_CLUSTER_NAME * * @author hujun */ +@NacosApi @RestController @RequestMapping(UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT) public class InstanceControllerV2 { @@ -81,192 +93,95 @@ public class InstanceControllerV2 { /** * Register new instance. - * - * @param namespaceId namespace id - * @param serviceName service name - * @param metadata service metadata - * @param cluster service cluster - * @param ip instance ip - * @param port instance port - * @param healthy instance healthy - * @param weight instance weight - * @param enabled instance enabled - * @param ephemeral instance ephemeral - * @return 'ok' if success - * @throws Exception any error during register */ @CanDistro @PostMapping @Secured(action = ActionTypes.WRITE) - public String register(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @RequestParam String serviceName, @RequestParam String ip, - @RequestParam(defaultValue = UtilsAndCommons.DEFAULT_CLUSTER_NAME) String cluster, - @RequestParam Integer port, @RequestParam(defaultValue = "true") Boolean healthy, - @RequestParam(defaultValue = "1") Double weight, @RequestParam(defaultValue = "true") Boolean enabled, - @RequestParam String metadata, @RequestParam Boolean ephemeral) throws Exception { - - NamingUtils.checkServiceNameFormat(serviceName); - checkWeight(weight); - final Instance instance = InstanceBuilder.newBuilder().setServiceName(serviceName).setIp(ip) - .setClusterName(cluster).setPort(port).setHealthy(healthy).setWeight(weight).setEnabled(enabled) - .setMetadata(UtilsAndCommons.parseMetadata(metadata)).setEphemeral(ephemeral).build(); - if (ephemeral == null) { - instance.setEphemeral((switchDomain.isDefaultInstanceEphemeral())); - } - instanceServiceV2.registerInstance(namespaceId, serviceName, instance); + public Result register(InstanceForm instanceForm) throws NacosException { + // check param + instanceForm.validate(); + checkWeight(instanceForm.getWeight()); + // build instance + Instance instance = buildInstance(instanceForm); + instanceServiceV2.registerInstance(instanceForm.getNamespaceId(), buildCompositeServiceName(instanceForm), instance); NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(), "", - false, namespaceId, NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName), + false, instanceForm.getNamespaceId(), instanceForm.getGroupName(), instanceForm.getServiceName(), instance.getIp(), instance.getPort())); - return "ok"; + return Result.success("ok"); } /** * Deregister instances. - * - * @param namespaceId namespace id - * @param serviceName service name - * @param metadata service metadata - * @param cluster service cluster - * @param ip instance ip - * @param port instance port - * @param healthy instance healthy - * @param weight instance weight - * @param enabled instance enabled - * @param ephemeral instance ephemeral - * @return 'ok' if success - * @throws Exception any error during deregister */ @CanDistro @DeleteMapping @Secured(action = ActionTypes.WRITE) - public String deregister(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @RequestParam String serviceName, @RequestParam String ip, - @RequestParam(defaultValue = UtilsAndCommons.DEFAULT_CLUSTER_NAME) String cluster, - @RequestParam Integer port, @RequestParam(defaultValue = "true") Boolean healthy, - @RequestParam(defaultValue = "1") Double weight, @RequestParam(defaultValue = "true") Boolean enabled, - @RequestParam String metadata, @RequestParam Boolean ephemeral) throws Exception { - NamingUtils.checkServiceNameFormat(serviceName); - checkWeight(weight); - final Instance instance = InstanceBuilder.newBuilder().setServiceName(serviceName).setIp(ip) - .setClusterName(cluster).setPort(port).setHealthy(healthy).setWeight(weight).setEnabled(enabled) - .setMetadata(UtilsAndCommons.parseMetadata(metadata)).setEphemeral(ephemeral).build(); - if (ephemeral == null) { - instance.setEphemeral((switchDomain.isDefaultInstanceEphemeral())); - } - - instanceServiceV2.removeInstance(namespaceId, serviceName, instance); + public Result deregister(InstanceForm instanceForm) throws NacosException { + // check param + instanceForm.validate(); + checkWeight(instanceForm.getWeight()); + // build instance + Instance instance = buildInstance(instanceForm); + instanceServiceV2.removeInstance(instanceForm.getNamespaceId(), buildCompositeServiceName(instanceForm), instance); NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(System.currentTimeMillis(), "", - false, DeregisterInstanceReason.REQUEST, namespaceId, NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName), - instance.getIp(), instance.getPort())); - return "ok"; + false, DeregisterInstanceReason.REQUEST, instanceForm.getNamespaceId(), instanceForm.getGroupName(), + instanceForm.getServiceName(), instance.getIp(), instance.getPort())); + return Result.success("ok"); } /** * Update instance. - * - * @param namespaceId namespace id - * @param serviceName service name - * @param metadata service metadata - * @param cluster service cluster - * @param ip instance ip - * @param port instance port - * @param healthy instance healthy - * @param weight instance weight - * @param enabled instance enabled - * @param ephemeral instance ephemeral - * @return 'ok' if success - * @throws Exception any error during update */ @CanDistro @PutMapping @Secured(action = ActionTypes.WRITE) - public String update(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @RequestParam String serviceName, @RequestParam String ip, - @RequestParam(defaultValue = UtilsAndCommons.DEFAULT_CLUSTER_NAME) String cluster, - @RequestParam Integer port, @RequestParam(defaultValue = "true") Boolean healthy, - @RequestParam(defaultValue = "1") Double weight, @RequestParam(defaultValue = "true") Boolean enabled, - @RequestParam String metadata, @RequestParam Boolean ephemeral) throws Exception { - - NamingUtils.checkServiceNameFormat(serviceName); - checkWeight(weight); - final Instance instance = InstanceBuilder.newBuilder().setServiceName(serviceName).setIp(ip) - .setClusterName(cluster).setPort(port).setHealthy(healthy).setWeight(weight).setEnabled(enabled) - .setMetadata(UtilsAndCommons.parseMetadata(metadata)).setEphemeral(ephemeral).build(); - if (ephemeral == null) { - instance.setEphemeral((switchDomain.isDefaultInstanceEphemeral())); - } - instanceServiceV2.updateInstance(namespaceId, serviceName, instance); - return "ok"; + public Result update(InstanceForm instanceForm) throws NacosException { + // check param + instanceForm.validate(); + checkWeight(instanceForm.getWeight()); + // build instance + Instance instance = buildInstance(instanceForm); + instanceServiceV2.updateInstance(instanceForm.getNamespaceId(), buildCompositeServiceName(instanceForm), instance); + return Result.success("ok"); } /** * Batch update instance's metadata. old key exist = update, old key not exist = add. - * - * @param namespaceId namespace id - * @param serviceName service name - * @param metadata service metadata - * @param consistencyType consistencyType - * @param instances instances info - * @return success updated instances. such as '{"updated":["2.2.2.2:8080:unknown:xxxx-cluster:ephemeral"}'. - * @throws Exception any error during update - * @since 1.4.0 */ @CanDistro @PutMapping(value = "/metadata/batch") @Secured(action = ActionTypes.WRITE) - public ObjectNode batchUpdateInstanceMetadata( - @RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @RequestParam String serviceName, @RequestParam(defaultValue = "") String consistencyType, - @RequestParam(defaultValue = "") String instances, @RequestParam String metadata) throws Exception { - - List targetInstances = parseBatchInstances(instances); - Map targetMetadata = UtilsAndCommons.parseMetadata(metadata); - InstanceOperationInfo instanceOperationInfo = buildOperationInfo(serviceName, consistencyType, targetInstances); + public Result batchUpdateInstanceMetadata(InstanceMetadataBatchOperationForm form) + throws NacosException { + form.validate(); + List targetInstances = parseBatchInstances(form.getInstances()); + Map targetMetadata = UtilsAndCommons.parseMetadata(form.getMetadata()); + InstanceOperationInfo instanceOperationInfo = buildOperationInfo(buildCompositeServiceName(form), form.getConsistencyType(), targetInstances); + List operatedInstances = instanceServiceV2 - .batchUpdateMetadata(namespaceId, instanceOperationInfo, targetMetadata); - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - ArrayNode ipArray = JacksonUtils.createEmptyArrayNode(); - for (String ip : operatedInstances) { - ipArray.add(ip); - } - result.replace("updated", ipArray); - return result; + .batchUpdateMetadata(form.getNamespaceId(), instanceOperationInfo, targetMetadata); + + ArrayList ipList = new ArrayList<>(operatedInstances); + return Result.success(new InstanceMetadataBatchOperationVo(ipList)); } /** * Batch delete instance's metadata. old key exist = delete, old key not exist = not operate - * - * @param namespaceId namespace id - * @param serviceName service name - * @param metadata service metadata - * @param consistencyType consistencyType - * @param instances instances info - * @return success updated instances. such as '{"updated":["2.2.2.2:8080:unknown:xxxx-cluster:ephemeral"}'. - * @throws Exception any error during update - * @since 1.4.0 */ @CanDistro @DeleteMapping("/metadata/batch") @Secured(action = ActionTypes.WRITE) - public ObjectNode batchDeleteInstanceMetadata( - @RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @RequestParam String serviceName, @RequestParam(defaultValue = "") String consistencyType, - @RequestParam(defaultValue = "") String instances, @RequestParam String metadata) throws Exception { - - List targetInstances = parseBatchInstances(instances); - Map targetMetadata = UtilsAndCommons.parseMetadata(metadata); - InstanceOperationInfo instanceOperationInfo = buildOperationInfo(serviceName, consistencyType, targetInstances); + public Result batchDeleteInstanceMetadata(InstanceMetadataBatchOperationForm form) + throws NacosException { + form.validate(); + List targetInstances = parseBatchInstances(form.getInstances()); + Map targetMetadata = UtilsAndCommons.parseMetadata(form.getMetadata()); + InstanceOperationInfo instanceOperationInfo = buildOperationInfo(buildCompositeServiceName(form), form.getConsistencyType(), targetInstances); List operatedInstances = instanceServiceV2 - .batchDeleteMetadata(namespaceId, instanceOperationInfo, targetMetadata); - - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - ArrayNode ipArray = JacksonUtils.createEmptyArrayNode(); - for (String ip : operatedInstances) { - ipArray.add(ip); - } - result.replace("updated", ipArray); - return result; + .batchDeleteMetadata(form.getNamespaceId(), instanceOperationInfo, targetMetadata); + ArrayList ipList = new ArrayList<>(operatedInstances); + return Result.success(new InstanceMetadataBatchOperationVo(ipList)); } private InstanceOperationInfo buildOperationInfo(String serviceName, String consistencyType, @@ -333,66 +248,72 @@ public class InstanceControllerV2 { /** * Get all instance of input service. - * - * @param namespaceId namespace id - * @param serviceName service name - * @param clusters service clusters - * @param clientIP service clientIP - * @param udpPort udpPort - * @param healthyOnly healthyOnly - * @param app app - * @param request http request - * @return list of instance - * @throws Exception any error during list + * @param namespaceId namespace id + * @param groupName group name + * @param serviceName service name + * @param clusterName service clusterName + * @param ip ip + * @param port port + * @param healthyOnly healthyOnly + * @param app app + * @param userAgent [header] userAgent + * @param clientVersion [header] clientVersion */ @GetMapping("/list") @Secured(action = ActionTypes.READ) - public Object list(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @RequestParam String serviceName, @RequestParam(defaultValue = StringUtils.EMPTY) String clusters, - @RequestParam(defaultValue = StringUtils.EMPTY) String clientIP, - @RequestParam(defaultValue = "0") Integer udpPort, - @RequestParam(defaultValue = "false") Boolean healthyOnly, - @RequestParam(defaultValue = StringUtils.EMPTY) String app, HttpServletRequest request) throws Exception { - - NamingUtils.checkServiceNameFormat(serviceName); - String agent = WebUtils.getUserAgent(request); - Subscriber subscriber = new Subscriber(clientIP + ":" + udpPort, agent, app, clientIP, namespaceId, serviceName, - udpPort, clusters); - return instanceServiceV2.listInstance(namespaceId, serviceName, subscriber, clusters, healthyOnly); + public Result list(@RequestParam(value = "namespaceId", defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, + @RequestParam(value = "groupName", defaultValue = Constants.DEFAULT_GROUP) String groupName, + @RequestParam("serviceName") String serviceName, + @RequestParam(value = "clusterName", defaultValue = StringUtils.EMPTY) String clusterName, + @RequestParam(value = "ip", defaultValue = StringUtils.EMPTY) String ip, + @RequestParam(value = "port", defaultValue = "0") Integer port, + @RequestParam(value = "healthyOnly", defaultValue = "false") Boolean healthyOnly, + @RequestParam(value = "app", defaultValue = StringUtils.EMPTY) String app, + @RequestHeader(value = HttpHeaderConsts.USER_AGENT_HEADER, required = false) String userAgent, + @RequestHeader(value = HttpHeaderConsts.CLIENT_VERSION_HEADER, required = false) String clientVersion) + throws NacosApiException { + if (StringUtils.isEmpty(userAgent)) { + userAgent = StringUtils.defaultIfEmpty(clientVersion, StringUtils.EMPTY); + } + String compositeServiceName = NamingUtils.getGroupedName(serviceName, groupName); + Subscriber subscriber = new Subscriber(ip + ":" + port, userAgent, app, ip, namespaceId, compositeServiceName, + port, clusterName); + return Result.success(instanceServiceV2.listInstance(namespaceId, compositeServiceName, subscriber, clusterName, healthyOnly)); } - /** * Get detail information of specified instance. * * @param namespaceId service namespaceId * @param serviceName service serviceName - * @param ip instance ip * @param clusterName service clusterName + * @param ip instance ip * @param port instance port * @return detail information of instance - * @throws Exception any error during get + * @throws NacosException any error during get */ @GetMapping @Secured(action = ActionTypes.READ) - public ObjectNode detail(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, - @RequestParam String serviceName, @RequestParam String ip, - @RequestParam(defaultValue = UtilsAndCommons.DEFAULT_CLUSTER_NAME) String clusterName, - @RequestParam Integer port) throws Exception { + public Result detail(@RequestParam(value = "namespaceId", defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, + @RequestParam(value = "groupName", defaultValue = Constants.DEFAULT_GROUP) String groupName, + @RequestParam("serviceName") String serviceName, + @RequestParam(value = "clusterName", defaultValue = UtilsAndCommons.DEFAULT_CLUSTER_NAME) String clusterName, + @RequestParam("ip") String ip, @RequestParam("port") Integer port) throws NacosException { + + String compositeServiceName = NamingUtils.getGroupedName(serviceName, groupName); + + Instance instance = instanceServiceV2.getInstance(namespaceId, compositeServiceName, clusterName, ip, port); - NamingUtils.checkServiceNameFormat(serviceName); - - Instance instance = instanceServiceV2.getInstance(namespaceId, serviceName, clusterName, ip, port); - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - result.put("service", serviceName); - result.put("ip", ip); - result.put("port", port); - result.put("clusterName", clusterName); - result.put("weight", instance.getWeight()); - result.put("healthy", instance.isHealthy()); - result.put("instanceId", instance.getInstanceId()); - result.set("metadata", JacksonUtils.transferToJsonNode(instance.getMetadata())); - return result; + InstanceDetailInfoVo instanceDetailInfoVo = new InstanceDetailInfoVo(); + instanceDetailInfoVo.setServiceName(compositeServiceName); + instanceDetailInfoVo.setIp(ip); + instanceDetailInfoVo.setPort(port); + instanceDetailInfoVo.setClusterName(clusterName); + instanceDetailInfoVo.setWeight(instance.getWeight()); + instanceDetailInfoVo.setHealthy(instance.isHealthy()); + instanceDetailInfoVo.setInstanceId(instance.getInstanceId()); + instanceDetailInfoVo.setMetadata(instance.getMetadata()); + return Result.success(instanceDetailInfoVo); } /** @@ -482,9 +403,35 @@ public class InstanceControllerV2 { private void checkWeight(Double weight) throws NacosException { if (weight > com.alibaba.nacos.naming.constants.Constants.MAX_WEIGHT_VALUE || weight < com.alibaba.nacos.naming.constants.Constants.MIN_WEIGHT_VALUE) { - throw new NacosException(NacosException.INVALID_PARAM, "instance format invalid: The weights range from " + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.WEIGHT_ERROR, "instance format invalid: The weights range from " + com.alibaba.nacos.naming.constants.Constants.MIN_WEIGHT_VALUE + " to " + com.alibaba.nacos.naming.constants.Constants.MAX_WEIGHT_VALUE); } } + + private Instance buildInstance(InstanceForm instanceForm) throws NacosException { + Instance instance = InstanceBuilder.newBuilder() + .setServiceName(buildCompositeServiceName(instanceForm)) + .setIp(instanceForm.getIp()) + .setClusterName(instanceForm.getClusterName()) + .setPort(instanceForm.getPort()) + .setHealthy(instanceForm.getHealthy()) + .setWeight(instanceForm.getWeight()) + .setEnabled(instanceForm.getEnabled()) + .setMetadata(UtilsAndCommons.parseMetadata(instanceForm.getMetadata())) + .setEphemeral(instanceForm.getEphemeral()) + .build(); + if (instanceForm.getEphemeral() == null) { + instance.setEphemeral((switchDomain.isDefaultInstanceEphemeral())); + } + return instance; + } + + private String buildCompositeServiceName(InstanceForm instanceForm) { + return NamingUtils.getGroupedName(instanceForm.getServiceName(), instanceForm.getGroupName()); + } + + private String buildCompositeServiceName(InstanceMetadataBatchOperationForm form) { + return NamingUtils.getGroupedName(form.getServiceName(), form.getGroupName()); + } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/OperatorControllerV2.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/OperatorControllerV2.java new file mode 100644 index 000000000..80ae2aae2 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/OperatorControllerV2.java @@ -0,0 +1,152 @@ +/* + * Copyright 1999-2022 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.nacos.naming.controllers.v2; + +import com.alibaba.nacos.api.annotation.NacosApi; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.naming.cluster.ServerStatusManager; +import com.alibaba.nacos.naming.constants.ClientConstants; +import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; +import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.misc.SwitchDomain; +import com.alibaba.nacos.naming.misc.SwitchManager; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.model.form.UpdateSwitchForm; +import com.alibaba.nacos.naming.model.vo.MetricsInfoVo; +import com.alibaba.nacos.naming.monitor.MetricsMonitor; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import com.alibaba.nacos.sys.env.EnvUtil; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collection; + +/** + * OperatorControllerV2. + * + * @author dongyafei + * @date 2022/9/8 + */ +@NacosApi +@RestController +@RequestMapping({UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_OPERATOR_CONTEXT, + UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + "/ops"}) +public class OperatorControllerV2 { + + private final SwitchManager switchManager; + + private final ServerStatusManager serverStatusManager; + + private final SwitchDomain switchDomain; + + private final ClientManager clientManager; + + public OperatorControllerV2(SwitchManager switchManager, ServerStatusManager serverStatusManager, + SwitchDomain switchDomain, ClientManager clientManager) { + this.switchManager = switchManager; + this.serverStatusManager = serverStatusManager; + this.switchDomain = switchDomain; + this.clientManager = clientManager; + } + + /** + * Get switch information. + * + * @return switchDomain + */ + @GetMapping("/switches") + public Result switches() { + return Result.success(switchDomain); + } + + /** + * Update switch information. + * + * @param updateSwitchForm debug, entry, value + * @return 'ok' if success + * @throws Exception exception + */ + @Secured(resource = "naming/switches", action = ActionTypes.WRITE) + @PutMapping("/switches") + public Result updateSwitch(UpdateSwitchForm updateSwitchForm) throws Exception { + updateSwitchForm.validate(); + try { + switchManager.update(updateSwitchForm.getEntry(), updateSwitchForm.getValue(), updateSwitchForm.getDebug()); + } catch (IllegalArgumentException e) { + throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(), ErrorCode.SERVER_ERROR, + e.getMessage()); + } + + return Result.success("ok"); + } + + /** + * Get metrics information. + * + * @param onlyStatus onlyStatus + * @return metrics information + */ + @GetMapping("/metrics") + public Result metrics( + @RequestParam(value = "onlyStatus", required = false, defaultValue = "true") Boolean onlyStatus) { + MetricsInfoVo metricsInfoVo = new MetricsInfoVo(); + metricsInfoVo.setStatus(serverStatusManager.getServerStatus().name()); + if (onlyStatus) { + return Result.success(metricsInfoVo); + } + + int connectionBasedClient = 0; + int ephemeralIpPortClient = 0; + int persistentIpPortClient = 0; + int responsibleClientCount = 0; + Collection allClientId = clientManager.allClientId(); + for (String clientId : allClientId) { + if (clientId.contains(IpPortBasedClient.ID_DELIMITER)) { + if (clientId.endsWith(ClientConstants.PERSISTENT_SUFFIX)) { + persistentIpPortClient += 1; + } else { + ephemeralIpPortClient += 1; + } + } else { + connectionBasedClient += 1; + } + if (clientManager.isResponsibleClient(clientManager.getClient(clientId))) { + responsibleClientCount += 1; + } + } + + metricsInfoVo.setServiceCount(MetricsMonitor.getDomCountMonitor().get()); + metricsInfoVo.setInstanceCount(MetricsMonitor.getIpCountMonitor().get()); + metricsInfoVo.setSubscribeCount(MetricsMonitor.getSubscriberCount().get()); + metricsInfoVo.setClientCount(allClientId.size()); + metricsInfoVo.setConnectionBasedClientCount(connectionBasedClient); + metricsInfoVo.setEphemeralIpPortClientCount(ephemeralIpPortClient); + metricsInfoVo.setPersistentIpPortClientCount(persistentIpPortClient); + metricsInfoVo.setResponsibleClientCount(responsibleClientCount); + metricsInfoVo.setCpu(EnvUtil.getCpu()); + metricsInfoVo.setLoad(EnvUtil.getLoad()); + metricsInfoVo.setMem(EnvUtil.getMem()); + return Result.success(metricsInfoVo); + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/ServiceControllerV2.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/ServiceControllerV2.java new file mode 100644 index 000000000..5c4fa8dc4 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/ServiceControllerV2.java @@ -0,0 +1,182 @@ +/* + * Copyright 1999-2022 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.nacos.naming.controllers.v2; + +import com.alibaba.nacos.api.annotation.NacosApi; +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.api.selector.Selector; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.trace.event.naming.DeregisterServiceTraceEvent; +import com.alibaba.nacos.common.trace.event.naming.RegisterServiceTraceEvent; +import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.naming.core.ServiceOperatorV2Impl; +import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.model.form.ServiceForm; +import com.alibaba.nacos.naming.pojo.ServiceDetailInfo; +import com.alibaba.nacos.naming.pojo.ServiceNameView; +import com.alibaba.nacos.naming.selector.NoneSelector; +import com.alibaba.nacos.naming.selector.SelectorManager; +import com.alibaba.nacos.naming.utils.ServiceUtil; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.net.URLDecoder; +import java.util.Collection; +import java.util.Objects; +import java.util.Optional; + +/** + * Service operation controller. + * + * @author nkorange + */ +@NacosApi +@RestController +@RequestMapping(UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_SERVICE_CONTEXT) +public class ServiceControllerV2 { + + private final ServiceOperatorV2Impl serviceOperatorV2; + + private final SelectorManager selectorManager; + + public ServiceControllerV2(ServiceOperatorV2Impl serviceOperatorV2, SelectorManager selectorManager) { + this.serviceOperatorV2 = serviceOperatorV2; + this.selectorManager = selectorManager; + } + + /** + * Create a new service. This API will create a persistence service. + */ + @PostMapping() + @Secured(action = ActionTypes.WRITE) + public Result create(ServiceForm serviceForm) throws Exception { + serviceForm.validate(); + ServiceMetadata serviceMetadata = new ServiceMetadata(); + serviceMetadata.setProtectThreshold(serviceForm.getProtectThreshold()); + serviceMetadata.setSelector(parseSelector(serviceForm.getSelector())); + serviceMetadata.setExtendData(UtilsAndCommons.parseMetadata(serviceForm.getMetadata())); + serviceMetadata.setEphemeral(serviceForm.getEphemeral()); + serviceOperatorV2.create(Service + .newService(serviceForm.getNamespaceId(), serviceForm.getGroupName(), serviceForm.getServiceName(), + serviceForm.getEphemeral()), serviceMetadata); + NotifyCenter.publishEvent( + new RegisterServiceTraceEvent(System.currentTimeMillis(), serviceForm.getNamespaceId(), + serviceForm.getGroupName(), serviceForm.getServiceName())); + return Result.success("ok"); + } + + /** + * Remove service. + */ + @DeleteMapping() + @Secured(action = ActionTypes.WRITE) + public Result remove( + @RequestParam(value = "namespaceId", defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, + @RequestParam("serviceName") String serviceName, + @RequestParam(value = "groupName", defaultValue = Constants.DEFAULT_GROUP) String groupName) + throws Exception { + serviceOperatorV2.delete(Service.newService(namespaceId, groupName, serviceName)); + NotifyCenter.publishEvent( + new DeregisterServiceTraceEvent(System.currentTimeMillis(), namespaceId, groupName, serviceName)); + return Result.success("ok"); + } + + /** + * Get detail of service. + */ + @GetMapping() + @Secured(action = ActionTypes.READ) + public Result detail( + @RequestParam(value = "namespaceId", defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, + @RequestParam("serviceName") String serviceName, + @RequestParam(value = "groupName", defaultValue = Constants.DEFAULT_GROUP) String groupName) + throws Exception { + ServiceDetailInfo result = serviceOperatorV2 + .queryService(Service.newService(namespaceId, groupName, serviceName)); + return Result.success(result); + } + + /** + * List all service names. + */ + @GetMapping("/list") + @Secured(action = ActionTypes.READ) + public Result list( + @RequestParam(value = "namespaceId", required = false, defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, + @RequestParam(value = "groupName", required = false, defaultValue = Constants.DEFAULT_GROUP) String groupName, + @RequestParam(value = "selector", required = false, defaultValue = StringUtils.EMPTY) String selector, + @RequestParam(value = "pageNo", required = false, defaultValue = "1") Integer pageNo, + @RequestParam(value = "pageSize", required = false, defaultValue = "20") Integer pageSize) + throws Exception { + pageSize = Math.min(500, pageSize); + ServiceNameView result = new ServiceNameView(); + Collection serviceNameList = serviceOperatorV2.listService(namespaceId, groupName, selector); + result.setCount(serviceNameList.size()); + result.setServices(ServiceUtil.pageServiceName(pageNo, pageSize, serviceNameList)); + return Result.success(result); + } + + /** + * Update service. + */ + @PutMapping() + @Secured(action = ActionTypes.WRITE) + public Result update(ServiceForm serviceForm) throws Exception { + serviceForm.validate(); + ServiceMetadata serviceMetadata = new ServiceMetadata(); + serviceMetadata.setProtectThreshold(serviceForm.getProtectThreshold()); + serviceMetadata.setExtendData(UtilsAndCommons.parseMetadata(serviceForm.getMetadata())); + serviceMetadata.setSelector(parseSelector(serviceForm.getSelector())); + Service service = Service + .newService(serviceForm.getNamespaceId(), serviceForm.getGroupName(), serviceForm.getServiceName()); + serviceOperatorV2.update(service, serviceMetadata); + return Result.success("ok"); + } + + private Selector parseSelector(String selectorJsonString) throws Exception { + if (StringUtils.isBlank(selectorJsonString)) { + return new NoneSelector(); + } + + JsonNode selectorJson = JacksonUtils.toObj(URLDecoder.decode(selectorJsonString, "UTF-8")); + String type = Optional.ofNullable(selectorJson.get("type")).orElseThrow( + () -> new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.SELECTOR_ERROR, + "not match any type of selector!")).asText(); + String expression = Optional.ofNullable(selectorJson.get("expression")).map(JsonNode::asText).orElse(null); + Selector selector = selectorManager.parseSelector(type, expression); + if (Objects.isNull(selector)) { + throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.SELECTOR_ERROR, + "not match any type of selector!"); + } + return selector; + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/CatalogServiceV1Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/CatalogServiceV1Impl.java deleted file mode 100644 index bf7cb3828..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/CatalogServiceV1Impl.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.alibaba.nacos.api.naming.utils.NamingUtils; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.naming.constants.FieldsConstants; -import com.alibaba.nacos.naming.pojo.ClusterInfo; -import com.alibaba.nacos.naming.pojo.IpAddressInfo; -import com.alibaba.nacos.naming.pojo.ServiceDetailInfo; -import com.alibaba.nacos.naming.pojo.ServiceView; -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.apache.commons.collections.CollectionUtils; -import com.alibaba.nacos.common.utils.StringUtils; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -/** - * Catalog service for v1.x . - * - * @author xiweng.yy - */ -@Component -public class CatalogServiceV1Impl implements CatalogService { - - private final ServiceManager serviceManager; - - public CatalogServiceV1Impl(ServiceManager serviceManager) { - this.serviceManager = serviceManager; - } - - @Override - public Object getServiceDetail(String namespaceId, String groupName, String serviceName) throws NacosException { - Service detailedService = serviceManager - .getService(namespaceId, NamingUtils.getGroupedName(serviceName, groupName)); - - serviceManager.checkServiceIsNull(detailedService, namespaceId, serviceName); - - ObjectNode serviceObject = JacksonUtils.createEmptyJsonNode(); - serviceObject.put(FieldsConstants.NAME, serviceName); - serviceObject.put(FieldsConstants.PROTECT_THRESHOLD, detailedService.getProtectThreshold()); - serviceObject.put(FieldsConstants.GROUP_NAME, groupName); - serviceObject.replace(FieldsConstants.SELECTOR, JacksonUtils.transferToJsonNode(detailedService.getSelector())); - serviceObject.replace(FieldsConstants.METADATA, JacksonUtils.transferToJsonNode(detailedService.getMetadata())); - - ObjectNode detailView = JacksonUtils.createEmptyJsonNode(); - detailView.replace(FieldsConstants.SERVICE, serviceObject); - detailView.replace(FieldsConstants.CLUSTERS, - JacksonUtils.transferToJsonNode(detailedService.getClusterMap().values())); - return detailView; - } - - @Override - public List listInstances(String namespaceId, String groupName, String serviceName, - String clusterName) throws NacosException { - Service service = serviceManager.getService(namespaceId, NamingUtils.getGroupedName(serviceName, groupName)); - - serviceManager.checkServiceIsNull(service, namespaceId, serviceName); - - if (!service.getClusterMap().containsKey(clusterName)) { - throw new NacosException(NacosException.NOT_FOUND, "cluster " + clusterName + " is not found!"); - } - return service.getClusterMap().get(clusterName).allIPs(); - } - - @Override - public Object pageListService(String namespaceId, String groupName, String serviceName, int pageNo, int pageSize, - String instancePattern, boolean ignoreEmptyService) throws NacosException { - String param = StringUtils.isBlank(serviceName) && StringUtils.isBlank(groupName) ? StringUtils.EMPTY - : NamingUtils.getGroupedNameOptional(serviceName, groupName); - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - - List services = new ArrayList<>(); - final int total = serviceManager - .getPagedService(namespaceId, pageNo - 1, pageSize, param, instancePattern, services, - ignoreEmptyService); - if (CollectionUtils.isEmpty(services)) { - result.replace(FieldsConstants.SERVICE_LIST, JacksonUtils.transferToJsonNode(Collections.emptyList())); - result.put(FieldsConstants.COUNT, 0); - return result; - } - - List serviceViews = new LinkedList<>(); - for (Service each : services) { - ServiceView serviceView = new ServiceView(); - serviceView.setName(NamingUtils.getServiceName(each.getName())); - serviceView.setGroupName(NamingUtils.getGroupName(each.getName())); - serviceView.setClusterCount(each.getClusterMap().size()); - serviceView.setIpCount(each.allIPs().size()); - serviceView.setHealthyInstanceCount(each.healthyInstanceCount()); - serviceView.setTriggerFlag(each.triggerFlag() ? "true" : "false"); - serviceViews.add(serviceView); - } - - result.set(FieldsConstants.SERVICE_LIST, JacksonUtils.transferToJsonNode(serviceViews)); - result.put(FieldsConstants.COUNT, total); - - return result; - } - - @Override - public Object pageListServiceDetail(String namespaceId, String groupName, String serviceName, int pageNo, - int pageSize) throws NacosException { - String param = StringUtils.isBlank(serviceName) && StringUtils.isBlank(groupName) ? StringUtils.EMPTY - : NamingUtils.getGroupedNameOptional(serviceName, groupName); - List serviceDetailInfoList = new ArrayList<>(); - List services = new ArrayList<>(8); - serviceManager.getPagedService(namespaceId, pageNo, pageSize, param, StringUtils.EMPTY, services, false); - - for (Service each : services) { - ServiceDetailInfo serviceDetailInfo = new ServiceDetailInfo(); - serviceDetailInfo.setServiceName(NamingUtils.getServiceName(each.getName())); - serviceDetailInfo.setGroupName(NamingUtils.getGroupName(each.getName())); - serviceDetailInfo.setMetadata(each.getMetadata()); - - Map clusterInfoMap = getStringClusterInfoMap(each); - serviceDetailInfo.setClusterMap(clusterInfoMap); - - serviceDetailInfoList.add(serviceDetailInfo); - } - - return serviceDetailInfoList; - } - - private Map getStringClusterInfoMap(Service service) { - Map clusterInfoMap = new HashMap<>(8); - - service.getClusterMap().forEach((clusterName, cluster) -> { - - ClusterInfo clusterInfo = new ClusterInfo(); - List ipAddressInfos = getIpAddressInfos(cluster.allIPs()); - clusterInfo.setHosts(ipAddressInfos); - clusterInfoMap.put(clusterName, clusterInfo); - - }); - return clusterInfoMap; - } - - private List getIpAddressInfos(List instances) { - List ipAddressInfos = new ArrayList<>(); - - instances.forEach((ipAddress) -> { - - IpAddressInfo ipAddressInfo = new IpAddressInfo(); - ipAddressInfo.setIp(ipAddress.getIp()); - ipAddressInfo.setPort(ipAddress.getPort()); - ipAddressInfo.setMetadata(ipAddress.getMetadata()); - ipAddressInfo.setValid(ipAddress.isHealthy()); - ipAddressInfo.setWeight(ipAddress.getWeight()); - ipAddressInfo.setEnabled(ipAddress.isEnabled()); - ipAddressInfos.add(ipAddressInfo); - - }); - return ipAddressInfos; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/Cluster.java b/naming/src/main/java/com/alibaba/nacos/naming/core/Cluster.java deleted file mode 100644 index d2d48c672..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/Cluster.java +++ /dev/null @@ -1,462 +0,0 @@ -/* - * 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.nacos.naming.core; - -import com.alibaba.nacos.naming.healthcheck.HealthCheckReactor; -import com.alibaba.nacos.naming.healthcheck.HealthCheckStatus; -import com.alibaba.nacos.naming.healthcheck.HealthCheckTask; -import com.alibaba.nacos.naming.misc.Loggers; -import com.fasterxml.jackson.annotation.JsonIgnore; - -import org.apache.commons.collections.CollectionUtils; -import com.alibaba.nacos.common.utils.StringUtils; -import org.springframework.util.Assert; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Cluster. - * - * @author nkorange - * @author jifengnan 2019-04-26 - */ -public class Cluster extends com.alibaba.nacos.api.naming.pojo.Cluster implements Cloneable { - - private static final String CLUSTER_NAME_SYNTAX = "[0-9a-zA-Z-]+"; - - private static final long serialVersionUID = 8940123791150907510L; - - /** - * an addition for same site routing, can group multiple sites into a region, like Hangzhou, Shanghai, etc. - */ - private String sitegroup = StringUtils.EMPTY; - - private int defCkport = 80; - - private int defIpPort = -1; - - @JsonIgnore - private HealthCheckTask checkTask; - - @JsonIgnore - private Set persistentInstances = new HashSet<>(); - - @JsonIgnore - private Set ephemeralInstances = new HashSet<>(); - - @JsonIgnore - private Service service; - - @JsonIgnore - private volatile boolean inited = false; - - private Map metadata = new ConcurrentHashMap<>(); - - public Cluster() { - } - - /** - * Create a cluster. - * - *

the cluster name cannot be null, and only the arabic numerals, letters and underline are allowed. - * - * @param clusterName the cluster name - * @param service the service to which the current cluster belongs - * @throws IllegalArgumentException the service is null, or the cluster name is null, or the cluster name is - * illegal - * @author jifengnan 2019-04-26 - * @since 1.0.1 - */ - public Cluster(String clusterName, Service service) { - this.setName(clusterName); - this.service = service; - validate(); - } - - /** - * Reason why method is not camel is that the old version has released, and the method name will be as the key - * serialize and deserialize for Json. So ignore checkstyle. - * - * @return default port - */ - @SuppressWarnings("checkstyle:abbreviationaswordinname") - public int getDefIPPort() { - // for compatibility with old entries - return defIpPort == -1 ? defCkport : defIpPort; - } - - @SuppressWarnings("checkstyle:abbreviationaswordinname") - public void setDefIPPort(int defIpPort) { - if (defIpPort == 0) { - throw new IllegalArgumentException("defIPPort can not be 0"); - } - this.defIpPort = defIpPort; - } - - /** - * Get all instances. - * - * @return list of instance - */ - public List allIPs() { - List allInstances = new ArrayList<>(); - allInstances.addAll(persistentInstances); - allInstances.addAll(ephemeralInstances); - return allInstances; - } - - /** - * Get all ephemeral or consistence instances. - * - * @param ephemeral whether returned instances are ephemeral - * @return list of special instances - */ - public List allIPs(boolean ephemeral) { - return ephemeral ? new ArrayList<>(ephemeralInstances) : new ArrayList<>(persistentInstances); - } - - /** - * Init cluster. - */ - public void init() { - if (inited) { - return; - } - checkTask = new HealthCheckTask(this); - - HealthCheckReactor.scheduleCheck(checkTask); - inited = true; - } - - /** - * Destroy cluster. - */ - public void destroy() { - if (checkTask != null) { - checkTask.setCancelled(true); - } - } - - @JsonIgnore - public HealthCheckTask getHealthCheckTask() { - return checkTask; - } - - public Service getService() { - return service; - } - - /** - * Replace the service for the current cluster. - * - *

the service shouldn't be replaced. so if the service is not empty will nothing to do. - * (the service fields can be changed, but the service A shouldn't be replaced to service B). If the service of a - * cluster is required to replace, actually, a new cluster is required. - * - * @param service the new service - */ - public void setService(Service service) { - if (this.service != null) { - return; - } - this.service = service; - } - - /** - * this method has been deprecated, the service name shouldn't be changed. - * - * @param serviceName the service name - * @author jifengnan 2019-04-26 - * @since 1.0.1 - */ - @Deprecated - @Override - public void setServiceName(String serviceName) { - super.setServiceName(serviceName); - } - - /** - * Get the service name of the current cluster. - * - *

Note that the returned service name is not the name which set by {@link #setServiceName(String)}, - * but the name of the service to which the current cluster belongs. - * - * @return the service name of the current cluster. - */ - @Override - public String getServiceName() { - if (service != null) { - return service.getName(); - } else { - return super.getServiceName(); - } - } - - @Override - public Cluster clone() throws CloneNotSupportedException { - super.clone(); - Cluster cluster = new Cluster(this.getName(), service); - cluster.setHealthChecker(getHealthChecker().clone()); - cluster.persistentInstances = new HashSet<>(); - cluster.checkTask = null; - cluster.metadata = new HashMap<>(metadata); - return cluster; - } - - public boolean isEmpty() { - return ephemeralInstances.isEmpty() && persistentInstances.isEmpty(); - } - - /** - * Update instance list. - * - * @param ips instance list - * @param ephemeral whether these instances are ephemeral - */ - public void updateIps(List ips, boolean ephemeral) { - - Set toUpdateInstances = ephemeral ? ephemeralInstances : persistentInstances; - - HashMap oldIpMap = new HashMap<>(toUpdateInstances.size()); - - for (Instance ip : toUpdateInstances) { - oldIpMap.put(ip.getDatumKey(), ip); - } - - List updatedIps = updatedIps(ips, oldIpMap.values()); - if (updatedIps.size() > 0) { - for (Instance ip : updatedIps) { - Instance oldIP = oldIpMap.get(ip.getDatumKey()); - - // do not update the ip validation status of updated ips - // because the checker has the most precise result - // Only when ip is not marked, don't we update the health status of IP: - if (!ip.isMarked()) { - ip.setHealthy(oldIP.isHealthy()); - } - - if (ip.isHealthy() != oldIP.isHealthy()) { - // ip validation status updated - Loggers.EVT_LOG.info("{} {SYNC} IP-{} {}:{}@{}", getService().getName(), - (ip.isHealthy() ? "ENABLED" : "DISABLED"), ip.getIp(), ip.getPort(), getName()); - } - - if (ip.getWeight() != oldIP.getWeight()) { - // ip validation status updated - Loggers.EVT_LOG.info("{} {SYNC} {IP-UPDATED} {}->{}", getService().getName(), oldIP, ip); - } - } - } - - List newIPs = subtract(ips, oldIpMap.values()); - if (newIPs.size() > 0) { - Loggers.EVT_LOG - .info("{} {SYNC} {IP-NEW} cluster: {}, new ips size: {}, content: {}", getService().getName(), - getName(), newIPs.size(), newIPs); - - for (Instance ip : newIPs) { - HealthCheckStatus.reset(ip); - } - } - - List deadIPs = subtract(oldIpMap.values(), ips); - - if (deadIPs.size() > 0) { - Loggers.EVT_LOG - .info("{} {SYNC} {IP-DEAD} cluster: {}, dead ips size: {}, content: {}", getService().getName(), - getName(), deadIPs.size(), deadIPs); - - for (Instance ip : deadIPs) { - HealthCheckStatus.remv(ip); - } - } - - toUpdateInstances = new HashSet<>(ips); - - if (ephemeral) { - ephemeralInstances = toUpdateInstances; - } else { - persistentInstances = toUpdateInstances; - } - } - - private List updatedIps(Collection newInstance, Collection oldInstance) { - - List intersects = (List) CollectionUtils.intersection(newInstance, oldInstance); - Map stringIpAddressMap = new ConcurrentHashMap<>(intersects.size()); - - for (Instance instance : intersects) { - stringIpAddressMap.put(instance.getIp() + ":" + instance.getPort(), instance); - } - - Map intersectMap = new ConcurrentHashMap<>(newInstance.size() + oldInstance.size()); - Map updatedInstancesMap = new ConcurrentHashMap<>(newInstance.size()); - Map newInstancesMap = new ConcurrentHashMap<>(newInstance.size()); - - for (Instance instance : oldInstance) { - if (stringIpAddressMap.containsKey(instance.getIp() + ":" + instance.getPort())) { - intersectMap.put(instance.toString(), 1); - } - } - - for (Instance instance : newInstance) { - if (stringIpAddressMap.containsKey(instance.getIp() + ":" + instance.getPort())) { - - if (intersectMap.containsKey(instance.toString())) { - intersectMap.put(instance.toString(), 2); - } else { - intersectMap.put(instance.toString(), 1); - } - } - - newInstancesMap.put(instance.toString(), instance); - - } - - for (Map.Entry entry : intersectMap.entrySet()) { - String key = entry.getKey(); - Integer value = entry.getValue(); - - if (value == 1) { - if (newInstancesMap.containsKey(key)) { - updatedInstancesMap.put(key, newInstancesMap.get(key)); - } - } - } - - return new ArrayList<>(updatedInstancesMap.values()); - } - - private List subtract(Collection oldIp, Collection ips) { - Map ipsMap = new HashMap<>(ips.size()); - for (Instance instance : ips) { - ipsMap.put(instance.getIp() + ":" + instance.getPort(), instance); - } - - List instanceResult = new ArrayList<>(); - - for (Instance instance : oldIp) { - if (!ipsMap.containsKey(instance.getIp() + ":" + instance.getPort())) { - instanceResult.add(instance); - } - } - return instanceResult; - } - - @Override - public int hashCode() { - return Objects.hash(getName()); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Cluster)) { - return false; - } - - return getName().equals(((Cluster) obj).getName()); - } - - public int getDefCkport() { - return defCkport; - } - - public void setDefCkport(int defCkport) { - this.defCkport = defCkport; - super.setDefaultCheckPort(defCkport); - } - - /** - * Update cluster from other cluster. - * - * @param cluster new cluster - */ - public void update(Cluster cluster) { - - if (!getHealthChecker().equals(cluster.getHealthChecker())) { - Loggers.SRV_LOG.info("[CLUSTER-UPDATE] {}:{}:, healthChecker: {} -> {}", getService().getName(), getName(), - getHealthChecker().toString(), cluster.getHealthChecker().toString()); - setHealthChecker(cluster.getHealthChecker()); - } - - if (defCkport != cluster.getDefCkport()) { - Loggers.SRV_LOG - .info("[CLUSTER-UPDATE] {}:{}, defCkport: {} -> {}", getService().getName(), getName(), defCkport, - cluster.getDefCkport()); - defCkport = cluster.getDefCkport(); - } - - if (defIpPort != cluster.getDefIPPort()) { - Loggers.SRV_LOG - .info("[CLUSTER-UPDATE] {}:{}, defIPPort: {} -> {}", getService().getName(), getName(), defIpPort, - cluster.getDefIPPort()); - defIpPort = cluster.getDefIPPort(); - } - - if (!StringUtils.equals(sitegroup, cluster.getSitegroup())) { - Loggers.SRV_LOG - .info("[CLUSTER-UPDATE] {}:{}, sitegroup: {} -> {}", getService().getName(), getName(), sitegroup, - cluster.getSitegroup()); - sitegroup = cluster.getSitegroup(); - } - - if (isUseIPPort4Check() != cluster.isUseIPPort4Check()) { - Loggers.SRV_LOG.info("[CLUSTER-UPDATE] {}:{}, useIPPort4Check: {} -> {}", getService().getName(), getName(), - isUseIPPort4Check(), cluster.isUseIPPort4Check()); - setUseIPPort4Check(cluster.isUseIPPort4Check()); - } - - metadata = cluster.getMetadata(); - } - - public String getSitegroup() { - return sitegroup; - } - - public void setSitegroup(String sitegroup) { - this.sitegroup = sitegroup; - } - - public boolean contains(Instance ip) { - return persistentInstances.contains(ip) || ephemeralInstances.contains(ip); - } - - /** - * validate the current cluster. - * - *

the cluster name cannot be null, and only the arabic numerals, letters and underline are allowed. - * - * @throws IllegalArgumentException the service is null, or the cluster name is null, or the cluster name is - * illegal - */ - public void validate() { - Assert.notNull(getName(), "cluster name cannot be null"); - Assert.notNull(service, "service cannot be null"); - if (!getName().matches(CLUSTER_NAME_SYNTAX)) { - throw new IllegalArgumentException( - "cluster name can only have these characters: 0-9a-zA-Z-, current: " + getName()); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/ClusterOperatorV1Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/ClusterOperatorV1Impl.java deleted file mode 100644 index d27282793..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/ClusterOperatorV1Impl.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; -import com.alibaba.nacos.naming.misc.Loggers; - -/** - * Implementation of cluster operator for v1.x. - * - * @author xiweng.yy - */ -@org.springframework.stereotype.Service -public class ClusterOperatorV1Impl implements ClusterOperator { - - private final ServiceManager serviceManager; - - public ClusterOperatorV1Impl(ServiceManager serviceManager) { - this.serviceManager = serviceManager; - } - - @Override - public void updateClusterMetadata(String namespaceId, String serviceName, String clusterName, - ClusterMetadata clusterMetadata) throws NacosException { - Service service = serviceManager.getService(namespaceId, serviceName); - - serviceManager.checkServiceIsNull(service, namespaceId, serviceName); - - Cluster cluster = service.getClusterMap().get(clusterName); - if (cluster == null) { - Loggers.SRV_LOG.warn("[UPDATE-CLUSTER] cluster not exist, will create it: {}, service: {}", clusterName, - serviceName); - cluster = new Cluster(clusterName, service); - } - cluster.setDefCkport(clusterMetadata.getHealthyCheckPort()); - cluster.setUseIPPort4Check(clusterMetadata.isUseInstancePortForCheck()); - cluster.setHealthChecker(clusterMetadata.getHealthChecker()); - cluster.setMetadata(clusterMetadata.getExtendData()); - cluster.init(); - service.getClusterMap().put(clusterName, cluster); - service.setLastModifiedMillis(System.currentTimeMillis()); - service.recalculateChecksum(); - service.validate(); - serviceManager.addOrReplaceService(service); - } - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/DistroMapper.java b/naming/src/main/java/com/alibaba/nacos/naming/core/DistroMapper.java index 30adfd075..a34775a74 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/DistroMapper.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/DistroMapper.java @@ -69,11 +69,6 @@ public class DistroMapper extends MemberChangeListener { this.healthyList = MemberUtil.simpleMembers(memberManager.allMembers()); } - public boolean responsible(Cluster cluster, Instance instance) { - return switchDomain.isHealthCheckEnabled(cluster.getServiceName()) && !cluster.getHealthCheckTask() - .isCancelled() && responsible(cluster.getServiceName()) && cluster.contains(instance); - } - /** * Judge whether current server is responsible for input tag. * diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/HealthOperatorV1Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/HealthOperatorV1Impl.java deleted file mode 100644 index 1bb054250..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/HealthOperatorV1Impl.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.pojo.healthcheck.HealthCheckType; -import com.alibaba.nacos.common.utils.CollectionUtils; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.push.UdpPushService; -import org.springframework.stereotype.Component; - -/** - * Health operator implementation for v1.x. - * - * @author xiweng.yy - */ -@Component -public class HealthOperatorV1Impl implements HealthOperator { - - private final ServiceManager serviceManager; - - private final UdpPushService pushService; - - public HealthOperatorV1Impl(ServiceManager serviceManager, UdpPushService pushService) { - this.serviceManager = serviceManager; - this.pushService = pushService; - } - - @Override - public void updateHealthStatusForPersistentInstance(String namespace, String fullServiceName, String clusterName, - String ip, int port, boolean healthy) throws NacosException { - Service service = serviceManager.getService(namespace, fullServiceName); - // Only health check "none" need update health status with api - if (HealthCheckType.NONE.name().equals(service.getClusterMap().get(clusterName).getHealthChecker().getType())) { - for (Instance instance : service.allIPs(CollectionUtils.list(clusterName))) { - if (instance.getIp().equals(ip) && instance.getPort() == port) { - instance.setHealthy(healthy); - Loggers.EVT_LOG - .info((healthy ? "[IP-ENABLED]" : "[IP-DISABLED]") + " ips: " + instance.getIp() + ":" - + instance.getPort() + "@" + instance.getClusterName() + ", service: " - + fullServiceName + ", msg: update thought HealthController api"); - pushService.serviceChanged(service); - break; - } - } - } else { - throw new NacosException(NacosException.INVALID_PARAM, - "health check is still working, service: " + fullServiceName); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/Instance.java b/naming/src/main/java/com/alibaba/nacos/naming/core/Instance.java deleted file mode 100644 index 3ac317af7..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/Instance.java +++ /dev/null @@ -1,377 +0,0 @@ -/* - * 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.nacos.naming.core; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.common.utils.InternetAddressUtil; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.naming.healthcheck.HealthCheckStatus; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.alibaba.nacos.common.utils.NumberUtils; - -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * IP under service. - * - * @author nkorange - */ -@JsonInclude(Include.NON_NULL) -public class Instance extends com.alibaba.nacos.api.naming.pojo.Instance implements Comparable { - - private static final long serialVersionUID = -6527721638428975306L; - - private volatile long lastBeat = System.currentTimeMillis(); - - @JsonIgnore - private volatile boolean mockValid = false; - - private volatile boolean marked = false; - - private String tenant; - - private String app; - - private static final String SPLITER = "_"; - - public Instance() { - } - - public boolean isMockValid() { - return mockValid; - } - - public void setMockValid(boolean mockValid) { - this.mockValid = mockValid; - } - - public long getLastBeat() { - return lastBeat; - } - - public void setLastBeat(long lastBeat) { - this.lastBeat = lastBeat; - } - - public Instance(String ip, int port) { - this.setIp(ip); - this.setPort(port); - this.setClusterName(UtilsAndCommons.DEFAULT_CLUSTER_NAME); - } - - public Instance(String ip, int port, String clusterName) { - this.setIp(ip.trim()); - this.setPort(port); - this.setClusterName(clusterName); - } - - public Instance(String ip, int port, String clusterName, String tenant, String app) { - this.setIp(ip.trim()); - this.setPort(port); - this.setClusterName(clusterName); - this.tenant = tenant; - this.app = app; - } - - /** - * Create {@link Instance} from encoding string. - * - * @param config instance encoding string - * @return new Instance - */ - public static Instance fromString(String config) { - String[] ipAddressAttributes = config.split(SPLITER); - if (ipAddressAttributes.length < 1) { - return null; - } - - String provider = ipAddressAttributes[0]; - String[] providerAddr; - try { - providerAddr = InternetAddressUtil.splitIPPortStr(provider); - } catch (Exception ex) { - return null; - } - - int port = 0; - if (providerAddr.length == InternetAddressUtil.SPLIT_IP_PORT_RESULT_LENGTH && NumberUtils - .isDigits(providerAddr[1])) { - port = Integer.parseInt(providerAddr[1]); - } - - Instance instance = new Instance(providerAddr[0], port); - - // 7 possible formats of config: - // ip:port - // ip:port_weight - // ip:port_weight_cluster - // ip:port_weight_valid - // ip:port_weight_valid_cluster - // ip:port_weight_valid_marked - // ip:port_weight_valid_marked_cluster - int minimumLength = 1; - - if (ipAddressAttributes.length > minimumLength) { - // determine 'weight': - instance.setWeight(NumberUtils.toDouble(ipAddressAttributes[minimumLength], 1)); - } - - minimumLength++; - - if (ipAddressAttributes.length > minimumLength) { - // determine 'valid': - if (Boolean.TRUE.toString().equals(ipAddressAttributes[minimumLength]) || Boolean.FALSE.toString() - .equals(ipAddressAttributes[minimumLength])) { - instance.setHealthy(Boolean.parseBoolean(ipAddressAttributes[minimumLength])); - } - - // determine 'cluster': - if (!Boolean.TRUE.toString().equals(ipAddressAttributes[ipAddressAttributes.length - 1]) && !Boolean.FALSE - .toString().equals(ipAddressAttributes[ipAddressAttributes.length - 1])) { - instance.setClusterName(ipAddressAttributes[ipAddressAttributes.length - 1]); - } - } - - minimumLength++; - - if (ipAddressAttributes.length > minimumLength) { - // determine 'marked': - if (Boolean.TRUE.toString().equals(ipAddressAttributes[minimumLength]) || Boolean.FALSE.toString() - .equals(ipAddressAttributes[minimumLength])) { - instance.setMarked(Boolean.parseBoolean(ipAddressAttributes[minimumLength])); - } - } - - return instance; - } - - public String toIpAddr() { - return getIp() + ":" + getPort(); - } - - @Override - public String toString() { - return getDatumKey() + SPLITER + getWeight() + SPLITER + isHealthy() + SPLITER + marked + SPLITER - + getClusterName(); - } - - /** - * Serialize to Json. - * - * @return json string - */ - public String toJson() { - return JacksonUtils.toJson(this); - } - - /** - * Create {@link Instance} from json string. - * - * @param json json string - * @return new Instance - */ - public static Instance fromJson(String json) { - Instance ip; - - try { - ip = JacksonUtils.toObj(json, Instance.class); - } catch (Exception e) { - ip = fromString(json); - } - - if (ip == null) { - throw new IllegalArgumentException("malformed ip config: " + json); - } - - if (ip.getWeight() > com.alibaba.nacos.naming.constants.Constants.MAX_WEIGHT_VALUE) { - ip.setWeight(com.alibaba.nacos.naming.constants.Constants.MAX_WEIGHT_VALUE); - } - - if (ip.getWeight() < com.alibaba.nacos.naming.constants.Constants.MIN_POSITIVE_WEIGHT_VALUE - && ip.getWeight() > com.alibaba.nacos.naming.constants.Constants.MIN_WEIGHT_VALUE) { - ip.setWeight(com.alibaba.nacos.naming.constants.Constants.MIN_POSITIVE_WEIGHT_VALUE); - } else if (ip.getWeight() < com.alibaba.nacos.naming.constants.Constants.MIN_WEIGHT_VALUE) { - ip.setWeight(0.0D); - } - - try { - ip.validate(); - } catch (NacosException e) { - throw new IllegalArgumentException("malformed ip config: " + json); - } - - return ip; - } - - @Override - public boolean equals(Object obj) { - if (null == obj || obj.getClass() != getClass()) { - return false; - } - if (obj == this) { - return true; - } - Instance other = (Instance) obj; - - // 0 means wild - return getIp().equals(other.getIp()) && (getPort() == other.getPort() || getPort() == 0) - && this.isEphemeral() == other.isEphemeral(); - } - - @JsonIgnore - public String getDatumKey() { - if (getPort() > 0) { - return getIp() + ":" + getPort() + ":" + UtilsAndCommons.LOCALHOST_SITE + ":" + getClusterName(); - } else { - return getIp() + ":" + UtilsAndCommons.LOCALHOST_SITE + ":" + getClusterName(); - } - } - - @JsonIgnore - public String getDefaultKey() { - if (getPort() > 0) { - return getIp() + ":" + getPort() + ":" + UtilsAndCommons.UNKNOWN_SITE; - } else { - return getIp() + ":" + UtilsAndCommons.UNKNOWN_SITE; - } - } - - @Override - public int hashCode() { - return getIp().hashCode(); - } - - public void setBeingChecked(boolean isBeingChecked) { - HealthCheckStatus.get(this).isBeingChecked.set(isBeingChecked); - } - - public boolean markChecking() { - return HealthCheckStatus.get(this).isBeingChecked.compareAndSet(false, true); - } - - @JsonIgnore - public long getCheckRt() { - return HealthCheckStatus.get(this).checkRt; - } - - @JsonIgnore - public AtomicInteger getOkCount() { - return HealthCheckStatus.get(this).checkOkCount; - } - - @JsonIgnore - public AtomicInteger getFailCount() { - return HealthCheckStatus.get(this).checkFailCount; - } - - @JsonIgnore - public void setCheckRt(long checkRt) { - HealthCheckStatus.get(this).checkRt = checkRt; - } - - public boolean isMarked() { - return marked; - } - - public void setMarked(boolean marked) { - this.marked = marked; - } - - public String getApp() { - return app; - } - - public void setApp(String app) { - this.app = app; - } - - public String getTenant() { - return tenant; - } - - public void setTenant(String tenant) { - this.tenant = tenant; - } - - public String generateInstanceId() { - return getIp() + "#" + getPort() + "#" + getClusterName() + "#" + getServiceName(); - } - - /** - * Generate instance id. - * - * @param currentInstanceIds current instance ids - * @return new instance id - */ - public String generateInstanceId(Set currentInstanceIds) { - String instanceIdGenerator = getInstanceIdGenerator(); - if (Constants.SNOWFLAKE_INSTANCE_ID_GENERATOR.equalsIgnoreCase(instanceIdGenerator)) { - return generateSnowflakeInstanceId(currentInstanceIds); - } else { - return generateInstanceId(); - } - } - - private String generateSnowflakeInstanceId(Set currentInstanceIds) { - int id = 0; - while (currentInstanceIds.contains(String.valueOf(id))) { - id++; - } - String idStr = String.valueOf(id); - currentInstanceIds.add(idStr); - return idStr; - } - - /** - * Judge whether this instance is validate. - * - * @throws NacosException if instance is not validate - */ - public void validate() throws NacosException { - if (!InternetAddressUtil.isIP(getIp())) { - throw new NacosException(NacosException.INVALID_PARAM, - "instance format invalid: Your IP address is spelled incorrectly"); - } - - if (getWeight() > com.alibaba.nacos.naming.constants.Constants.MAX_WEIGHT_VALUE - || getWeight() < com.alibaba.nacos.naming.constants.Constants.MIN_WEIGHT_VALUE) { - throw new NacosException(NacosException.INVALID_PARAM, "instance format invalid: The weights range from " - + com.alibaba.nacos.naming.constants.Constants.MIN_WEIGHT_VALUE + " to " - + com.alibaba.nacos.naming.constants.Constants.MAX_WEIGHT_VALUE); - } - - } - - @Override - public int compareTo(Object o) { - if (!(o instanceof Instance)) { - Loggers.SRV_LOG.error("[INSTANCE-COMPARE] Object is not an instance of IPAdress, object: {}", o.getClass()); - throw new IllegalArgumentException("Object is not an instance of IPAdress,object: " + o.getClass()); - } - - Instance instance = (Instance) o; - String ipKey = instance.toString(); - - return this.toString().compareTo(ipKey); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorClientImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorClientImpl.java index 20135482a..124b2ab3c 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorClientImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorClientImpl.java @@ -17,6 +17,8 @@ package com.alibaba.nacos.naming.core; import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.NamingResponseCode; import com.alibaba.nacos.api.naming.PreservedMetadataKeys; import com.alibaba.nacos.api.naming.pojo.Instance; @@ -50,6 +52,7 @@ import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.pojo.instance.BeatInfoInstanceBuilder; import com.alibaba.nacos.naming.push.UdpPushService; import com.alibaba.nacos.naming.utils.ServiceUtil; +import com.alibaba.nacos.naming.web.ClientAttributesFilter; import java.util.HashMap; import java.util.LinkedList; @@ -124,7 +127,7 @@ public class InstanceOperatorClientImpl implements InstanceOperator { Service service = getService(namespaceId, serviceName, instance.isEphemeral()); if (!ServiceManager.getInstance().containSingleton(service)) { - throw new NacosException(NacosException.INVALID_PARAM, + throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, "service not found, namespace: " + namespaceId + ", service: " + service); } String metadataId = InstancePublishInfo @@ -203,7 +206,7 @@ public class InstanceOperatorClientImpl implements InstanceOperator { private Instance getInstance0(Service service, String cluster, String ip, int port) throws NacosException { ServiceInfo serviceInfo = serviceStorage.getData(service); if (serviceInfo.getHosts().isEmpty()) { - throw new NacosException(NacosException.NOT_FOUND, + throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, "no ips found for cluster " + cluster + " in service " + service.getGroupedServiceName()); } for (Instance each : serviceInfo.getHosts()) { @@ -211,7 +214,7 @@ public class InstanceOperatorClientImpl implements InstanceOperator { return each; } } - throw new NacosException(NacosException.NOT_FOUND, "no matched ip found!"); + throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, "no matched ip found!"); } @Override @@ -326,7 +329,13 @@ public class InstanceOperatorClientImpl implements InstanceOperator { private void createIpPortClientIfAbsent(String clientId) { if (!clientManager.contains(clientId)) { - clientManager.clientConnected(clientId, new ClientAttributes()); + ClientAttributes clientAttributes; + if (ClientAttributesFilter.threadLocalClientAttributes.get() != null) { + clientAttributes = ClientAttributesFilter.threadLocalClientAttributes.get(); + } else { + clientAttributes = new ClientAttributes(); + } + clientManager.clientConnected(clientId, clientAttributes); } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorServiceImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorServiceImpl.java deleted file mode 100644 index e3d0a6b72..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorServiceImpl.java +++ /dev/null @@ -1,375 +0,0 @@ -/* - * 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.nacos.naming.core; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.NamingResponseCode; -import com.alibaba.nacos.api.naming.PreservedMetadataKeys; -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.alibaba.nacos.api.naming.pojo.ServiceInfo; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.execute.InstanceUpgradeHelper; -import com.alibaba.nacos.naming.healthcheck.RsInfo; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.pojo.InstanceOperationContext; -import com.alibaba.nacos.naming.pojo.InstanceOperationInfo; -import com.alibaba.nacos.naming.pojo.Subscriber; -import com.alibaba.nacos.naming.pojo.instance.BeatInfoInstanceBuilder; -import com.alibaba.nacos.naming.push.UdpPushService; -import com.alibaba.nacos.naming.push.v1.ClientInfo; -import com.alibaba.nacos.naming.push.v1.DataSource; -import com.alibaba.nacos.naming.push.v1.NamingSubscriberServiceV1Impl; -import com.alibaba.nacos.naming.push.v1.PushClient; -import com.alibaba.nacos.naming.selector.SelectorManager; -import com.alibaba.nacos.naming.utils.InstanceUtil; -import org.apache.commons.collections.CollectionUtils; -import org.springframework.stereotype.Component; - -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static com.alibaba.nacos.naming.misc.UtilsAndCommons.EPHEMERAL; -import static com.alibaba.nacos.naming.misc.UtilsAndCommons.PERSIST; -import static com.alibaba.nacos.naming.misc.UtilsAndCommons.UPDATE_INSTANCE_METADATA_ACTION_REMOVE; -import static com.alibaba.nacos.naming.misc.UtilsAndCommons.UPDATE_INSTANCE_METADATA_ACTION_UPDATE; - -/** - * Implementation of {@link InstanceOperator} by service for v1.x. - * - * @author xiweng.yy - */ -@Component -public class InstanceOperatorServiceImpl implements InstanceOperator { - - private final ServiceManager serviceManager; - - private final SwitchDomain switchDomain; - - private final UdpPushService pushService; - - private final NamingSubscriberServiceV1Impl subscriberServiceV1; - - private final InstanceUpgradeHelper instanceUpgradeHelper; - - private final SelectorManager selectorManager; - - private DataSource pushDataSource = new DataSource() { - - @Override - public String getData(PushClient client) { - ServiceInfo result = new ServiceInfo(client.getServiceName(), client.getClusters()); - try { - Subscriber subscriber = new Subscriber(client.getAddrStr(), client.getAgent(), client.getApp(), - client.getIp(), client.getNamespaceId(), client.getServiceName(), client.getPort(), - client.getClusters()); - result = listInstance(client.getNamespaceId(), client.getServiceName(), subscriber, - client.getClusters(), false); - } catch (Exception e) { - Loggers.SRV_LOG.warn("PUSH-SERVICE: service is not modified", e); - } - - // overdrive the cache millis to push mode - result.setCacheMillis(switchDomain.getPushCacheMillis(client.getServiceName())); - return JacksonUtils.toJson(result); - } - }; - - public InstanceOperatorServiceImpl(ServiceManager serviceManager, SwitchDomain switchDomain, - UdpPushService pushService, NamingSubscriberServiceV1Impl subscriberServiceV1, - InstanceUpgradeHelper instanceUpgradeHelper, SelectorManager selectorManager) { - this.serviceManager = serviceManager; - this.switchDomain = switchDomain; - this.pushService = pushService; - this.subscriberServiceV1 = subscriberServiceV1; - this.instanceUpgradeHelper = instanceUpgradeHelper; - this.selectorManager = selectorManager; - } - - @Override - public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException { - com.alibaba.nacos.naming.core.Instance coreInstance = parseInstance(instance); - serviceManager.registerInstance(namespaceId, serviceName, coreInstance); - } - - @Override - public void removeInstance(String namespaceId, String serviceName, Instance instance) throws NacosException { - com.alibaba.nacos.naming.core.Instance coreInstance = parseInstance(instance); - Service service = serviceManager.getService(namespaceId, serviceName); - if (service == null) { - Loggers.SRV_LOG.warn("remove instance from non-exist service: {}", serviceName); - return; - } - serviceManager.removeInstance(namespaceId, serviceName, instance.isEphemeral(), coreInstance); - } - - @Override - public void updateInstance(String namespaceId, String serviceName, Instance instance) throws NacosException { - com.alibaba.nacos.naming.core.Instance coreInstance = parseInstance(instance); - serviceManager.updateInstance(namespaceId, serviceName, coreInstance); - } - - @Override - public void patchInstance(String namespaceId, String serviceName, InstancePatchObject patchObject) - throws NacosException { - com.alibaba.nacos.naming.core.Instance instance = serviceManager - .getInstance(namespaceId, serviceName, patchObject.getCluster(), patchObject.getIp(), - patchObject.getPort()); - if (instance == null) { - throw new NacosException(NacosException.INVALID_PARAM, "instance not found"); - } - if (null != patchObject.getMetadata()) { - instance.setMetadata(patchObject.getMetadata()); - } - if (null != patchObject.getApp()) { - instance.setApp(patchObject.getApp()); - } - if (null != patchObject.getEnabled()) { - instance.setEnabled(patchObject.getEnabled()); - } - if (null != patchObject.getHealthy()) { - instance.setHealthy(patchObject.getHealthy()); - } - instance.setLastBeat(System.currentTimeMillis()); - instance.validate(); - serviceManager.updateInstance(namespaceId, serviceName, instance); - } - - @Override - public ServiceInfo listInstance(String namespaceId, String serviceName, Subscriber subscriber, String cluster, - boolean healthOnly) throws Exception { - ClientInfo clientInfo = new ClientInfo(subscriber.getAgent()); - String clientIP = subscriber.getIp(); - ServiceInfo result = new ServiceInfo(serviceName, cluster); - Service service = serviceManager.getService(namespaceId, serviceName); - long cacheMillis = switchDomain.getDefaultCacheMillis(); - - // now try to enable the push - try { - if (subscriber.getPort() > 0 && pushService.canEnablePush(subscriber.getAgent())) { - subscriberServiceV1.addClient(namespaceId, serviceName, cluster, subscriber.getAgent(), - new InetSocketAddress(clientIP, subscriber.getPort()), pushDataSource, StringUtils.EMPTY, - StringUtils.EMPTY); - cacheMillis = switchDomain.getPushCacheMillis(serviceName); - } - } catch (Exception e) { - Loggers.SRV_LOG.error("[NACOS-API] failed to added push client {}, {}:{}", clientInfo, clientIP, - subscriber.getPort(), e); - cacheMillis = switchDomain.getDefaultCacheMillis(); - } - - if (service == null) { - if (Loggers.SRV_LOG.isDebugEnabled()) { - Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName); - } - result.setCacheMillis(cacheMillis); - return result; - } - - checkIfDisabled(service); - - List srvedIps = service - .srvIPs(Arrays.asList(StringUtils.split(cluster, StringUtils.COMMA))); - - // filter ips using selector: - if (service.getSelector() != null && StringUtils.isNotBlank(clientIP)) { - srvedIps = selectorManager.select(service.getSelector(), clientIP, srvedIps); - } - - if (CollectionUtils.isEmpty(srvedIps)) { - - if (Loggers.SRV_LOG.isDebugEnabled()) { - Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName); - } - - result.setCacheMillis(cacheMillis); - result.setLastRefTime(System.currentTimeMillis()); - result.setChecksum(service.getChecksum()); - return result; - } - - long total = 0; - Map> ipMap = new HashMap<>(2); - ipMap.put(Boolean.TRUE, new ArrayList<>()); - ipMap.put(Boolean.FALSE, new ArrayList<>()); - - for (com.alibaba.nacos.naming.core.Instance ip : srvedIps) { - // remove disabled instance: - if (!ip.isEnabled()) { - continue; - } - ipMap.get(ip.isHealthy()).add(ip); - total += 1; - } - - double threshold = service.getProtectThreshold(); - List hosts; - if ((float) ipMap.get(Boolean.TRUE).size() / total <= threshold) { - - Loggers.SRV_LOG.warn("protect threshold reached, return all ips, service: {}", result.getName()); - result.setReachProtectionThreshold(true); - hosts = Stream.of(Boolean.TRUE, Boolean.FALSE).map(ipMap::get).flatMap(Collection::stream) - .map(InstanceUtil::deepCopy) - // set all to `healthy` state to protect - .peek(instance -> instance.setHealthy(true)).collect(Collectors.toCollection(LinkedList::new)); - } else { - result.setReachProtectionThreshold(false); - hosts = new LinkedList<>(ipMap.get(Boolean.TRUE)); - if (!healthOnly) { - hosts.addAll(ipMap.get(Boolean.FALSE)); - } - } - - result.setHosts(hosts); - result.setCacheMillis(cacheMillis); - result.setLastRefTime(System.currentTimeMillis()); - result.setChecksum(service.getChecksum()); - return result; - } - - @Override - public Instance getInstance(String namespaceId, String serviceName, String cluster, String ip, int port) - throws NacosException { - Service service = serviceManager.getService(namespaceId, serviceName); - - serviceManager.checkServiceIsNull(service, namespaceId, serviceName); - - List clusters = new ArrayList<>(); - clusters.add(cluster); - List ips = service.allIPs(clusters); - if (ips == null || ips.isEmpty()) { - throw new NacosException(NacosException.NOT_FOUND, - "no ips found for cluster " + cluster + " in service " + serviceName); - } - for (com.alibaba.nacos.naming.core.Instance each : ips) { - if (each.getIp().equals(ip) && each.getPort() == port) { - return each; - } - } - throw new NacosException(NacosException.NOT_FOUND, "no matched ip found!"); - } - - private void checkIfDisabled(Service service) throws Exception { - if (!service.getEnabled()) { - throw new Exception("service is disabled now."); - } - } - - @Override - public int handleBeat(String namespaceId, String serviceName, String ip, int port, String cluster, - RsInfo clientBeat, BeatInfoInstanceBuilder builder) throws NacosException { - com.alibaba.nacos.naming.core.Instance instance = serviceManager - .getInstance(namespaceId, serviceName, cluster, ip, port); - - if (instance == null) { - if (clientBeat == null) { - return NamingResponseCode.RESOURCE_NOT_FOUND; - } - - Loggers.SRV_LOG.warn("[CLIENT-BEAT] The instance has been removed for health mechanism, " - + "perform data compensation operations, beat: {}, serviceName: {}", clientBeat, serviceName); - instance = parseInstance(builder.setBeatInfo(clientBeat).setServiceName(serviceName).build()); - serviceManager.registerInstance(namespaceId, serviceName, instance); - } - - Service service = serviceManager.getService(namespaceId, serviceName); - - serviceManager.checkServiceIsNull(service, namespaceId, serviceName); - - if (clientBeat == null) { - clientBeat = new RsInfo(); - clientBeat.setIp(ip); - clientBeat.setPort(port); - clientBeat.setCluster(cluster); - } - service.processClientBeat(clientBeat); - return NamingResponseCode.OK; - } - - @Override - public long getHeartBeatInterval(String namespaceId, String serviceName, String ip, int port, String cluster) { - com.alibaba.nacos.naming.core.Instance instance = serviceManager - .getInstance(namespaceId, serviceName, cluster, ip, port); - if (null != instance && instance.containsMetadata(PreservedMetadataKeys.HEART_BEAT_INTERVAL)) { - return instance.getInstanceHeartBeatInterval(); - } - return switchDomain.getClientBeatInterval(); - } - - @Override - public List listAllInstances(String namespaceId, String serviceName) throws NacosException { - Service service = serviceManager.getService(namespaceId, serviceName); - - serviceManager.checkServiceIsNull(service, namespaceId, serviceName); - - return service.allIPs(); - } - - @Override - public List batchUpdateMetadata(String namespaceId, InstanceOperationInfo instanceOperationInfo, - Map metadata) { - return batchOperate(namespaceId, instanceOperationInfo, metadata, UPDATE_INSTANCE_METADATA_ACTION_UPDATE); - } - - @Override - public List batchDeleteMetadata(String namespaceId, InstanceOperationInfo instanceOperationInfo, - Map metadata) throws NacosException { - return batchOperate(namespaceId, instanceOperationInfo, metadata, UPDATE_INSTANCE_METADATA_ACTION_REMOVE); - } - - private List batchOperate(String namespaceId, InstanceOperationInfo instanceOperationInfo, - Map metadata, String updateInstanceMetadataAction) { - List result = new LinkedList<>(); - for (com.alibaba.nacos.naming.core.Instance each : batchOperateMetadata(namespaceId, instanceOperationInfo, - metadata, updateInstanceMetadataAction)) { - result.add(each.getDatumKey() + ":" + (each.isEphemeral() ? EPHEMERAL : PERSIST)); - } - return result; - } - - private List batchOperateMetadata(String namespace, - InstanceOperationInfo instanceOperationInfo, Map metadata, String action) { - Function> operateFunction = instanceOperationContext -> { - try { - return serviceManager.updateMetadata(instanceOperationContext.getNamespace(), - instanceOperationContext.getServiceName(), instanceOperationContext.getEphemeral(), action, - instanceOperationContext.getAll(), instanceOperationContext.getInstances(), metadata); - } catch (NacosException e) { - Loggers.SRV_LOG.warn("UPDATE-METADATA: updateMetadata failed", e); - } - return new ArrayList<>(); - }; - return serviceManager.batchOperate(namespace, instanceOperationInfo, operateFunction); - } - - private com.alibaba.nacos.naming.core.Instance parseInstance(Instance apiInstance) throws NacosException { - com.alibaba.nacos.naming.core.Instance result = instanceUpgradeHelper.toV1(apiInstance); - result.setApp(apiInstance.getMetadata().getOrDefault("app", "DEFAULT")); - result.validate(); - return result; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/InstancePatchObject.java b/naming/src/main/java/com/alibaba/nacos/naming/core/InstancePatchObject.java index 5b4aa4ed9..8d3bb796a 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/InstancePatchObject.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/InstancePatchObject.java @@ -47,11 +47,6 @@ public class InstancePatchObject { this.port = port; } - /** - * Will be deprecated in 2.x. - */ - private String app; - public String getCluster() { return cluster; } @@ -95,12 +90,4 @@ public class InstancePatchObject { public void setEnabled(Boolean enabled) { this.enabled = enabled; } - - public String getApp() { - return app; - } - - public void setApp(String app) { - this.app = app; - } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/Instances.java b/naming/src/main/java/com/alibaba/nacos/naming/core/Instances.java deleted file mode 100644 index 590ac0585..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/Instances.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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.nacos.naming.core; - -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.common.utils.MD5Utils; -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.naming.pojo.Record; -import com.fasterxml.jackson.annotation.JsonIgnore; - -import com.alibaba.nacos.common.utils.StringUtils; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * Package of instance list. - * - * @author nkorange - * @since 1.0.0 - */ -public class Instances implements Record { - - private static final long serialVersionUID = 5500823673993740145L; - - private List instanceList = new ArrayList<>(); - - public List getInstanceList() { - return instanceList; - } - - public void setInstanceList(List instanceList) { - this.instanceList = instanceList; - } - - @Override - public String toString() { - try { - return JacksonUtils.toJson(this); - } catch (Exception e) { - throw new RuntimeException("Instances toJSON failed", e); - } - } - - @Override - @JsonIgnore - public String getChecksum() { - - return recalculateChecksum(); - } - - private String recalculateChecksum() { - StringBuilder sb = new StringBuilder(); - Collections.sort(instanceList); - for (Instance ip : instanceList) { - String string = - ip.getIp() + ":" + ip.getPort() + "_" + ip.getWeight() + "_" + ip.isHealthy() + "_" + ip.isEnabled() - + "_" + ip.getClusterName() + "_" + convertMap2String(ip.getMetadata()); - sb.append(string); - sb.append(','); - } - - return MD5Utils.md5Hex(sb.toString(), Constants.ENCODE); - } - - /** - * Convert Map to KV string with ':'. - * - * @param map map need to be converted - * @return KV string with ':' - */ - public String convertMap2String(Map map) { - - if (map == null || map.isEmpty()) { - return StringUtils.EMPTY; - } - - StringBuilder sb = new StringBuilder(); - List keys = new ArrayList<>(map.keySet()); - Collections.sort(keys); - for (String key : keys) { - sb.append(key); - sb.append(':'); - sb.append(map.get(key)); - sb.append(','); - } - return sb.toString(); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/Service.java b/naming/src/main/java/com/alibaba/nacos/naming/core/Service.java deleted file mode 100644 index e0b3af2db..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/Service.java +++ /dev/null @@ -1,625 +0,0 @@ -/* - * 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.nacos.naming.core; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.selector.Selector; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.common.utils.MD5Utils; -import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.consistency.RecordListener; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteEventListener; -import com.alibaba.nacos.naming.healthcheck.ClientBeatCheckTask; -import com.alibaba.nacos.naming.healthcheck.ClientBeatProcessor; -import com.alibaba.nacos.naming.healthcheck.HealthCheckReactor; -import com.alibaba.nacos.naming.healthcheck.RsInfo; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.naming.pojo.Record; -import com.alibaba.nacos.naming.push.UdpPushService; -import com.alibaba.nacos.naming.selector.NoneSelector; -import com.alibaba.nacos.sys.utils.ApplicationUtils; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.collections.ListUtils; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * Service of Nacos server side - * - *

We introduce a 'service --> cluster --> instance' model, in which service stores a list of clusters, which - * contain a list of instances. - * - *

his class inherits from Service in API module and stores some fields that do not have to expose to client. - * - * @author nkorange - */ -@JsonInclude(Include.NON_NULL) -public class Service extends com.alibaba.nacos.api.naming.pojo.Service implements Record, RecordListener { - - private static final String SERVICE_NAME_SYNTAX = "[0-9a-zA-Z@\\.:_-]+"; - - @JsonIgnore - private ClientBeatCheckTask clientBeatCheckTask = new ClientBeatCheckTask(this); - - /** - * Identify the information used to determine how many isEmpty judgments the service has experienced. - */ - private int finalizeCount = 0; - - private String token; - - private List owners = new ArrayList<>(); - - private Boolean resetWeight = false; - - private Boolean enabled = true; - - private Selector selector = new NoneSelector(); - - private String namespaceId; - - /** - * IP will be deleted if it has not send beat for some time, default timeout is 30 seconds. - */ - private long ipDeleteTimeout = 30 * 1000; - - private volatile long lastModifiedMillis = 0L; - - private volatile String checksum; - - /** - * TODO set customized push expire time. - */ - private long pushCacheMillis = 0L; - - private Map clusterMap = new HashMap<>(); - - public Service() { - } - - public Service(String name) { - super(name); - } - - @JsonIgnore - public UdpPushService getPushService() { - return ApplicationUtils.getBean(UdpPushService.class); - } - - public long getIpDeleteTimeout() { - return ipDeleteTimeout; - } - - public void setIpDeleteTimeout(long ipDeleteTimeout) { - this.ipDeleteTimeout = ipDeleteTimeout; - } - - /** - * Process client beat. - * - * @param rsInfo metrics info of server - */ - public void processClientBeat(final RsInfo rsInfo) { - ClientBeatProcessor clientBeatProcessor = new ClientBeatProcessor(); - clientBeatProcessor.setService(this); - clientBeatProcessor.setRsInfo(rsInfo); - HealthCheckReactor.scheduleNow(clientBeatProcessor); - } - - public Boolean getEnabled() { - return enabled; - } - - public void setEnabled(Boolean enabled) { - this.enabled = enabled; - } - - public long getLastModifiedMillis() { - return lastModifiedMillis; - } - - public void setLastModifiedMillis(long lastModifiedMillis) { - this.lastModifiedMillis = lastModifiedMillis; - } - - public Boolean getResetWeight() { - return resetWeight; - } - - public void setResetWeight(Boolean resetWeight) { - this.resetWeight = resetWeight; - } - - public Selector getSelector() { - return selector; - } - - public void setSelector(Selector selector) { - this.selector = selector; - } - - @Override - public boolean interests(String key) { - return KeyBuilder.matchInstanceListKey(key, namespaceId, getName()); - } - - @Override - public boolean matchUnlistenKey(String key) { - return KeyBuilder.matchInstanceListKey(key, namespaceId, getName()); - } - - @Override - public void onChange(String key, Instances value) throws Exception { - - Loggers.SRV_LOG.info("[NACOS-RAFT] datum is changed, key: {}, value: {}", key, value); - - for (Instance instance : value.getInstanceList()) { - - if (instance == null) { - // Reject this abnormal instance list: - throw new RuntimeException("got null instance " + key); - } - - if (instance.getWeight() > 10000.0D) { - instance.setWeight(10000.0D); - } - - if (instance.getWeight() < 0.01D && instance.getWeight() > 0.0D) { - instance.setWeight(0.01D); - } - } - - updateIPs(value.getInstanceList(), KeyBuilder.matchEphemeralInstanceListKey(key)); - - recalculateChecksum(); - } - - @Override - public void onDelete(String key) throws Exception { - boolean isEphemeral = KeyBuilder.matchEphemeralInstanceListKey(key); - for (Cluster each : clusterMap.values()) { - each.updateIps(Collections.emptyList(), isEphemeral); - } - } - - /** - * Get count of healthy instance in service. - * - * @return count of healthy instance - */ - public int healthyInstanceCount() { - - int healthyCount = 0; - for (Instance instance : allIPs()) { - if (instance.isHealthy()) { - healthyCount++; - } - } - return healthyCount; - } - - public boolean triggerFlag() { - return (healthyInstanceCount() * 1.0 / allIPs().size()) <= getProtectThreshold(); - } - - /** - * Update instances. - * - * @param instances instances - * @param ephemeral whether is ephemeral instance - */ - public void updateIPs(Collection instances, boolean ephemeral) { - Map> ipMap = new HashMap<>(clusterMap.size()); - for (String clusterName : clusterMap.keySet()) { - ipMap.put(clusterName, new ArrayList<>()); - } - - for (Instance instance : instances) { - try { - if (instance == null) { - Loggers.SRV_LOG.error("[NACOS-DOM] received malformed ip: null"); - continue; - } - - if (StringUtils.isEmpty(instance.getClusterName())) { - instance.setClusterName(UtilsAndCommons.DEFAULT_CLUSTER_NAME); - } - - if (!clusterMap.containsKey(instance.getClusterName())) { - Loggers.SRV_LOG.warn( - "cluster: {} not found, ip: {}, will create new cluster with default configuration.", - instance.getClusterName(), instance.toJson()); - Cluster cluster = new Cluster(instance.getClusterName(), this); - cluster.init(); - getClusterMap().put(instance.getClusterName(), cluster); - } - - List clusterIPs = ipMap.get(instance.getClusterName()); - if (clusterIPs == null) { - clusterIPs = new LinkedList<>(); - ipMap.put(instance.getClusterName(), clusterIPs); - } - - clusterIPs.add(instance); - } catch (Exception e) { - Loggers.SRV_LOG.error("[NACOS-DOM] failed to process ip: " + instance, e); - } - } - - for (Map.Entry> entry : ipMap.entrySet()) { - //make every ip mine - List entryIPs = entry.getValue(); - clusterMap.get(entry.getKey()).updateIps(entryIPs, ephemeral); - } - - setLastModifiedMillis(System.currentTimeMillis()); - getPushService().serviceChanged(this); - ApplicationUtils.getBean(DoubleWriteEventListener.class).doubleWriteToV2(this, ephemeral); - StringBuilder stringBuilder = new StringBuilder(); - - for (Instance instance : allIPs()) { - stringBuilder.append(instance.toIpAddr()).append('_').append(instance.isHealthy()).append(','); - } - - Loggers.EVT_LOG.info("[IP-UPDATED] namespace: {}, service: {}, ips: {}", getNamespaceId(), getName(), - stringBuilder.toString()); - - } - - /** - * Init service. - */ - public void init() { - HealthCheckReactor.scheduleCheck(clientBeatCheckTask); - for (Map.Entry entry : clusterMap.entrySet()) { - entry.getValue().setService(this); - entry.getValue().init(); - } - } - - /** - * Destroy service. - * - * @throws Exception exception - */ - public void destroy() throws Exception { - for (Map.Entry entry : clusterMap.entrySet()) { - entry.getValue().destroy(); - } - HealthCheckReactor.cancelCheck(clientBeatCheckTask); - ApplicationUtils.getBean(DoubleWriteEventListener.class) - .doubleWriteMetadataToV2(this, this.allIPs(false).isEmpty(), true); - } - - /** - * Judge whether service has instance. - * - * @return true if no instance, otherwise false - */ - public boolean isEmpty() { - for (Map.Entry entry : clusterMap.entrySet()) { - final Cluster cluster = entry.getValue(); - if (!cluster.isEmpty()) { - return false; - } - } - return true; - } - - /** - * Get all instance. - * - * @return list of all instance - */ - public List allIPs() { - List result = new ArrayList<>(); - for (Map.Entry entry : clusterMap.entrySet()) { - result.addAll(entry.getValue().allIPs()); - } - - return result; - } - - /** - * Get all instance of ephemeral or consistency. - * - * @param ephemeral whether ephemeral instance - * @return all instance of ephemeral if @param ephemeral = true, otherwise all instance of consistency - */ - public List allIPs(boolean ephemeral) { - List result = new ArrayList<>(); - for (Map.Entry entry : clusterMap.entrySet()) { - result.addAll(entry.getValue().allIPs(ephemeral)); - } - - return result; - } - - /** - * Get all instance from input clusters. - * - * @param clusters cluster names - * @return all instance from input clusters. - */ - public List allIPs(List clusters) { - List result = new ArrayList<>(); - for (String cluster : clusters) { - Cluster clusterObj = clusterMap.get(cluster); - if (clusterObj == null) { - continue; - } - - result.addAll(clusterObj.allIPs()); - } - return result; - } - - /** - * Get all instance from input clusters. - * - * @param clusters cluster names - * @return all instance from input clusters, if clusters is empty, return all cluster - */ - public List srvIPs(List clusters) { - if (CollectionUtils.isEmpty(clusters)) { - clusters = new ArrayList<>(); - clusters.addAll(clusterMap.keySet()); - } - return allIPs(clusters); - } - - public String toJson() { - return JacksonUtils.toJson(this); - } - - @JsonIgnore - public String getServiceString() { - Map serviceObject = new HashMap(10); - Service service = this; - - serviceObject.put("name", service.getName()); - - List ips = service.allIPs(); - int invalidIpCount = 0; - int ipCount = 0; - for (Instance ip : ips) { - if (!ip.isHealthy()) { - invalidIpCount++; - } - - ipCount++; - } - - serviceObject.put("ipCount", ipCount); - serviceObject.put("invalidIPCount", invalidIpCount); - - serviceObject.put("owners", service.getOwners()); - serviceObject.put("token", service.getToken()); - - serviceObject.put("protectThreshold", service.getProtectThreshold()); - - List clustersList = new ArrayList(); - - for (Map.Entry entry : service.getClusterMap().entrySet()) { - Cluster cluster = entry.getValue(); - - Map clusters = new HashMap(10); - clusters.put("name", cluster.getName()); - clusters.put("healthChecker", cluster.getHealthChecker()); - clusters.put("defCkport", cluster.getDefCkport()); - clusters.put("defIPPort", cluster.getDefIPPort()); - clusters.put("useIPPort4Check", cluster.isUseIPPort4Check()); - clusters.put("sitegroup", cluster.getSitegroup()); - - clustersList.add(clusters); - } - - serviceObject.put("clusters", clustersList); - - try { - return JacksonUtils.toJson(serviceObject); - } catch (Exception e) { - throw new RuntimeException("Service toJson failed", e); - } - } - - public String getToken() { - return token; - } - - public void setToken(String token) { - this.token = token; - } - - public List getOwners() { - return owners; - } - - public void setOwners(List owners) { - this.owners = owners; - } - - public Map getClusterMap() { - return clusterMap; - } - - public void setClusterMap(Map clusterMap) { - this.clusterMap = clusterMap; - } - - public String getNamespaceId() { - return namespaceId; - } - - public void setNamespaceId(String namespaceId) { - this.namespaceId = namespaceId; - } - - /** - * Update from other service. - * - * @param vDom other service - */ - public void update(Service vDom) { - - if (!StringUtils.equals(token, vDom.getToken())) { - Loggers.SRV_LOG.info("[SERVICE-UPDATE] service: {}, token: {} -> {}", getName(), token, vDom.getToken()); - token = vDom.getToken(); - } - - if (!ListUtils.isEqualList(owners, vDom.getOwners())) { - Loggers.SRV_LOG.info("[SERVICE-UPDATE] service: {}, owners: {} -> {}", getName(), owners, vDom.getOwners()); - owners = vDom.getOwners(); - } - - if (getProtectThreshold() != vDom.getProtectThreshold()) { - Loggers.SRV_LOG.info("[SERVICE-UPDATE] service: {}, protectThreshold: {} -> {}", getName(), - getProtectThreshold(), vDom.getProtectThreshold()); - setProtectThreshold(vDom.getProtectThreshold()); - } - - if (resetWeight != vDom.getResetWeight().booleanValue()) { - Loggers.SRV_LOG.info("[SERVICE-UPDATE] service: {}, resetWeight: {} -> {}", getName(), resetWeight, - vDom.getResetWeight()); - resetWeight = vDom.getResetWeight(); - } - - if (enabled != vDom.getEnabled().booleanValue()) { - Loggers.SRV_LOG.info("[SERVICE-UPDATE] service: {}, enabled: {} -> {}", getName(), enabled, - vDom.getEnabled()); - enabled = vDom.getEnabled(); - } - - selector = vDom.getSelector(); - - setMetadata(vDom.getMetadata()); - - updateOrAddCluster(vDom.getClusterMap().values()); - remvDeadClusters(this, vDom); - - Loggers.SRV_LOG.info("cluster size, new: {}, old: {}", getClusterMap().size(), vDom.getClusterMap().size()); - - recalculateChecksum(); - ApplicationUtils.getBean(DoubleWriteEventListener.class) - .doubleWriteMetadataToV2(this, vDom.allIPs(false).isEmpty(), false); - } - - @Override - public String getChecksum() { - if (StringUtils.isEmpty(checksum)) { - recalculateChecksum(); - } - - return checksum; - } - - /** - * Re-calculate checksum of service. - */ - public synchronized void recalculateChecksum() { - List ips = allIPs(); - - StringBuilder ipsString = new StringBuilder(); - String serviceString = getServiceString(); - ipsString.append(serviceString); - - if (Loggers.SRV_LOG.isDebugEnabled()) { - Loggers.SRV_LOG.debug("service to json: " + serviceString); - } - - if (CollectionUtils.isNotEmpty(ips)) { - Collections.sort(ips); - } - - for (Instance ip : ips) { - String string = ip.getIp() + ":" + ip.getPort() + "_" + ip.getWeight() + "_" + ip.isHealthy() + "_" - + ip.getClusterName(); - ipsString.append(string); - ipsString.append(','); - } - - checksum = MD5Utils.md5Hex(ipsString.toString(), Constants.ENCODE); - } - - private void updateOrAddCluster(Collection clusters) { - for (Cluster cluster : clusters) { - Cluster oldCluster = clusterMap.get(cluster.getName()); - if (oldCluster != null) { - oldCluster.setService(this); - oldCluster.update(cluster); - } else { - cluster.init(); - cluster.setService(this); - clusterMap.put(cluster.getName(), cluster); - } - } - } - - private void remvDeadClusters(Service oldDom, Service newDom) { - Collection oldClusters = oldDom.getClusterMap().values(); - Collection newClusters = newDom.getClusterMap().values(); - List deadClusters = (List) CollectionUtils.subtract(oldClusters, newClusters); - for (Cluster cluster : deadClusters) { - oldDom.getClusterMap().remove(cluster.getName()); - - cluster.destroy(); - } - } - - public int getFinalizeCount() { - return finalizeCount; - } - - public void setFinalizeCount(int finalizeCount) { - this.finalizeCount = finalizeCount; - } - - public void addCluster(Cluster cluster) { - clusterMap.put(cluster.getName(), cluster); - } - - /** - * Judge whether service is validate. - * - * @throws IllegalArgumentException if service is not validate - */ - public void validate() { - String serviceName = getName(); - if (Objects.isNull(serviceName)) { - throw new IllegalArgumentException("service name can not be null!"); - } - if (!serviceName.matches(SERVICE_NAME_SYNTAX)) { - throw new IllegalArgumentException( - "dom name can only have these characters: 0-9a-zA-Z-._:, current: " + serviceName); - } - for (Cluster cluster : clusterMap.values()) { - cluster.validate(); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java deleted file mode 100644 index 4a9a2c318..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java +++ /dev/null @@ -1,1170 +0,0 @@ -/* - * 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.nacos.naming.core; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.utils.NamingUtils; -import com.alibaba.nacos.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.InternetAddressUtil; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.core.cluster.Member; -import com.alibaba.nacos.core.cluster.ServerMemberManager; -import com.alibaba.nacos.naming.consistency.ConsistencyService; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.consistency.RecordListener; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeer; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeerSet; -import com.alibaba.nacos.naming.core.v2.cleaner.EmptyServiceAutoCleaner; -import com.alibaba.nacos.naming.misc.GlobalExecutor; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.Message; -import com.alibaba.nacos.naming.misc.NetUtils; -import com.alibaba.nacos.naming.misc.ServiceStatusSynchronizer; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.misc.Synchronizer; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.naming.pojo.InstanceOperationContext; -import com.alibaba.nacos.naming.pojo.InstanceOperationInfo; -import com.alibaba.nacos.naming.push.UdpPushService; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.alibaba.nacos.common.utils.StringUtils; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import javax.annotation.Resource; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.StringJoiner; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentSkipListMap; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.stream.Collectors; - -import static com.alibaba.nacos.naming.misc.UtilsAndCommons.UPDATE_INSTANCE_METADATA_ACTION_REMOVE; -import static com.alibaba.nacos.naming.misc.UtilsAndCommons.UPDATE_INSTANCE_METADATA_ACTION_UPDATE; - -/** - * Core manager storing all services in Nacos. - * - * @author nkorange - */ -@Component -public class ServiceManager implements RecordListener { - - /** - * Map(namespace, Map(group::serviceName, Service)). - */ - private final Map> serviceMap = new ConcurrentHashMap<>(); - - private final LinkedBlockingDeque toBeUpdatedServicesQueue = new LinkedBlockingDeque<>(1024 * 1024); - - private final Synchronizer synchronizer = new ServiceStatusSynchronizer(); - - @Resource(name = "consistencyDelegate") - private ConsistencyService consistencyService; - - private final SwitchDomain switchDomain; - - private final DistroMapper distroMapper; - - private final ServerMemberManager memberManager; - - private final UdpPushService pushService; - - private final RaftPeerSet raftPeerSet; - - @Value("${nacos.naming.empty-service.auto-clean:false}") - private boolean emptyServiceAutoClean; - - @Value("${nacos.naming.empty-service.clean.initial-delay-ms:60000}") - private int cleanEmptyServiceDelay; - - @Value("${nacos.naming.empty-service.clean.period-time-ms:20000}") - private int cleanEmptyServicePeriod; - - public ServiceManager(SwitchDomain switchDomain, DistroMapper distroMapper, ServerMemberManager memberManager, - UdpPushService pushService, RaftPeerSet raftPeerSet) { - this.switchDomain = switchDomain; - this.distroMapper = distroMapper; - this.memberManager = memberManager; - this.pushService = pushService; - this.raftPeerSet = raftPeerSet; - } - - /** - * Init service maneger. - */ - @PostConstruct - public void init() { - GlobalExecutor.scheduleServiceReporter(new ServiceReporter(), 60000, TimeUnit.MILLISECONDS); - - GlobalExecutor.submitServiceUpdateManager(new UpdatedServiceProcessor()); - - if (emptyServiceAutoClean) { - - Loggers.SRV_LOG.info("open empty service auto clean job, initialDelay : {} ms, period : {} ms", - cleanEmptyServiceDelay, cleanEmptyServicePeriod); - - // delay 60s, period 20s; - - // This task is not recommended to be performed frequently in order to avoid - // the possibility that the service cache information may just be deleted - // and then created due to the heartbeat mechanism - - GlobalExecutor - .scheduleServiceAutoClean(new EmptyServiceAutoCleaner(this, distroMapper), cleanEmptyServiceDelay, - cleanEmptyServicePeriod); - } - - try { - Loggers.SRV_LOG.info("listen for service meta change"); - consistencyService.listen(KeyBuilder.SERVICE_META_KEY_PREFIX, this); - } catch (NacosException e) { - Loggers.SRV_LOG.error("listen for service meta change failed!"); - } - } - - public Map chooseServiceMap(String namespaceId) { - return serviceMap.get(namespaceId); - } - - /** - * Add a service into queue to update. - * - * @param namespaceId namespace - * @param serviceName service name - * @param serverIP target server ip - * @param checksum checksum of service - */ - public synchronized void addUpdatedServiceToQueue(String namespaceId, String serviceName, String serverIP, String checksum) { - try { - toBeUpdatedServicesQueue - .offer(new ServiceKey(namespaceId, serviceName, serverIP, checksum), 5, TimeUnit.MILLISECONDS); - } catch (Exception e) { - toBeUpdatedServicesQueue.poll(); - toBeUpdatedServicesQueue.add(new ServiceKey(namespaceId, serviceName, serverIP, checksum)); - Loggers.SRV_LOG.error("[DOMAIN-STATUS] Failed to add service to be updated to queue.", e); - } - } - - @Override - public boolean interests(String key) { - return KeyBuilder.matchServiceMetaKey(key) && !KeyBuilder.matchSwitchKey(key); - } - - @Override - public boolean matchUnlistenKey(String key) { - return KeyBuilder.matchServiceMetaKey(key) && !KeyBuilder.matchSwitchKey(key); - } - - @Override - public void onChange(String key, Service service) throws Exception { - try { - if (service == null) { - Loggers.SRV_LOG.warn("received empty push from raft, key: {}", key); - return; - } - - if (StringUtils.isBlank(service.getNamespaceId())) { - service.setNamespaceId(Constants.DEFAULT_NAMESPACE_ID); - } - - Loggers.RAFT.info("[RAFT-NOTIFIER] datum is changed, key: {}, value: {}", key, service); - - Service oldDom = getService(service.getNamespaceId(), service.getName()); - - if (oldDom != null) { - oldDom.update(service); - // re-listen to handle the situation when the underlying listener is removed: - consistencyService - .listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), true), - oldDom); - consistencyService - .listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), false), - oldDom); - } else { - putServiceAndInit(service); - } - } catch (Throwable e) { - Loggers.SRV_LOG.error("[NACOS-SERVICE] error while processing service update", e); - } - } - - @Override - public void onDelete(String key) throws Exception { - String namespace = KeyBuilder.getNamespace(key); - String name = KeyBuilder.getServiceName(key); - Service service = chooseServiceMap(namespace).get(name); - Loggers.RAFT.info("[RAFT-NOTIFIER] datum is deleted, key: {}", key); - - if (service != null) { - cleanupService(namespace, name, service); - } - - chooseServiceMap(namespace).remove(name); - } - - private class UpdatedServiceProcessor implements Runnable { - - //get changed service from other server asynchronously - @Override - public void run() { - ServiceKey serviceKey = null; - - try { - while (true) { - try { - serviceKey = toBeUpdatedServicesQueue.take(); - } catch (Exception e) { - Loggers.EVT_LOG.error("[UPDATE-DOMAIN] Exception while taking item from LinkedBlockingDeque."); - } - - if (serviceKey == null) { - continue; - } - GlobalExecutor.submitServiceUpdate(new ServiceUpdater(serviceKey)); - } - } catch (Exception e) { - Loggers.EVT_LOG.error("[UPDATE-DOMAIN] Exception while update service: {}", serviceKey, e); - } - } - } - - private class ServiceUpdater implements Runnable { - - String namespaceId; - - String serviceName; - - String serverIP; - - public ServiceUpdater(ServiceKey serviceKey) { - this.namespaceId = serviceKey.getNamespaceId(); - this.serviceName = serviceKey.getServiceName(); - this.serverIP = serviceKey.getServerIP(); - } - - @Override - public void run() { - try { - updatedHealthStatus(namespaceId, serviceName, serverIP); - } catch (Exception e) { - Loggers.SRV_LOG - .warn("[DOMAIN-UPDATER] Exception while update service: {} from {}, error: {}", serviceName, - serverIP, e); - } - } - } - - public RaftPeer getMySelfClusterState() { - return raftPeerSet.local(); - } - - /** - * Update health status of instance in service. - * - * @param namespaceId namespace - * @param serviceName service name - * @param serverIP source server Ip - */ - public void updatedHealthStatus(String namespaceId, String serviceName, String serverIP) { - Message msg = synchronizer.get(serverIP, UtilsAndCommons.assembleFullServiceName(namespaceId, serviceName)); - JsonNode serviceJson = JacksonUtils.toObj(msg.getData()); - - ArrayNode ipList = (ArrayNode) serviceJson.get("ips"); - Map ipsMap = new HashMap<>(ipList.size()); - for (int i = 0; i < ipList.size(); i++) { - - String ip = ipList.get(i).asText(); - String[] strings = ip.split("_"); - ipsMap.put(strings[0], strings[1]); - } - - Service service = getService(namespaceId, serviceName); - - if (service == null) { - return; - } - - boolean changed = false; - - List instances = service.allIPs(); - for (Instance instance : instances) { - - boolean valid = Boolean.parseBoolean(ipsMap.get(instance.toIpAddr())); - if (valid != instance.isHealthy()) { - changed = true; - instance.setHealthy(valid); - Loggers.EVT_LOG.info("{} {SYNC} IP-{} : {}:{}@{}", serviceName, - (instance.isHealthy() ? "ENABLED" : "DISABLED"), instance.getIp(), instance.getPort(), - instance.getClusterName()); - } - } - - if (changed) { - pushService.serviceChanged(service); - if (Loggers.EVT_LOG.isDebugEnabled()) { - StringBuilder stringBuilder = new StringBuilder(); - List allIps = service.allIPs(); - for (Instance instance : allIps) { - stringBuilder.append(instance.toIpAddr()).append('_').append(instance.isHealthy()).append(','); - } - Loggers.EVT_LOG - .debug("[HEALTH-STATUS-UPDATED] namespace: {}, service: {}, ips: {}", service.getNamespaceId(), - service.getName(), stringBuilder); - } - } - - } - - public Set getAllServiceNames(String namespaceId) { - return serviceMap.get(namespaceId).keySet(); - } - - public Map> getAllServiceNames() { - - Map> namesMap = new HashMap<>(16); - for (String namespaceId : serviceMap.keySet()) { - namesMap.put(namespaceId, serviceMap.get(namespaceId).keySet()); - } - return namesMap; - } - - public Set getAllNamespaces() { - return serviceMap.keySet(); - } - - public List getAllServiceNameList(String namespaceId) { - if (chooseServiceMap(namespaceId) == null) { - return new ArrayList<>(); - } - return new ArrayList<>(chooseServiceMap(namespaceId).keySet()); - } - - public Map> getResponsibleServices() { - Map> result = new HashMap<>(16); - for (String namespaceId : serviceMap.keySet()) { - result.put(namespaceId, new HashSet<>()); - for (Map.Entry entry : serviceMap.get(namespaceId).entrySet()) { - Service service = entry.getValue(); - if (distroMapper.responsible(entry.getKey())) { - result.get(namespaceId).add(service); - } - } - } - return result; - } - - public int getResponsibleServiceCount() { - int serviceCount = 0; - for (String namespaceId : serviceMap.keySet()) { - for (Map.Entry entry : serviceMap.get(namespaceId).entrySet()) { - if (distroMapper.responsible(entry.getKey())) { - serviceCount++; - } - } - } - return serviceCount; - } - - public int getResponsibleInstanceCount() { - Map> responsibleServices = getResponsibleServices(); - int count = 0; - for (String namespaceId : responsibleServices.keySet()) { - for (Service service : responsibleServices.get(namespaceId)) { - count += service.allIPs().size(); - } - } - - return count; - } - - /** - * Fast remove service. - * - *

Remove service bu async. - * - * @param namespaceId namespace - * @param serviceName service name - * @throws NacosException exception - */ - public void easyRemoveService(String namespaceId, String serviceName) throws NacosException { - - Service service = getService(namespaceId, serviceName); - if (service == null) { - throw new NacosException(NacosException.INVALID_PARAM, - "specified service not exist, serviceName : " + serviceName); - } - - consistencyService.remove(KeyBuilder.buildServiceMetaKey(namespaceId, serviceName)); - } - - public void addOrReplaceService(Service service) throws NacosException { - consistencyService.put(KeyBuilder.buildServiceMetaKey(service.getNamespaceId(), service.getName()), service); - } - - public void createEmptyService(String namespaceId, String serviceName, boolean local) throws NacosException { - createServiceIfAbsent(namespaceId, serviceName, local, null); - } - - /** - * Create service if not exist. - * - * @param namespaceId namespace - * @param serviceName service name - * @param local whether create service by local - * @param cluster cluster - * @throws NacosException nacos exception - */ - public void createServiceIfAbsent(String namespaceId, String serviceName, boolean local, Cluster cluster) - throws NacosException { - Service service = getService(namespaceId, serviceName); - //return if service already exists - if (service != null) { - return; - } - - Loggers.SRV_LOG.info("creating empty service {}:{}", namespaceId, serviceName); - service = new Service(); - service.setName(serviceName); - service.setNamespaceId(namespaceId); - service.setGroupName(NamingUtils.getGroupName(serviceName)); - // now validate the service. if failed, exception will be thrown - service.setLastModifiedMillis(System.currentTimeMillis()); - service.recalculateChecksum(); - if (cluster != null) { - cluster.setService(service); - service.getClusterMap().put(cluster.getName(), cluster); - } - service.validate(); - - putServiceAndInit(service); - if (!local) { - addOrReplaceService(service); - } - } - - /** - * Register an instance to a service in AP mode. - * - *

This method creates service or cluster silently if they don't exist. - * - * @param namespaceId id of namespace - * @param serviceName service name - * @param instance instance to register - * @throws Exception any error occurred in the process - */ - public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException { - - NamingUtils.checkInstanceIsLegal(instance); - - createEmptyService(namespaceId, serviceName, instance.isEphemeral()); - - Service service = getService(namespaceId, serviceName); - - checkServiceIsNull(service, namespaceId, serviceName); - - addInstance(namespaceId, serviceName, instance.isEphemeral(), instance); - } - - /** - * Update instance to service. - * - * @param namespaceId namespace - * @param serviceName service name - * @param instance instance - * @throws NacosException nacos exception - */ - public void updateInstance(String namespaceId, String serviceName, Instance instance) throws NacosException { - - Service service = getService(namespaceId, serviceName); - - checkServiceIsNull(service, namespaceId, serviceName); - - if (!service.allIPs().contains(instance)) { - throw new NacosException(NacosException.INVALID_PARAM, "instance not exist: " + instance); - } - - addInstance(namespaceId, serviceName, instance.isEphemeral(), instance); - } - - /** - * Update instance's metadata. - * - * @param namespaceId namespace - * @param serviceName service name - * @param action update or remove - * @param ips need update instances - * @param metadata target metadata - * @return update succeed instances - * @throws NacosException nacos exception - */ - public List updateMetadata(String namespaceId, String serviceName, boolean isEphemeral, String action, - boolean all, List ips, Map metadata) throws NacosException { - - Service service = getService(namespaceId, serviceName); - - checkServiceIsNull(service, namespaceId, serviceName); - - List locatedInstance = getLocatedInstance(namespaceId, serviceName, isEphemeral, all, ips); - - if (CollectionUtils.isEmpty(locatedInstance)) { - throw new NacosException(NacosException.INVALID_PARAM, "not locate instances, input instances: " + ips); - } - - if (UPDATE_INSTANCE_METADATA_ACTION_UPDATE.equals(action)) { - locatedInstance.forEach(ele -> ele.getMetadata().putAll(metadata)); - } else if (UPDATE_INSTANCE_METADATA_ACTION_REMOVE.equals(action)) { - Set removeKeys = metadata.keySet(); - for (String removeKey : removeKeys) { - locatedInstance.forEach(ele -> ele.getMetadata().remove(removeKey)); - } - } - Instance[] instances = new Instance[locatedInstance.size()]; - locatedInstance.toArray(instances); - - addInstance(namespaceId, serviceName, isEphemeral, instances); - - return locatedInstance; - } - - /** - * Check if the service is null. - * - * @param service service - * @param namespaceId namespace - * @param serviceName service name - * @throws NacosException nacos exception - */ - public void checkServiceIsNull(Service service, String namespaceId, String serviceName) throws NacosException { - if (service == null) { - throw new NacosException(NacosException.INVALID_PARAM, - "service not found, namespace: " + namespaceId + ", serviceName: " + serviceName); - } - } - - /** - * Locate consistency's datum by all or instances provided. - * - * @param namespaceId namespace - * @param serviceName serviceName - * @param isEphemeral isEphemeral - * @param all get from consistencyService directly - * @param waitLocateInstance instances provided - * @return located instances - * @throws NacosException nacos exception - */ - public List getLocatedInstance(String namespaceId, String serviceName, boolean isEphemeral, boolean all, - List waitLocateInstance) throws NacosException { - List locatedInstance; - - //need the newest data from consistencyService - Datum datum = consistencyService.get(KeyBuilder.buildInstanceListKey(namespaceId, serviceName, isEphemeral)); - if (datum == null) { - throw new NacosException(NacosException.NOT_FOUND, - "instances from consistencyService not exist, namespace: " + namespaceId + ", service: " - + serviceName + ", ephemeral: " + isEphemeral); - } - - if (all) { - locatedInstance = ((Instances) datum.value).getInstanceList(); - } else { - locatedInstance = new ArrayList<>(); - for (Instance instance : waitLocateInstance) { - Instance located = locateInstance(((Instances) datum.value).getInstanceList(), instance); - if (located == null) { - continue; - } - locatedInstance.add(located); - } - } - - return locatedInstance; - } - - private Instance locateInstance(List sources, Instance target) { - if (CollectionUtils.isEmpty(sources)) { - return null; - } - - for (Instance element : sources) { - //also need clusterName equals, the same instance maybe exist in two cluster. - if (Objects.equals(element, target) && Objects.equals(element.getClusterName(), target.getClusterName())) { - return element; - } - } - return null; - } - - /** - * Add instance to service. - * - * @param namespaceId namespace - * @param serviceName service name - * @param ephemeral whether instance is ephemeral - * @param ips instances - * @throws NacosException nacos exception - */ - public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips) - throws NacosException { - - String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral); - - Service service = getService(namespaceId, serviceName); - - synchronized (service) { - List instanceList = addIpAddresses(service, ephemeral, ips); - - Instances instances = new Instances(); - instances.setInstanceList(instanceList); - - consistencyService.put(key, instances); - } - } - - /** - * Remove instance from service. - * - * @param namespaceId namespace - * @param serviceName service name - * @param ephemeral whether instance is ephemeral - * @param ips instances - * @throws NacosException nacos exception - */ - public void removeInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips) - throws NacosException { - Service service = getService(namespaceId, serviceName); - - synchronized (service) { - removeInstance(namespaceId, serviceName, ephemeral, service, ips); - } - } - - private void removeInstance(String namespaceId, String serviceName, boolean ephemeral, Service service, - Instance... ips) throws NacosException { - - String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral); - - List instanceList = substractIpAddresses(service, ephemeral, ips); - - Instances instances = new Instances(); - instances.setInstanceList(instanceList); - - consistencyService.put(key, instances); - } - - public Instance getInstance(String namespaceId, String serviceName, String cluster, String ip, int port) { - Service service = getService(namespaceId, serviceName); - if (service == null) { - return null; - } - - List clusters = new ArrayList<>(); - clusters.add(cluster); - - List ips = service.allIPs(clusters); - if (ips == null || ips.isEmpty()) { - return null; - } - - for (Instance instance : ips) { - if (instance.getIp().equals(ip) && instance.getPort() == port) { - return instance; - } - } - - return null; - } - - /** - * batch operate kinds of resources. - * - * @param namespace namespace. - * @param operationInfo operation resources description. - * @param operateFunction some operation defined by kinds of situation. - */ - public List batchOperate(String namespace, InstanceOperationInfo operationInfo, - Function> operateFunction) { - List operatedInstances = new ArrayList<>(); - try { - String serviceName = operationInfo.getServiceName(); - NamingUtils.checkServiceNameFormat(serviceName); - // type: ephemeral/persist - InstanceOperationContext operationContext; - String type = operationInfo.getConsistencyType(); - if (!StringUtils.isEmpty(type)) { - switch (type) { - case UtilsAndCommons.EPHEMERAL: - operationContext = new InstanceOperationContext(namespace, serviceName, true, true); - operatedInstances.addAll(operateFunction.apply(operationContext)); - break; - case UtilsAndCommons.PERSIST: - operationContext = new InstanceOperationContext(namespace, serviceName, false, true); - operatedInstances.addAll(operateFunction.apply(operationContext)); - break; - default: - Loggers.SRV_LOG - .warn("UPDATE-METADATA: services.all value is illegal, it should be ephemeral/persist. ignore the service '" - + serviceName + "'"); - break; - } - } else { - List instances = (List) operationInfo.getInstances(); - if (!CollectionUtils.isEmpty(instances)) { - //ephemeral:instances or persist:instances - Map> instanceMap = instances.stream() - .collect(Collectors.groupingBy(ele -> ele.isEphemeral())); - - for (Map.Entry> entry : instanceMap.entrySet()) { - operationContext = new InstanceOperationContext(namespace, serviceName, entry.getKey(), false, - entry.getValue()); - operatedInstances.addAll(operateFunction.apply(operationContext)); - } - } - } - } catch (Exception e) { - Loggers.SRV_LOG.warn("UPDATE-METADATA: update metadata failed, ignore the service '" + operationInfo - .getServiceName() + "'", e); - } - return operatedInstances; - } - - /** - * Compare and get new instance list. - * - * @param service service - * @param action {@link UtilsAndCommons#UPDATE_INSTANCE_ACTION_REMOVE} or {@link UtilsAndCommons#UPDATE_INSTANCE_ACTION_ADD} - * @param ephemeral whether instance is ephemeral - * @param ips instances - * @return instance list after operation - * @throws NacosException nacos exception - */ - public List updateIpAddresses(Service service, String action, boolean ephemeral, Instance... ips) - throws NacosException { - - Datum datum = consistencyService - .get(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), ephemeral)); - - List currentIPs = service.allIPs(ephemeral); - Map currentInstances = new HashMap<>(currentIPs.size()); - Set currentInstanceIds = CollectionUtils.set(); - - for (Instance instance : currentIPs) { - currentInstances.put(instance.toIpAddr(), instance); - currentInstanceIds.add(instance.getInstanceId()); - } - - Map instanceMap; - if (datum != null && null != datum.value) { - instanceMap = setValid(((Instances) datum.value).getInstanceList(), currentInstances); - } else { - instanceMap = new HashMap<>(ips.length); - } - - for (Instance instance : ips) { - if (!service.getClusterMap().containsKey(instance.getClusterName())) { - Cluster cluster = new Cluster(instance.getClusterName(), service); - cluster.init(); - service.getClusterMap().put(instance.getClusterName(), cluster); - Loggers.SRV_LOG - .warn("cluster: {} not found, ip: {}, will create new cluster with default configuration.", - instance.getClusterName(), instance.toJson()); - } - - if (UtilsAndCommons.UPDATE_INSTANCE_ACTION_REMOVE.equals(action)) { - instanceMap.remove(instance.getDatumKey()); - } else { - Instance oldInstance = instanceMap.get(instance.getDatumKey()); - if (oldInstance != null) { - instance.setInstanceId(oldInstance.getInstanceId()); - } else { - instance.setInstanceId(instance.generateInstanceId(currentInstanceIds)); - } - instanceMap.put(instance.getDatumKey(), instance); - } - - } - - if (instanceMap.size() <= 0 && UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD.equals(action)) { - throw new IllegalArgumentException( - "ip list can not be empty, service: " + service.getName() + ", ip list: " + JacksonUtils - .toJson(instanceMap.values())); - } - - return new ArrayList<>(instanceMap.values()); - } - - private List substractIpAddresses(Service service, boolean ephemeral, Instance... ips) - throws NacosException { - return updateIpAddresses(service, UtilsAndCommons.UPDATE_INSTANCE_ACTION_REMOVE, ephemeral, ips); - } - - private List addIpAddresses(Service service, boolean ephemeral, Instance... ips) throws NacosException { - return updateIpAddresses(service, UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD, ephemeral, ips); - } - - private Map setValid(List oldInstances, Map map) { - - Map instanceMap = new HashMap<>(oldInstances.size()); - for (Instance instance : oldInstances) { - Instance instance1 = map.get(instance.toIpAddr()); - if (instance1 != null) { - instance.setHealthy(instance1.isHealthy()); - instance.setLastBeat(instance1.getLastBeat()); - } - instanceMap.put(instance.getDatumKey(), instance); - } - return instanceMap; - } - - public Service getService(String namespaceId, String serviceName) { - Map service = this.serviceMap.get(namespaceId); - if (service == null) { - return null; - } - return service.get(serviceName); - } - - public boolean containService(String namespaceId, String serviceName) { - return getService(namespaceId, serviceName) != null; - } - - /** - * Put service into manager. - * - * @param service service - */ - public void putService(Service service) { - if (!serviceMap.containsKey(service.getNamespaceId())) { - serviceMap.putIfAbsent(service.getNamespaceId(), new ConcurrentSkipListMap<>()); - } - serviceMap.get(service.getNamespaceId()).putIfAbsent(service.getName(), service); - } - - private void putServiceAndInit(Service service) throws NacosException { - putService(service); - service = getService(service.getNamespaceId(), service.getName()); - service.init(); - consistencyService - .listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), true), service); - consistencyService - .listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), false), service); - Loggers.SRV_LOG.info("[NEW-SERVICE] {}", service.toJson()); - } - - /** - * Search services. - * - * @param namespaceId namespace - * @param regex search regex - * @return list of service which searched - */ - public List searchServices(String namespaceId, String regex) { - List result = new ArrayList<>(); - for (Map.Entry entry : chooseServiceMap(namespaceId).entrySet()) { - Service service = entry.getValue(); - String key = service.getName() + ":" + service.getOwners().toString(); - if (key.matches(regex)) { - result.add(service); - } - } - - return result; - } - - public int getServiceCount() { - int serviceCount = 0; - for (String namespaceId : serviceMap.keySet()) { - serviceCount += serviceMap.get(namespaceId).size(); - } - return serviceCount; - } - - public int getInstanceCount() { - int total = 0; - for (String namespaceId : serviceMap.keySet()) { - for (Service service : serviceMap.get(namespaceId).values()) { - total += service.allIPs().size(); - } - } - return total; - } - - public int getPagedService(String namespaceId, int startPage, int pageSize, String param, String containedInstance, - List serviceList, boolean hasIpCount) { - - List matchList; - - if (chooseServiceMap(namespaceId) == null) { - return 0; - } - - if (StringUtils.isNotBlank(param)) { - StringJoiner regex = new StringJoiner(Constants.SERVICE_INFO_SPLITER); - for (String s : param.split(Constants.SERVICE_INFO_SPLITER, Constants.SERVICE_INFO_SPLIT_COUNT)) { - regex.add(StringUtils.isBlank(s) ? Constants.ANY_PATTERN - : Constants.ANY_PATTERN + s + Constants.ANY_PATTERN); - } - matchList = searchServices(namespaceId, regex.toString()); - } else { - matchList = new ArrayList<>(chooseServiceMap(namespaceId).values()); - } - - if (!CollectionUtils.isEmpty(matchList) && hasIpCount) { - matchList = matchList.stream().filter(s -> !CollectionUtils.isEmpty(s.allIPs())) - .collect(Collectors.toList()); - } - - if (StringUtils.isNotBlank(containedInstance)) { - - boolean contained; - for (int i = 0; i < matchList.size(); i++) { - Service service = matchList.get(i); - contained = false; - List instances = service.allIPs(); - for (Instance instance : instances) { - if (InternetAddressUtil.containsPort(containedInstance)) { - if (StringUtils.equals(instance.getIp() + InternetAddressUtil.IP_PORT_SPLITER + instance.getPort(), - containedInstance)) { - contained = true; - break; - } - } else { - if (StringUtils.equals(instance.getIp(), containedInstance)) { - contained = true; - break; - } - } - } - if (!contained) { - matchList.remove(i); - i--; - } - } - } - - if (pageSize >= matchList.size()) { - serviceList.addAll(matchList); - return matchList.size(); - } - - for (int i = 0; i < matchList.size(); i++) { - if (i < startPage * pageSize) { - continue; - } - - serviceList.add(matchList.get(i)); - - if (serviceList.size() >= pageSize) { - break; - } - } - - return matchList.size(); - } - - /** - * Shut down service manager v1.x. - * - * @throws NacosException nacos exception during shutdown - */ - public void shutdown() throws NacosException { - try { - long start = System.nanoTime(); - Loggers.SRV_LOG - .info("Start to destroy ALL services. namespaces: {}, services: {}", serviceMap.keySet().size(), - getServiceCount()); - for (Iterator>> iterator = serviceMap.entrySet().iterator(); - iterator.hasNext(); ) { - Map.Entry> entry = iterator.next(); - destroyAllService(entry.getKey(), entry.getValue()); - iterator.remove(); - } - Loggers.SRV_LOG.info(String.format("Successfully destroy ALL services. costs %.2fms", - ((float) (System.nanoTime() - start)) * 1e-6)); - } catch (Exception e) { - throw new NacosException(NacosException.SERVER_ERROR, "shutdown serviceManager failed", e); - } - } - - private void destroyAllService(String namespace, Map serviceMap) throws Exception { - for (Iterator> iterator = serviceMap.entrySet().iterator(); iterator.hasNext(); ) { - Map.Entry entry = iterator.next(); - Service service = entry.getValue(); - String name = service.getName(); - cleanupService(namespace, name, service); - iterator.remove(); - } - } - - private void cleanupService(String namespace, String name, Service service) throws Exception { - service.destroy(); - String ephemeralInstanceListKey = KeyBuilder.buildInstanceListKey(namespace, name, true); - String persistInstanceListKey = KeyBuilder.buildInstanceListKey(namespace, name, false); - String serviceMetaKey = KeyBuilder.buildServiceMetaKey(namespace, name); - consistencyService.remove(ephemeralInstanceListKey); - consistencyService.remove(persistInstanceListKey); - consistencyService.remove(serviceMetaKey); - - // remove listeners of key to avoid mem leak - consistencyService.unListen(ephemeralInstanceListKey, service); - consistencyService.unListen(persistInstanceListKey, service); - consistencyService.unListen(serviceMetaKey, service); - Loggers.SRV_LOG.info("[DEAD-SERVICE] {}", service.toJson()); - } - - public static class ServiceChecksum { - - public String namespaceId; - - public Map serviceName2Checksum = new HashMap(); - - public ServiceChecksum() { - this.namespaceId = Constants.DEFAULT_NAMESPACE_ID; - } - - public ServiceChecksum(String namespaceId) { - this.namespaceId = namespaceId; - } - - /** - * Add service checksum. - * - * @param serviceName service name - * @param checksum checksum of service - */ - public void addItem(String serviceName, String checksum) { - if (StringUtils.isEmpty(serviceName) || StringUtils.isEmpty(checksum)) { - Loggers.SRV_LOG.warn("[DOMAIN-CHECKSUM] serviceName or checksum is empty,serviceName: {}, checksum: {}", - serviceName, checksum); - return; - } - serviceName2Checksum.put(serviceName, checksum); - } - } - - private class ServiceReporter implements Runnable { - - @Override - public void run() { - try { - - Map> allServiceNames = getAllServiceNames(); - - if (allServiceNames.size() <= 0) { - //ignore - return; - } - - for (String namespaceId : allServiceNames.keySet()) { - - ServiceChecksum checksum = new ServiceChecksum(namespaceId); - - for (String serviceName : allServiceNames.get(namespaceId)) { - if (!distroMapper.responsible(serviceName)) { - continue; - } - - Service service = getService(namespaceId, serviceName); - - if (service == null || service.isEmpty()) { - continue; - } - - service.recalculateChecksum(); - - checksum.addItem(serviceName, service.getChecksum()); - } - - Message msg = new Message(); - - msg.setData(JacksonUtils.toJson(checksum)); - - Collection sameSiteServers = memberManager.allMembers(); - - if (sameSiteServers == null || sameSiteServers.size() <= 0) { - return; - } - - for (Member server : sameSiteServers) { - if (server.getAddress().equals(NetUtils.localServer())) { - continue; - } - synchronizer.send(server.getAddress(), msg); - } - } - } catch (Exception e) { - Loggers.SRV_LOG.error("[DOMAIN-STATUS] Exception while sending service status", e); - } finally { - GlobalExecutor.scheduleServiceReporter(this, switchDomain.getServiceStatusSynchronizationPeriodMillis(), - TimeUnit.MILLISECONDS); - } - } - } - - private static class ServiceKey { - - private String namespaceId; - - private String serviceName; - - private String serverIP; - - private String checksum; - - public String getChecksum() { - return checksum; - } - - public String getServerIP() { - return serverIP; - } - - public String getServiceName() { - return serviceName; - } - - public String getNamespaceId() { - return namespaceId; - } - - public ServiceKey(String namespaceId, String serviceName, String serverIP, String checksum) { - this.namespaceId = namespaceId; - this.serviceName = serviceName; - this.serverIP = serverIP; - this.checksum = checksum; - } - - @Override - public String toString() { - return JacksonUtils.toJson(this); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperator.java b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperator.java index de85be347..19131aa60 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperator.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperator.java @@ -90,11 +90,10 @@ public interface ServiceOperator { /** * Search service name in namespace according to expr. * - * @param namespaceId namespace id - * @param expr search expr - * @param responsibleOnly only search responsible service, will deprecated after v2.0. + * @param namespaceId namespace id + * @param expr search expr * @return service name collection of match expr * @throws NacosException nacos exception during query */ - Collection searchServiceName(String namespaceId, String expr, @Deprecated boolean responsibleOnly) throws NacosException; + Collection searchServiceName(String namespaceId, String expr) throws NacosException; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperatorV1Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperatorV1Impl.java deleted file mode 100644 index 879cea5c3..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperatorV1Impl.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.utils.NamingUtils; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.naming.constants.FieldsConstants; -import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.utils.ServiceUtil; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.springframework.stereotype.Component; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; - -/** - * Implementation of service operator for v1.x. - * - * @author xiweng.yy - */ -@Component -public class ServiceOperatorV1Impl implements ServiceOperator { - - private final ServiceManager serviceManager; - - private final DistroMapper distroMapper; - - public ServiceOperatorV1Impl(ServiceManager serviceManager, DistroMapper distroMapper) { - this.serviceManager = serviceManager; - this.distroMapper = distroMapper; - } - - @Override - public void create(String namespaceId, String serviceName, ServiceMetadata metadata) throws NacosException { - if (serviceManager.getService(namespaceId, serviceName) != null) { - throw new IllegalArgumentException("specified service already exists, serviceName : " + serviceName); - } - com.alibaba.nacos.naming.core.Service service = new com.alibaba.nacos.naming.core.Service(serviceName); - service.setProtectThreshold(metadata.getProtectThreshold()); - service.setEnabled(true); - service.setMetadata(metadata.getExtendData()); - service.setSelector(metadata.getSelector()); - service.setNamespaceId(namespaceId); - service.setGroupName(NamingUtils.getGroupName(serviceName)); - - // now valid the service. if failed, exception will be thrown - service.setLastModifiedMillis(System.currentTimeMillis()); - service.recalculateChecksum(); - service.validate(); - - serviceManager.addOrReplaceService(service); - } - - @Override - public void update(Service service, ServiceMetadata metadata) throws NacosException { - String namespaceId = service.getNamespace(); - String serviceName = service.getGroupedServiceName(); - com.alibaba.nacos.naming.core.Service serviceV1 = serviceManager.getService(namespaceId, serviceName); - - serviceManager.checkServiceIsNull(serviceV1, namespaceId, serviceName); - - serviceV1.setProtectThreshold(metadata.getProtectThreshold()); - serviceV1.setSelector(metadata.getSelector()); - serviceV1.setMetadata(metadata.getExtendData()); - serviceV1.setLastModifiedMillis(System.currentTimeMillis()); - serviceV1.recalculateChecksum(); - serviceV1.validate(); - serviceManager.addOrReplaceService(serviceV1); - } - - @Override - public void delete(String namespaceId, String serviceName) throws NacosException { - serviceManager.easyRemoveService(namespaceId, serviceName); - } - - @Override - public ObjectNode queryService(String namespaceId, String serviceName) throws NacosException { - com.alibaba.nacos.naming.core.Service service = serviceManager.getService(namespaceId, serviceName); - - serviceManager.checkServiceIsNull(service, namespaceId, serviceName); - - ObjectNode res = JacksonUtils.createEmptyJsonNode(); - res.put(FieldsConstants.NAME, NamingUtils.getServiceName(serviceName)); - res.put(FieldsConstants.NAME_SPACE_ID, service.getNamespaceId()); - res.put(FieldsConstants.PROTECT_THRESHOLD, service.getProtectThreshold()); - res.replace(FieldsConstants.METADATA, JacksonUtils.transferToJsonNode(service.getMetadata())); - res.replace(FieldsConstants.SELECTOR, JacksonUtils.transferToJsonNode(service.getSelector())); - res.put(FieldsConstants.GROUP_NAME, NamingUtils.getGroupName(serviceName)); - - ArrayNode clusters = JacksonUtils.createEmptyArrayNode(); - for (Cluster cluster : service.getClusterMap().values()) { - ObjectNode clusterJson = JacksonUtils.createEmptyJsonNode(); - clusterJson.put(FieldsConstants.NAME, cluster.getName()); - clusterJson.replace(FieldsConstants.HEALTH_CHECKER, - JacksonUtils.transferToJsonNode(cluster.getHealthChecker())); - clusterJson.replace(FieldsConstants.METADATA, JacksonUtils.transferToJsonNode(cluster.getMetadata())); - clusters.add(clusterJson); - } - res.replace(FieldsConstants.CLUSTERS, clusters); - return res; - } - - @Override - public Collection listService(String namespaceId, String groupName, String selector) throws NacosException { - Map serviceMap = serviceManager.chooseServiceMap(namespaceId); - if (serviceMap == null || serviceMap.isEmpty()) { - return Collections.emptyList(); - } - serviceMap = ServiceUtil.selectServiceWithGroupName(serviceMap, groupName); - serviceMap = ServiceUtil.selectServiceBySelector(serviceMap, selector); - if (!Constants.ALL_PATTERN.equals(groupName)) { - serviceMap.entrySet() - .removeIf(entry -> !entry.getKey().startsWith(groupName + Constants.SERVICE_INFO_SPLITER)); - } - return serviceMap.keySet(); - } - - @Override - public Collection listAllNamespace() { - return serviceManager.getAllNamespaces(); - } - - @Override - public Collection searchServiceName(String namespaceId, String expr, boolean responsibleOnly) - throws NacosException { - List services = serviceManager - .searchServices(namespaceId, Constants.ANY_PATTERN + expr + Constants.ANY_PATTERN); - Collection result = new HashSet<>(); - for (com.alibaba.nacos.naming.core.Service each : services) { - if (!responsibleOnly || distroMapper.responsible(each.getName())) { - result.add(NamingUtils.getServiceName(each.getName())); - } - } - return result; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperatorV2Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperatorV2Impl.java index b34d54c21..8afac9d9f 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperatorV2Impl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperatorV2Impl.java @@ -18,6 +18,8 @@ package com.alibaba.nacos.naming.core; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.naming.constants.FieldsConstants; @@ -77,7 +79,7 @@ public class ServiceOperatorV2Impl implements ServiceOperator { */ public void create(Service service, ServiceMetadata metadata) throws NacosException { if (ServiceManager.getInstance().containSingleton(service)) { - throw new NacosException(NacosException.INVALID_PARAM, + throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.SERVICE_ALREADY_EXIST, String.format("specified service %s already exists!", service.getGroupedServiceName())); } metadataOperateService.updateServiceMetadata(service, metadata); @@ -86,7 +88,7 @@ public class ServiceOperatorV2Impl implements ServiceOperator { @Override public void update(Service service, ServiceMetadata metadata) throws NacosException { if (!ServiceManager.getInstance().containSingleton(service)) { - throw new NacosException(NacosException.INVALID_PARAM, + throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.SERVICE_NOT_EXIST, String.format("service %s not found!", service.getGroupedServiceName())); } metadataOperateService.updateServiceMetadata(service, metadata); @@ -106,13 +108,14 @@ public class ServiceOperatorV2Impl implements ServiceOperator { */ public void delete(Service service) throws NacosException { if (!ServiceManager.getInstance().containSingleton(service)) { - throw new NacosException(NacosException.INVALID_PARAM, + throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.SERVICE_NOT_EXIST, String.format("service %s not found!", service.getGroupedServiceName())); } if (!serviceStorage.getPushData(service).getHosts().isEmpty()) { - throw new NacosException(NacosException.INVALID_PARAM, "Service " + service.getGroupedServiceName() - + " is not empty, can't be delete. Please unregister instance first"); + throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.SERVICE_DELETE_FAILURE, + "Service " + service.getGroupedServiceName() + + " is not empty, can't be delete. Please unregister instance first"); } metadataOperateService.deleteServiceMetadata(service); } @@ -121,7 +124,7 @@ public class ServiceOperatorV2Impl implements ServiceOperator { public ObjectNode queryService(String namespaceId, String serviceName) throws NacosException { Service service = getServiceFromGroupedServiceName(namespaceId, serviceName, true); if (!ServiceManager.getInstance().containSingleton(service)) { - throw new NacosException(NacosException.INVALID_PARAM, + throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.SERVICE_NOT_EXIST, "service not found, namespace: " + namespaceId + ", serviceName: " + serviceName); } ObjectNode result = JacksonUtils.createEmptyJsonNode(); @@ -147,7 +150,7 @@ public class ServiceOperatorV2Impl implements ServiceOperator { */ public ServiceDetailInfo queryService(Service service) throws NacosException { if (!ServiceManager.getInstance().containSingleton(service)) { - throw new NacosException(NacosException.INVALID_PARAM, + throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.SERVICE_NOT_EXIST, "service not found, namespace: " + service.getNamespace() + ", serviceName: " + service .getGroupedServiceName()); } @@ -236,8 +239,7 @@ public class ServiceOperatorV2Impl implements ServiceOperator { } @Override - public Collection searchServiceName(String namespaceId, String expr, boolean responsibleOnly) - throws NacosException { + public Collection searchServiceName(String namespaceId, String expr) throws NacosException { String regex = Constants.ANY_PATTERN + expr + Constants.ANY_PATTERN; Collection result = new HashSet<>(); for (Service each : ServiceManager.getInstance().getSingletons(namespaceId)) { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/cleaner/EmptyServiceAutoCleaner.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/cleaner/EmptyServiceAutoCleaner.java deleted file mode 100644 index 1463834ad..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/cleaner/EmptyServiceAutoCleaner.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.cleaner; - -import com.alibaba.nacos.naming.core.DistroMapper; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.misc.Loggers; - -import java.util.Map; -import java.util.stream.Stream; - -/** - * Empty Service Auto Cleaner. - * - * @author xiweng.yy - */ -public class EmptyServiceAutoCleaner extends AbstractNamingCleaner { - - private static final int MAX_FINALIZE_COUNT = 3; - - private final ServiceManager serviceManager; - - private final DistroMapper distroMapper; - - public EmptyServiceAutoCleaner(ServiceManager serviceManager, DistroMapper distroMapper) { - this.serviceManager = serviceManager; - this.distroMapper = distroMapper; - } - - @Override - public void run() { - - // Parallel flow opening threshold - int parallelSize = 100; - - for (String each : serviceManager.getAllNamespaces()) { - Map serviceMap = serviceManager.chooseServiceMap(each); - - Stream> stream; - if (serviceMap.size() > parallelSize) { - stream = serviceMap.entrySet().parallelStream(); - } else { - stream = serviceMap.entrySet().stream(); - } - stream.filter(entry -> { - final String serviceName = entry.getKey(); - return distroMapper.responsible(serviceName); - }).forEach(entry -> serviceMap.computeIfPresent(entry.getKey(), (serviceName, service) -> { - if (service.isEmpty()) { - - // To avoid violent Service removal, the number of times the Service - // experiences Empty is determined by finalizeCnt, and if the specified - // value is reached, it is removed - - if (service.getFinalizeCount() > MAX_FINALIZE_COUNT) { - Loggers.SRV_LOG - .warn("namespace : {}, [{}] services are automatically cleaned", each, serviceName); - try { - serviceManager.easyRemoveService(each, serviceName); - } catch (Exception e) { - Loggers.SRV_LOG - .error("namespace : {}, [{}] services are automatically clean has " + "error : {}", - each, serviceName, e); - } - } - - service.setFinalizeCount(service.getFinalizeCount() + 1); - - Loggers.SRV_LOG.debug("namespace : {}, [{}] The number of times the current service experiences " - + "an empty instance is : {}", each, serviceName, service.getFinalizeCount()); - } else { - service.setFinalizeCount(0); - } - return service; - })); - } - } - - @Override - public String getType() { - return null; - } - - @Override - public void doClean() { - - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java index a51e6ac01..23c7086a6 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java @@ -25,12 +25,16 @@ import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.monitor.MetricsMonitor; import com.alibaba.nacos.naming.pojo.Subscriber; +import com.alibaba.nacos.naming.utils.DistroUtils; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +import static com.alibaba.nacos.naming.constants.ClientConstants.REVISION; /** * Abstract implementation of {@code Client}. @@ -45,8 +49,13 @@ public abstract class AbstractClient implements Client { protected volatile long lastUpdatedTime; - public AbstractClient() { + protected final AtomicLong revision; + + protected ClientAttributes attributes; + + public AbstractClient(Long revision) { lastUpdatedTime = System.currentTimeMillis(); + this.revision = new AtomicLong(revision == null ? 0 : revision); } @Override @@ -151,7 +160,9 @@ public abstract class AbstractClient implements Client { instances.add(entry.getValue()); } } - return new ClientSyncData(getClientId(), namespaces, groupNames, serviceNames, instances, batchInstanceData); + ClientSyncData data = new ClientSyncData(getClientId(), namespaces, groupNames, serviceNames, instances, batchInstanceData); + data.getAttributes().addClientAttribute(REVISION, getRevision()); + return data; } private static BatchInstanceData buildBatchInstanceData(BatchInstanceData batchInstanceData, List batchNamespaces, @@ -176,6 +187,34 @@ public abstract class AbstractClient implements Client { MetricsMonitor.getIpCountMonitor().decrementAndGet(); } } - MetricsMonitor.getIpCountMonitor().addAndGet(-1 * subscribers.size()); + MetricsMonitor.getSubscriberCount().addAndGet(-1 * subscribers.size()); + } + + @Override + public long recalculateRevision() { + int hash = DistroUtils.hash(this); + revision.set(hash); + return hash; + } + + @Override + public long getRevision() { + return revision.get(); + } + + @Override + public void setRevision(long revision) { + this.revision.set(revision); + } + + /** + * get client attributes. + */ + public ClientAttributes getClientAttributes() { + return attributes; + } + + public void setAttributes(ClientAttributes attributes) { + this.attributes = attributes; } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java index 58f3b2189..6cbe0112d 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java @@ -141,4 +141,23 @@ public interface Client { * Release current client and release resources if neccessary. */ void release(); + + /** + * Recalculate client revision and get its value. + * @return recalculated revision value + */ + long recalculateRevision(); + + /** + * Get client revision. + * @return current revision without recalculation + */ + long getRevision(); + + /** + * Set client revision. + * @param revision revision of this client to update + */ + void setRevision(long revision); + } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/factory/impl/ConnectionBasedClientFactory.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/factory/impl/ConnectionBasedClientFactory.java index f38db8a95..2c28a9246 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/factory/impl/ConnectionBasedClientFactory.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/factory/impl/ConnectionBasedClientFactory.java @@ -21,6 +21,8 @@ import com.alibaba.nacos.naming.core.v2.client.ClientAttributes; import com.alibaba.nacos.naming.core.v2.client.factory.ClientFactory; import com.alibaba.nacos.naming.core.v2.client.impl.ConnectionBasedClient; +import static com.alibaba.nacos.naming.constants.ClientConstants.REVISION; + /** * Client factory for {@link ConnectionBasedClient}. * @@ -35,11 +37,17 @@ public class ConnectionBasedClientFactory implements ClientFactory ClientConfig.getInstance().getClientExpiredTime(); } + + @Override + public long recalculateRevision() { + return revision.addAndGet(1); + } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/impl/IpPortBasedClient.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/impl/IpPortBasedClient.java index a855ebfaf..2a4eb2031 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/impl/IpPortBasedClient.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/impl/IpPortBasedClient.java @@ -51,6 +51,11 @@ public class IpPortBasedClient extends AbstractClient { private HealthCheckTaskV2 healthCheckTaskV2; public IpPortBasedClient(String clientId, boolean ephemeral) { + this(clientId, ephemeral, null); + } + + public IpPortBasedClient(String clientId, boolean ephemeral, Long revision) { + super(revision); this.ephemeral = ephemeral; this.clientId = clientId; this.responsibleId = getResponsibleTagFromId(); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/ClientManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/ClientManager.java index 8f59ccb03..6d7d89eb1 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/ClientManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/ClientManager.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.naming.core.v2.client.manager; +import com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.ClientAttributes; @@ -96,8 +97,8 @@ public interface ClientManager { /** * verify client. * - * @param clientId client id + * @param verifyData verify data from remote responsible server * @return true if client is valid, otherwise is false. */ - boolean verifyClient(String clientId); + boolean verifyClient(DistroClientVerifyInfo verifyData); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/ClientManagerDelegate.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/ClientManagerDelegate.java index 53a35fb81..3525e37d6 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/ClientManagerDelegate.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/ClientManagerDelegate.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.naming.core.v2.client.manager; +import com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo; import com.alibaba.nacos.naming.constants.ClientConstants; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.ClientAttributes; @@ -23,6 +24,7 @@ import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; import com.alibaba.nacos.naming.core.v2.client.manager.impl.ConnectionBasedClientManager; import com.alibaba.nacos.naming.core.v2.client.manager.impl.EphemeralIpPortClientManager; import com.alibaba.nacos.naming.core.v2.client.manager.impl.PersistentIpPortClientManager; +import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; import java.util.Collection; @@ -33,6 +35,7 @@ import java.util.HashSet; * * @author xiweng.yy */ +@DependsOn({"clientServiceIndexesManager", "namingMetadataManager"}) @Component("clientManager") public class ClientManagerDelegate implements ClientManager { @@ -96,8 +99,8 @@ public class ClientManagerDelegate implements ClientManager { } @Override - public boolean verifyClient(String clientId) { - return getClientManagerById(clientId).verifyClient(clientId); + public boolean verifyClient(DistroClientVerifyInfo verifyData) { + return getClientManagerById(verifyData.getClientId()).verifyClient(verifyData); } private ClientManager getClientManagerById(String clientId) { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/ConnectionBasedClientManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/ConnectionBasedClientManager.java index 4f1e1681c..e7888fb82 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/ConnectionBasedClientManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/ConnectionBasedClientManager.java @@ -21,6 +21,7 @@ import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.core.remote.ClientConnectionEventListener; import com.alibaba.nacos.core.remote.Connection; +import com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo; import com.alibaba.nacos.naming.constants.ClientConstants; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.ClientAttributes; @@ -126,11 +127,17 @@ public class ConnectionBasedClientManager extends ClientConnectionEventListener } @Override - public boolean verifyClient(String clientId) { - ConnectionBasedClient client = clients.get(clientId); + public boolean verifyClient(DistroClientVerifyInfo verifyData) { + ConnectionBasedClient client = clients.get(verifyData.getClientId()); if (null != client) { - client.setLastRenewTime(); - return true; + // remote node of old version will always verify with zero revision + if (0 == verifyData.getRevision() || client.getRevision() == verifyData.getRevision()) { + client.setLastRenewTime(); + return true; + } else { + Loggers.DISTRO.info("[DISTRO-VERIFY-FAILED] ConnectionBasedClient[{}] revision local={}, remote={}", + client.getClientId(), client.getRevision(), verifyData.getRevision()); + } } return false; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/EphemeralIpPortClientManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/EphemeralIpPortClientManager.java index 4d3f1c9b9..64a750ec8 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/EphemeralIpPortClientManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/EphemeralIpPortClientManager.java @@ -18,6 +18,7 @@ package com.alibaba.nacos.naming.core.v2.client.manager.impl; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo; import com.alibaba.nacos.naming.constants.ClientConstants; import com.alibaba.nacos.naming.core.DistroMapper; import com.alibaba.nacos.naming.core.v2.client.Client; @@ -33,6 +34,7 @@ import com.alibaba.nacos.naming.misc.GlobalExecutor; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.NamingExecuteTaskDispatcher; import com.alibaba.nacos.naming.misc.SwitchDomain; +import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; import java.util.Collection; @@ -45,6 +47,7 @@ import java.util.concurrent.TimeUnit; * * @author xiweng.yy */ +@DependsOn("clientServiceIndexesManager") @Component("ephemeralIpPortClientManager") public class EphemeralIpPortClientManager implements ClientManager { @@ -118,12 +121,19 @@ public class EphemeralIpPortClientManager implements ClientManager { } @Override - public boolean verifyClient(String clientId) { + public boolean verifyClient(DistroClientVerifyInfo verifyData) { + String clientId = verifyData.getClientId(); IpPortBasedClient client = clients.get(clientId); if (null != client) { - NamingExecuteTaskDispatcher.getInstance() - .dispatchAndExecuteTask(clientId, new ClientBeatUpdateTask(client)); - return true; + // remote node of old version will always verify with zero revision + if (0 == verifyData.getRevision() || client.getRevision() == verifyData.getRevision()) { + NamingExecuteTaskDispatcher.getInstance() + .dispatchAndExecuteTask(clientId, new ClientBeatUpdateTask(client)); + return true; + } else { + Loggers.DISTRO.info("[DISTRO-VERIFY-FAILED] IpPortBasedClient[{}] revision local={}, remote={}", + client.getClientId(), client.getRevision(), verifyData.getRevision()); + } } return false; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/PersistentIpPortClientManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/PersistentIpPortClientManager.java index 918dd493c..b062d9b7c 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/PersistentIpPortClientManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/PersistentIpPortClientManager.java @@ -17,6 +17,7 @@ package com.alibaba.nacos.naming.core.v2.client.manager.impl; import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo; import com.alibaba.nacos.naming.constants.ClientConstants; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.ClientAttributes; @@ -114,7 +115,7 @@ public class PersistentIpPortClientManager implements ClientManager { } @Override - public boolean verifyClient(String clientId) { + public boolean verifyClient(DistroClientVerifyInfo verifyData) { throw new UnsupportedOperationException(""); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java index 65b283ba4..e03059001 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java @@ -135,11 +135,11 @@ public class ClientServiceIndexesManager extends SmartSubscriber { } private void removePublisherIndexes(Service service, String clientId) { - if (!publisherIndexes.containsKey(service)) { - return; - } - publisherIndexes.get(service).remove(clientId); - NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true)); + publisherIndexes.computeIfPresent(service, (s, ids) -> { + ids.remove(clientId); + NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true)); + return ids.isEmpty() ? null : ids; + }); } private void addSubscriberIndexes(Service service, String clientId) { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/metadata/ServiceMetadataProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/metadata/ServiceMetadataProcessor.java index 4eb92173d..baac0445c 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/metadata/ServiceMetadataProcessor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/metadata/ServiceMetadataProcessor.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.naming.core.v2.metadata; +import com.alibaba.nacos.common.utils.TypeUtils; import com.alibaba.nacos.consistency.DataOperation; import com.alibaba.nacos.consistency.SerializeFactory; import com.alibaba.nacos.consistency.Serializer; @@ -26,13 +27,10 @@ import com.alibaba.nacos.consistency.entity.WriteRequest; import com.alibaba.nacos.consistency.snapshot.SnapshotOperation; import com.alibaba.nacos.core.distributed.ProtocolManager; import com.alibaba.nacos.core.utils.Loggers; +import com.alibaba.nacos.naming.constants.Constants; import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteEventListener; -import com.alibaba.nacos.naming.constants.Constants; -import com.alibaba.nacos.sys.utils.ApplicationUtils; -import com.alibaba.nacos.common.utils.TypeUtils; import org.springframework.stereotype.Component; import java.lang.reflect.Type; @@ -121,7 +119,6 @@ public class ServiceMetadataProcessor extends RequestProcessor4CP { Service singleton = ServiceManager.getInstance().getSingleton(service); namingMetadataManager.updateServiceMetadata(singleton, op.getMetadata()); } - doubleWriteMetadata(service, false); } private void updateServiceMetadata(MetadataOperation op) { @@ -136,18 +133,6 @@ public class ServiceMetadataProcessor extends RequestProcessor4CP { Service singleton = ServiceManager.getInstance().getSingleton(service); namingMetadataManager.updateServiceMetadata(singleton, op.getMetadata()); } - doubleWriteMetadata(service, false); - } - - /** - * Only for downgrade to v1.x. - * - * @param service double write service - * @param remove is removing service of v2 - * @deprecated will remove in v2.1.x - */ - private void doubleWriteMetadata(Service service, boolean remove) { - ApplicationUtils.getBean(DoubleWriteEventListener.class).doubleWriteMetadataToV1(service, remove); } /** @@ -177,7 +162,6 @@ public class ServiceMetadataProcessor extends RequestProcessor4CP { service = removed; } serviceStorage.removeData(service); - doubleWriteMetadata(service, true); } @Override diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/pojo/BatchInstancePublishInfo.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/pojo/BatchInstancePublishInfo.java index 1ab5b33a2..9097834a9 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/pojo/BatchInstancePublishInfo.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/pojo/BatchInstancePublishInfo.java @@ -27,7 +27,6 @@ import java.util.Objects; * @author : ChenHao26 * @ClassName: BatchInstancePublishInfo * @Date: 2022/4/21 16:19 - * @Description: TODO */ public class BatchInstancePublishInfo extends InstancePublishInfo { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/pojo/InstancePublishInfo.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/pojo/InstancePublishInfo.java index d6020c7b8..1632fa88e 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/pojo/InstancePublishInfo.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/pojo/InstancePublishInfo.java @@ -115,7 +115,12 @@ public class InstancePublishInfo implements Serializable { @Override public String toString() { - return "InstancePublishInfo{" + "ip='" + ip + '\'' + ", port=" + port + ", healthy=" + healthy + '}'; + return "InstancePublishInfo{" + + "ip='" + ip + '\'' + + ", port=" + port + + ", healthy=" + healthy + + ", cluster='" + cluster + '\'' + + '}'; } public static String genMetadataId(String ip, int port, String cluster) { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationServiceProxy.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationServiceProxy.java index 9b08b0d3c..2efa7c6dd 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationServiceProxy.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationServiceProxy.java @@ -24,6 +24,7 @@ import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationSer import com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.pojo.Subscriber; +import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; import java.util.List; @@ -31,8 +32,11 @@ import java.util.List; /** * Implementation of external exposure. * + *

Depends on {@link com.alibaba.nacos.naming.push.v2.NamingSubscriberServiceV2Impl namingSubscriberServiceV2Impl} + * having listen on related {@link com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent.ServiceChangedEvent events}. * @author liaochuntao */ +@DependsOn("namingSubscriberServiceV2Impl") @SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule") @Component public class ClientOperationServiceProxy implements ClientOperationService { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java index 916fdda84..f2223a49f 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java @@ -69,6 +69,7 @@ public class EphemeralClientOperationServiceImpl implements ClientOperationServi InstancePublishInfo instanceInfo = getPublishInfo(instance); client.addServiceInstance(singleton, instanceInfo); client.setLastUpdatedTime(); + client.recalculateRevision(); NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId)); NotifyCenter .publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false)); @@ -113,6 +114,7 @@ public class EphemeralClientOperationServiceImpl implements ClientOperationServi } InstancePublishInfo removedInstance = client.removeServiceInstance(singleton); client.setLastUpdatedTime(); + client.recalculateRevision(); if (null != removedInstance) { NotifyCenter.publishEvent(new ClientOperationEvent.ClientDeregisterServiceEvent(singleton, clientId)); NotifyCenter.publishEvent( diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl.java index 1db5608f0..aa650966e 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl.java @@ -34,6 +34,7 @@ import com.alibaba.nacos.consistency.snapshot.Reader; import com.alibaba.nacos.consistency.snapshot.SnapshotOperation; import com.alibaba.nacos.consistency.snapshot.Writer; import com.alibaba.nacos.core.distributed.ProtocolManager; +import com.alibaba.nacos.core.utils.Loggers; import com.alibaba.nacos.naming.consistency.persistent.impl.AbstractSnapshotOperation; import com.alibaba.nacos.naming.constants.Constants; import com.alibaba.nacos.naming.core.v2.ServiceManager; @@ -113,6 +114,7 @@ public class PersistentClientOperationServiceImpl extends RequestProcessor4CP im try { protocol.write(writeRequest); + Loggers.RAFT.info("Client registered. service={}, clientId={}, instance={}", service, instance, clientId); } catch (Exception e) { throw new NacosRuntimeException(NacosException.SERVER_ERROR, e); } @@ -160,6 +162,7 @@ public class PersistentClientOperationServiceImpl extends RequestProcessor4CP im try { protocol.write(writeRequest); + Loggers.RAFT.info("Client unregistered. service={}, clientId={}, instance={}", service, instance, clientId); } catch (Exception e) { throw new NacosRuntimeException(NacosException.SERVER_ERROR, e); } @@ -231,6 +234,10 @@ public class PersistentClientOperationServiceImpl extends RequestProcessor4CP im private void onInstanceDeregister(Service service, String clientId) { Service singleton = ServiceManager.getInstance().getSingleton(service); Client client = clientManager.getClient(clientId); + if (client == null) { + Loggers.RAFT.warn("client not exist onInstanceDeregister, clientId : {} ", clientId); + return; + } client.removeServiceInstance(singleton); client.setLastUpdatedTime(); if (client.getAllPublishedService().isEmpty()) { @@ -313,6 +320,7 @@ public class PersistentClientOperationServiceImpl extends RequestProcessor4CP im @Override protected boolean readSnapshot(Reader reader) throws Exception { final String readerPath = reader.getPath(); + Loggers.RAFT.info("snapshot start to load from : {}", readerPath); final String sourceFile = Paths.get(readerPath, SNAPSHOT_ARCHIVE).toString(); final Checksum checksum = new CRC64(); byte[] snapshotBytes = DiskUtils.decompress(sourceFile, checksum); @@ -323,6 +331,7 @@ public class PersistentClientOperationServiceImpl extends RequestProcessor4CP im } } loadSnapshot(snapshotBytes); + Loggers.RAFT.info("snapshot success to load from : {}", readerPath); return true; } @@ -340,6 +349,7 @@ public class PersistentClientOperationServiceImpl extends RequestProcessor4CP im ConcurrentHashMap snapshot = new ConcurrentHashMap<>(newData.size()); for (Map.Entry entry : newData.entrySet()) { IpPortBasedClient snapshotClient = new IpPortBasedClient(entry.getKey(), false); + snapshotClient.setAttributes(entry.getValue().getAttributes()); snapshotClient.init(); loadSyncDataToClient(entry, snapshotClient); snapshot.put(entry.getKey(), snapshotClient); @@ -357,6 +367,7 @@ public class PersistentClientOperationServiceImpl extends RequestProcessor4CP im Service service = Service.newService(namespaces.get(i), groupNames.get(i), serviceNames.get(i), false); Service singleton = ServiceManager.getInstance().getSingleton(service); client.putServiceInstance(singleton, instances.get(i)); + Loggers.RAFT.info("[SNAPSHOT-LOAD] service={}, instance={}", service, instances.get(i)); NotifyCenter.publishEvent( new ClientOperationEvent.ClientRegisterServiceEvent(singleton, client.getClientId())); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/DefaultSelfUpgradeChecker.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/DefaultSelfUpgradeChecker.java deleted file mode 100644 index ac8ddc9b9..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/DefaultSelfUpgradeChecker.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade; - -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteDelayTaskEngine; -import com.alibaba.nacos.naming.monitor.MetricsMonitor; - -/** - * Default upgrade checker for self node. - * - * @author xiweng.yy - */ -public class DefaultSelfUpgradeChecker implements SelfUpgradeChecker { - - private static final String DEFAULT = "default"; - - @Override - public String checkType() { - return DEFAULT; - } - - @Override - public boolean isReadyToUpgrade(ServiceManager serviceManager, DoubleWriteDelayTaskEngine taskEngine) { - return checkServiceAndInstanceNumber(serviceManager) && checkDoubleWriteStatus(taskEngine); - } - - private boolean checkServiceAndInstanceNumber(ServiceManager serviceManager) { - boolean result = serviceManager.getServiceCount() == MetricsMonitor.getDomCountMonitor().get(); - result &= serviceManager.getInstanceCount() == MetricsMonitor.getIpCountMonitor().get(); - return result; - } - - private boolean checkDoubleWriteStatus(DoubleWriteDelayTaskEngine taskEngine) { - return taskEngine.isEmpty(); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/SelfUpgradeChecker.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/SelfUpgradeChecker.java deleted file mode 100644 index e32c1ed09..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/SelfUpgradeChecker.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade; - -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteDelayTaskEngine; - -/** - * Upgrade checker for self-node to judge whether current node is ready to upgrade. - * - * @author xiweng.yy - */ -public interface SelfUpgradeChecker { - - /** - * Get the check type of this self upgrade checker. - * - * @return type - */ - String checkType(); - - /** - * Judge whether current node is ready to upgrade. - * - * @param serviceManager service manager for v1 mode. - * @param taskEngine double write task engine - * @return {@code true} if current node is ready to upgrade, otherwise {@code false} - */ - boolean isReadyToUpgrade(ServiceManager serviceManager, DoubleWriteDelayTaskEngine taskEngine); -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/SelfUpgradeCheckerSpiHolder.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/SelfUpgradeCheckerSpiHolder.java deleted file mode 100644 index 460fef491..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/SelfUpgradeCheckerSpiHolder.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade; - -import com.alibaba.nacos.common.spi.NacosServiceLoader; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -/** - * SPI holder for self upgrade checker. - * - * @author xiweng.yy - */ -public class SelfUpgradeCheckerSpiHolder { - - private static final SelfUpgradeCheckerSpiHolder INSTANCE = new SelfUpgradeCheckerSpiHolder(); - - private static final DefaultSelfUpgradeChecker DEFAULT_SELF_UPGRADE_CHECKER = new DefaultSelfUpgradeChecker(); - - private final Map selfUpgradeCheckerMap; - - private SelfUpgradeCheckerSpiHolder() { - Collection checkers = NacosServiceLoader.load(SelfUpgradeChecker.class); - selfUpgradeCheckerMap = new HashMap<>(checkers.size()); - for (SelfUpgradeChecker each : checkers) { - selfUpgradeCheckerMap.put(each.checkType(), each); - } - } - - /** - * Find target type self checker. - * - * @param type target type - * @return target {@link SelfUpgradeChecker} if exist, otherwise {@link DefaultSelfUpgradeChecker} - */ - public static SelfUpgradeChecker findSelfChecker(String type) { - return INSTANCE.selfUpgradeCheckerMap.getOrDefault(type, DEFAULT_SELF_UPGRADE_CHECKER); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/UpgradeJudgement.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/UpgradeJudgement.java deleted file mode 100644 index 8f2d8e44a..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/UpgradeJudgement.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.common.JustForTest; -import com.alibaba.nacos.common.executor.ExecutorFactory; -import com.alibaba.nacos.common.executor.NameThreadFactory; -import com.alibaba.nacos.common.notify.Event; -import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.notify.listener.Subscriber; -import com.alibaba.nacos.core.cluster.Member; -import com.alibaba.nacos.core.cluster.MemberMetaDataConstants; -import com.alibaba.nacos.core.cluster.MembersChangeEvent; -import com.alibaba.nacos.core.cluster.ServerMemberManager; -import com.alibaba.nacos.naming.consistency.persistent.ClusterVersionJudgement; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftCore; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeerSet; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.RefreshStorageDataTask; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteDelayTaskEngine; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.execute.AsyncServicesCheckTask; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.NamingExecuteTaskDispatcher; -import com.alibaba.nacos.sys.env.EnvUtil; -import org.codehaus.jackson.Version; -import org.codehaus.jackson.util.VersionUtil; -import org.springframework.stereotype.Component; - -import javax.annotation.PreDestroy; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Ability judgement during upgrading. - * - * @author xiweng.yy - */ -@Component -public class UpgradeJudgement extends Subscriber { - - /** - * Only when all cluster upgrade upper 2.0.0, this features is true. - */ - private final AtomicBoolean useGrpcFeatures = new AtomicBoolean(false); - - /** - * Only when all cluster upgrade upper 1.4.0, this features is true. - */ - private final AtomicBoolean useJraftFeatures = new AtomicBoolean(false); - - private final AtomicBoolean all20XVersion = new AtomicBoolean(false); - - private final RaftPeerSet raftPeerSet; - - private final RaftCore raftCore; - - private final ClusterVersionJudgement versionJudgement; - - private final ServerMemberManager memberManager; - - private final ServiceManager serviceManager; - - private final DoubleWriteDelayTaskEngine doubleWriteDelayTaskEngine; - - private ScheduledExecutorService upgradeChecker; - - private SelfUpgradeChecker selfUpgradeChecker; - - private static final int MAJOR_VERSION = 2; - - private static final int MINOR_VERSION = 4; - - public UpgradeJudgement(RaftPeerSet raftPeerSet, RaftCore raftCore, ClusterVersionJudgement versionJudgement, - ServerMemberManager memberManager, ServiceManager serviceManager, - UpgradeStates upgradeStates, - DoubleWriteDelayTaskEngine doubleWriteDelayTaskEngine) { - this.raftPeerSet = raftPeerSet; - this.raftCore = raftCore; - this.versionJudgement = versionJudgement; - this.memberManager = memberManager; - this.serviceManager = serviceManager; - this.doubleWriteDelayTaskEngine = doubleWriteDelayTaskEngine; - Boolean upgraded = upgradeStates.isUpgraded(); - upgraded = upgraded != null && upgraded; - boolean isStandaloneMode = EnvUtil.getStandaloneMode(); - boolean isSupportUpgradeFrom1X = EnvUtil.isSupportUpgradeFrom1X(); - if (isStandaloneMode || upgraded || !isSupportUpgradeFrom1X) { - useGrpcFeatures.set(true); - useJraftFeatures.set(true); - all20XVersion.set(true); - } - if (!isStandaloneMode && isSupportUpgradeFrom1X) { - initUpgradeChecker(); - NotifyCenter.registerSubscriber(this); - } - } - - private void initUpgradeChecker() { - selfUpgradeChecker = SelfUpgradeCheckerSpiHolder.findSelfChecker(EnvUtil.getProperty("upgrading.checker.type", "default")); - upgradeChecker = ExecutorFactory.newSingleScheduledExecutorService(new NameThreadFactory("upgrading.checker")); - upgradeChecker.scheduleAtFixedRate(() -> { - if (isUseGrpcFeatures()) { - return; - } - boolean canUpgrade = checkForUpgrade(); - Loggers.SRV_LOG.info("upgrade check result {}", canUpgrade); - if (canUpgrade) { - doUpgrade(); - } - }, 100L, 5000L, TimeUnit.MILLISECONDS); - } - - @JustForTest - void setUseGrpcFeatures(boolean value) { - useGrpcFeatures.set(value); - } - - @JustForTest - void setUseJraftFeatures(boolean value) { - useJraftFeatures.set(value); - } - - public boolean isUseGrpcFeatures() { - return useGrpcFeatures.get(); - } - - public boolean isUseJraftFeatures() { - return useJraftFeatures.get(); - } - - public boolean isAll20XVersion() { - return all20XVersion.get(); - } - - @Override - public void onEvent(MembersChangeEvent event) { - if (!event.hasTriggers()) { - Loggers.SRV_LOG.info("Member change without no trigger. " - + "It may be triggered by member lookup on startup. " - + "Skip."); - return; - } - Loggers.SRV_LOG.info("member change, event: {}", event); - for (Member each : event.getTriggers()) { - Object versionStr = each.getExtendVal(MemberMetaDataConstants.VERSION); - // come from below 1.3.0 - if (null == versionStr) { - checkAndDowngrade(false); - all20XVersion.set(false); - return; - } - Version version = VersionUtil.parseVersion(versionStr.toString()); - if (version.getMajorVersion() < MAJOR_VERSION) { - checkAndDowngrade(version.getMinorVersion() >= MINOR_VERSION); - all20XVersion.set(false); - return; - } - } - all20XVersion.set(true); - } - - private void checkAndDowngrade(boolean jraftFeature) { - boolean isDowngradeGrpc = useGrpcFeatures.compareAndSet(true, false); - boolean isDowngradeJraft = useJraftFeatures.getAndSet(jraftFeature); - if (isDowngradeGrpc && isDowngradeJraft && !jraftFeature) { - Loggers.SRV_LOG.info("Downgrade to 1.X"); - NotifyCenter.publishEvent(new UpgradeStates.UpgradeStateChangedEvent(false)); - try { - raftPeerSet.init(); - raftCore.init(); - versionJudgement.reset(); - } catch (Exception e) { - Loggers.SRV_LOG.error("Downgrade rafe failed ", e); - } - } - } - - private boolean checkForUpgrade() { - if (!useGrpcFeatures.get()) { - boolean selfCheckResult = selfUpgradeChecker.isReadyToUpgrade(serviceManager, doubleWriteDelayTaskEngine); - Member self = memberManager.getSelf(); - self.setExtendVal(MemberMetaDataConstants.READY_TO_UPGRADE, selfCheckResult); - memberManager.updateMember(self); - if (!selfCheckResult) { - NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask(AsyncServicesCheckTask.class, - new AsyncServicesCheckTask(doubleWriteDelayTaskEngine, this)); - } - } - boolean result = true; - for (Member each : memberManager.allMembers()) { - Object isReadyToUpgrade = each.getExtendVal(MemberMetaDataConstants.READY_TO_UPGRADE); - result &= null != isReadyToUpgrade && (boolean) isReadyToUpgrade; - } - return result; - } - - private void doUpgrade() { - Loggers.SRV_LOG.info("Upgrade to 2.0.X"); - useGrpcFeatures.compareAndSet(false, true); - NotifyCenter.publishEvent(new UpgradeStates.UpgradeStateChangedEvent(true)); - useJraftFeatures.set(true); - refreshPersistentServices(); - } - - private void refreshPersistentServices() { - for (String each : com.alibaba.nacos.naming.core.v2.ServiceManager.getInstance().getAllNamespaces()) { - for (Service service : com.alibaba.nacos.naming.core.v2.ServiceManager.getInstance().getSingletons(each)) { - NamingExecuteTaskDispatcher.getInstance() - .dispatchAndExecuteTask(service, new RefreshStorageDataTask(service)); - } - } - } - - @Override - public Class subscribeType() { - return MembersChangeEvent.class; - } - - /** - * Shut down. - */ - @PreDestroy - public void shutdown() { - if (null != upgradeChecker) { - upgradeChecker.shutdownNow(); - } - NotifyCenter.deregisterSubscriber(this); - } - - /** - * Stop judgement and clear all cache. - */ - public void stopAll() { - try { - Loggers.SRV_LOG.info("Disable Double write, stop and clean v1.x cache and features"); - useGrpcFeatures.set(true); - NotifyCenter.publishEvent(new UpgradeStates.UpgradeStateChangedEvent(true)); - useJraftFeatures.set(true); - NotifyCenter.deregisterSubscriber(this); - doubleWriteDelayTaskEngine.shutdown(); - if (null != upgradeChecker) { - upgradeChecker.shutdownNow(); - } - serviceManager.shutdown(); - raftCore.shutdown(); - } catch (NacosException e) { - Loggers.SRV_LOG.info("Close double write with exception", e); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/UpgradeStates.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/UpgradeStates.java deleted file mode 100644 index 548fe5342..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/UpgradeStates.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 1999-2021 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.nacos.naming.core.v2.upgrade; - -import com.alibaba.nacos.common.notify.Event; -import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.notify.listener.Subscriber; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.sys.env.EnvUtil; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.util.Properties; - -/** - * Persist upgrade states to disk. - * - * @author gengtuo.ygt - * on 2021/5/18 - */ -@Component -public class UpgradeStates extends Subscriber { - - private static final String FILE_NAME = "upgrade.state"; - - private static final String UPGRADED_KEY = "upgraded"; - - public static final Path UPGRADE_STATE_FILE = - Paths.get(EnvUtil.getNacosHome() + File.separator + "data" + File.separator + FILE_NAME); - - private final Properties properties = new Properties(); - - @PostConstruct - private void init() throws IOException { - if (Files.isDirectory(UPGRADE_STATE_FILE)) { - throw new IOException(UPGRADE_STATE_FILE + " is a directory"); - } - try { - Files.createDirectories(UPGRADE_STATE_FILE.getParent().toAbsolutePath()); - } catch (FileAlreadyExistsException ignored) { - } - readFromDisk(); - NotifyCenter.registerSubscriber(this); - } - - @PreDestroy - private void destroy() throws IOException { - writeToDisk(); - } - - private void readFromDisk() { - try { - if (Files.notExists(UPGRADE_STATE_FILE)) { - Loggers.SRV_LOG.info("{} file is not exist", FILE_NAME); - return; - } - if (Files.isRegularFile(UPGRADE_STATE_FILE)) { - try (InputStream is = Files.newInputStream(UPGRADE_STATE_FILE)) { - properties.load(is); - } - } - } catch (Exception e) { - Loggers.SRV_LOG.error("Failed to load file " + UPGRADE_STATE_FILE, e); - throw new IllegalStateException(e); - } - } - - private void writeToDisk() throws IOException { - try (OutputStream os = Files.newOutputStream(UPGRADE_STATE_FILE, - StandardOpenOption.CREATE, StandardOpenOption.WRITE)) { - properties.store(os, null); - } - } - - /** - * Cluster has been upgraded at recent process. - * - * @return Has been upgraded - */ - public Boolean isUpgraded() { - String value = properties.getProperty(UPGRADED_KEY); - if (value == null) { - return null; - } - return Boolean.parseBoolean(value); - } - - @Override - public void onEvent(UpgradeStateChangedEvent event) { - properties.setProperty(UPGRADED_KEY, String.valueOf(event.isUpgraded)); - try { - writeToDisk(); - } catch (IOException e) { - Loggers.EVT_LOG.error("Failed to write " + FILE_NAME + " to disk", e); - } - } - - @Override - public Class subscribeType() { - return UpgradeStateChangedEvent.class; - } - - public static class UpgradeStateChangedEvent extends Event { - - private final boolean isUpgraded; - - public UpgradeStateChangedEvent(boolean isUpgraded) { - this.isUpgraded = isUpgraded; - } - - public boolean isUpgraded() { - return isUpgraded; - } - - } - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/RefreshStorageDataTask.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/RefreshStorageDataTask.java deleted file mode 100644 index 5987ac632..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/RefreshStorageDataTask.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite; - -import com.alibaba.nacos.common.task.AbstractExecuteTask; -import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.sys.utils.ApplicationUtils; - -/** - * Refresh service storage cache data when upgrading. - * - * @author xiweng.yy - */ -public class RefreshStorageDataTask extends AbstractExecuteTask { - - private final Service service; - - public RefreshStorageDataTask(Service service) { - this.service = service; - } - - @Override - public void run() { - ApplicationUtils.getBean(ServiceStorage.class).getPushData(service); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteAction.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteAction.java deleted file mode 100644 index ef79986f2..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteAction.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 1999-2021 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.nacos.naming.core.v2.upgrade.doublewrite.delay; - -/** - * Indicate write actions. - * - * @author gengtuo.ygt - * on 2021/5/13 - */ -public enum DoubleWriteAction { - - /** - * Update. - */ - UPDATE, - /** - * Remove. - */ - REMOVE; -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteContent.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteContent.java deleted file mode 100644 index 9329df923..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteContent.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite.delay; - -/** - * Double write content. - * - * @author xiweng.yy - */ -public enum DoubleWriteContent { - - /** - * Only write metadata. - */ - METADATA, - /** - * Only write instance. - */ - INSTANCE, - /** - * Both of content. - */ - BOTH; -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteDelayTaskEngine.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteDelayTaskEngine.java deleted file mode 100644 index e2a340210..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteDelayTaskEngine.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite.delay; - -import com.alibaba.nacos.common.task.NacosTaskProcessor; -import com.alibaba.nacos.common.task.engine.NacosDelayTaskExecuteEngine; -import com.alibaba.nacos.naming.misc.Loggers; -import org.springframework.stereotype.Component; - -/** - * Double Write task engine. - * - * @author xiweng.yy - */ -@Component -public class DoubleWriteDelayTaskEngine extends NacosDelayTaskExecuteEngine { - - public DoubleWriteDelayTaskEngine() { - super(DoubleWriteDelayTaskEngine.class.getSimpleName(), Loggers.SRV_LOG); - addProcessor("v1", new ServiceChangeV1Task.ServiceChangeV1TaskProcessor()); - addProcessor("v2", new ServiceChangeV2Task.ServiceChangeV2TaskProcessor()); - } - - @Override - public NacosTaskProcessor getProcessor(Object key) { - String actualKey = key.toString().split(":")[0]; - return super.getProcessor(actualKey); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteEventListener.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteEventListener.java deleted file mode 100644 index 047dc4142..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteEventListener.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite.delay; - -import com.alibaba.nacos.common.notify.Event; -import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.notify.listener.Subscriber; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.v2.event.publisher.NamingEventPublisherFactory; -import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.sys.env.EnvUtil; -import com.alibaba.nacos.sys.utils.ApplicationUtils; -import org.springframework.stereotype.Component; - -import java.util.concurrent.TimeUnit; - -import static com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteAction.REMOVE; -import static com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteAction.UPDATE; -import static com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteContent.METADATA; - -/** - * Event listener for double write. - * - * @author xiweng.yy - */ -@Component -public class DoubleWriteEventListener extends Subscriber { - - private final UpgradeJudgement upgradeJudgement; - - private final DoubleWriteDelayTaskEngine doubleWriteDelayTaskEngine; - - private volatile boolean stopDoubleWrite; - - public DoubleWriteEventListener(UpgradeJudgement upgradeJudgement, - DoubleWriteDelayTaskEngine doubleWriteDelayTaskEngine) { - this.upgradeJudgement = upgradeJudgement; - this.doubleWriteDelayTaskEngine = doubleWriteDelayTaskEngine; - NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance()); - stopDoubleWrite = EnvUtil.getStandaloneMode() || !EnvUtil.isSupportUpgradeFrom1X(); - if (!stopDoubleWrite) { - Thread doubleWriteEnabledChecker = new DoubleWriteEnabledChecker(); - doubleWriteEnabledChecker.start(); - } - } - - @Override - public void onEvent(ServiceEvent.ServiceChangedEvent event) { - if (stopDoubleWrite) { - return; - } - if (!upgradeJudgement.isUseGrpcFeatures()) { - return; - } - String taskKey = ServiceChangeV2Task.getKey(event.getService()); - ServiceChangeV2Task task = new ServiceChangeV2Task(event.getService(), DoubleWriteContent.INSTANCE); - doubleWriteDelayTaskEngine.addTask(taskKey, task); - } - - @Override - public Class subscribeType() { - return ServiceEvent.ServiceChangedEvent.class; - } - - /** - * Double write service metadata from v2 to v1. - * - * @param service service for v2 - * @param remove is removing service for v2 - */ - public void doubleWriteMetadataToV1(com.alibaba.nacos.naming.core.v2.pojo.Service service, boolean remove) { - if (stopDoubleWrite) { - return; - } - if (!upgradeJudgement.isUseGrpcFeatures()) { - return; - } - doubleWriteDelayTaskEngine.addTask(ServiceChangeV2Task.getKey(service), - new ServiceChangeV2Task(service, METADATA, remove ? REMOVE : UPDATE)); - } - - /** - * Double write service from v1 to v2. - * - * @param service service for v1 - * @param ephemeral ephemeral of service - */ - public void doubleWriteToV2(Service service, boolean ephemeral) { - if (stopDoubleWrite) { - return; - } - if (upgradeJudgement.isUseGrpcFeatures() || upgradeJudgement.isAll20XVersion()) { - return; - } - String namespace = service.getNamespaceId(); - String serviceName = service.getName(); - doubleWriteDelayTaskEngine.addTask(ServiceChangeV1Task.getKey(namespace, serviceName, ephemeral), - new ServiceChangeV1Task(namespace, serviceName, ephemeral, DoubleWriteContent.INSTANCE)); - } - - /** - * Double write service metadata from v1 to v2. - * - * @param service service for v1 - * @param ephemeral ephemeral of service - * @param remove is removing service for v1 - */ - public void doubleWriteMetadataToV2(Service service, boolean ephemeral, boolean remove) { - if (stopDoubleWrite) { - return; - } - if (upgradeJudgement.isUseGrpcFeatures() || upgradeJudgement.isAll20XVersion()) { - return; - } - String namespace = service.getNamespaceId(); - String serviceName = service.getName(); - doubleWriteDelayTaskEngine.addTask(ServiceChangeV1Task.getKey(namespace, serviceName, ephemeral), - new ServiceChangeV1Task(namespace, serviceName, ephemeral, METADATA, remove ? REMOVE : UPDATE)); - } - - private class DoubleWriteEnabledChecker extends Thread { - - private volatile boolean stillCheck = true; - - @Override - public void run() { - Loggers.SRV_LOG.info("Check whether close double write"); - while (stillCheck) { - try { - TimeUnit.SECONDS.sleep(5); - stopDoubleWrite = !ApplicationUtils.getBean(SwitchDomain.class).isDoubleWriteEnabled(); - if (stopDoubleWrite) { - upgradeJudgement.stopAll(); - stillCheck = false; - } - } catch (Exception e) { - Loggers.SRV_LOG.error("Close double write failed ", e); - } - } - Loggers.SRV_LOG.info("Check double write closed"); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/ServiceChangeV1Task.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/ServiceChangeV1Task.java deleted file mode 100644 index c97a42663..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/ServiceChangeV1Task.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite.delay; - -import com.alibaba.nacos.api.naming.pojo.ServiceInfo; -import com.alibaba.nacos.api.naming.utils.NamingUtils; -import com.alibaba.nacos.common.task.AbstractDelayTask; -import com.alibaba.nacos.common.task.NacosTask; -import com.alibaba.nacos.common.task.NacosTaskProcessor; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; -import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; -import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.execute.DoubleWriteInstanceChangeToV2Task; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.execute.DoubleWriteMetadataChangeToV2Task; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.execute.DoubleWriteServiceRemovalToV2Task; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.execute.InstanceUpgradeHelper; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.execute.ServiceMetadataUpgradeHelper; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.NamingExecuteTaskDispatcher; -import com.alibaba.nacos.sys.utils.ApplicationUtils; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Double write delay task for service from v1 to v2 during upgrading. - * - * @author xiweng.yy - */ -public class ServiceChangeV1Task extends AbstractDelayTask { - - private final String namespace; - - private final String serviceName; - - private final boolean ephemeral; - - private DoubleWriteContent content; - - private DoubleWriteAction action; - - public ServiceChangeV1Task(String namespace, String serviceName, boolean ephemeral, DoubleWriteContent content) { - this(namespace, serviceName, ephemeral, content, DoubleWriteAction.UPDATE); - } - - public ServiceChangeV1Task(String namespace, String serviceName, boolean ephemeral, - DoubleWriteContent content, DoubleWriteAction action) { - this.namespace = namespace; - this.serviceName = serviceName; - this.ephemeral = ephemeral; - this.content = action == DoubleWriteAction.REMOVE ? DoubleWriteContent.BOTH : content; - this.action = action; - setLastProcessTime(System.currentTimeMillis()); - setTaskInterval(INTERVAL); - } - - @Override - public void merge(AbstractDelayTask task) { - if (!(task instanceof ServiceChangeV1Task)) { - return; - } - ServiceChangeV1Task oldTask = (ServiceChangeV1Task) task; - if (!action.equals(oldTask.getAction())) { - action = DoubleWriteAction.REMOVE; - content = DoubleWriteContent.BOTH; - return; - } - if (!content.equals(oldTask.getContent())) { - content = DoubleWriteContent.BOTH; - } - } - - public String getNamespace() { - return namespace; - } - - public String getServiceName() { - return serviceName; - } - - public boolean isEphemeral() { - return ephemeral; - } - - public DoubleWriteContent getContent() { - return content; - } - - public DoubleWriteAction getAction() { - return action; - } - - public static String getKey(String namespace, String serviceName, boolean ephemeral) { - return "v1:" + namespace + "_" + serviceName + "_" + ephemeral; - } - - public static class ServiceChangeV1TaskProcessor implements NacosTaskProcessor { - - @Override - public boolean process(NacosTask task) { - ServiceChangeV1Task serviceTask = (ServiceChangeV1Task) task; - if (serviceTask.getAction() == DoubleWriteAction.REMOVE) { - Loggers.SRV_LOG.info("double write removal of service {}, ephemeral: {}", - serviceTask.getServiceName(), serviceTask.isEphemeral()); - dispatchRemoveAllTask(serviceTask); - return true; - } - Loggers.SRV_LOG.info("double write for service {}, ephemeral: {}, content {}", - serviceTask.getServiceName(), serviceTask.isEphemeral(), serviceTask.getContent()); - ServiceManager serviceManager = ApplicationUtils.getBean(ServiceManager.class); - Service service = serviceManager.getService(serviceTask.getNamespace(), serviceTask.getServiceName()); - if (null != service) { - switch (serviceTask.getContent()) { - case METADATA: - dispatchMetadataTask(service, serviceTask.isEphemeral()); - break; - case INSTANCE: - dispatchInstanceTask(service, serviceTask.isEphemeral()); - break; - default: - dispatchAllTask(service, serviceTask.isEphemeral()); - } - } - return true; - } - - private void dispatchRemoveAllTask(ServiceChangeV1Task serviceTask) { - com.alibaba.nacos.naming.core.v2.pojo.Service serviceV2 = com.alibaba.nacos.naming.core.v2.pojo.Service.newService( - serviceTask.getNamespace(), - NamingUtils.getGroupName(serviceTask.getServiceName()), - NamingUtils.getServiceName(serviceTask.getServiceName()), - serviceTask.isEphemeral() - ); - DoubleWriteServiceRemovalToV2Task serviceRemovalTask = new DoubleWriteServiceRemovalToV2Task(serviceV2); - NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask(serviceV2.getName(), serviceRemovalTask); - } - - private void dispatchAllTask(Service service, boolean ephemeral) { - dispatchMetadataTask(service, ephemeral); - dispatchInstanceTask(service, ephemeral); - } - - private void dispatchInstanceTask(Service service, boolean ephemeral) { - ServiceStorage serviceStorage = ApplicationUtils.getBean(ServiceStorage.class); - InstanceUpgradeHelper instanceUpgradeHelper = ApplicationUtils.getBean(InstanceUpgradeHelper.class); - ServiceInfo serviceInfo = serviceStorage.getPushData(transfer(service, ephemeral)); - List newInstance = service.allIPs(ephemeral); - Set instances = new HashSet<>(); - for (Instance each : newInstance) { - instances.add(each.toIpAddr()); - com.alibaba.nacos.api.naming.pojo.Instance instance = instanceUpgradeHelper.toV2(each); - // Ephemeral value in v1 data may not be right. - // The ephemeral come from parameter which is reference to data key matching - instance.setEphemeral(ephemeral); - DoubleWriteInstanceChangeToV2Task instanceTask = new DoubleWriteInstanceChangeToV2Task( - service.getNamespaceId(), service.getName(), instance, true); - NamingExecuteTaskDispatcher.getInstance() - .dispatchAndExecuteTask(IpPortBasedClient.getClientId(each.toIpAddr(), ephemeral), - instanceTask); - } - List oldInstance = serviceInfo.getHosts(); - for (com.alibaba.nacos.api.naming.pojo.Instance each : oldInstance) { - if (!instances.contains(each.toInetAddr())) { - DoubleWriteInstanceChangeToV2Task instanceTask = new DoubleWriteInstanceChangeToV2Task( - service.getNamespaceId(), service.getName(), each, false); - NamingExecuteTaskDispatcher.getInstance() - .dispatchAndExecuteTask(IpPortBasedClient.getClientId(each.toInetAddr(), ephemeral), - instanceTask); - } - } - } - - private com.alibaba.nacos.naming.core.v2.pojo.Service transfer(Service service, boolean ephemeral) { - return com.alibaba.nacos.naming.core.v2.pojo.Service - .newService(service.getNamespaceId(), NamingUtils.getGroupName(service.getName()), - NamingUtils.getServiceName(service.getName()), ephemeral); - } - - private void dispatchMetadataTask(Service service, boolean ephemeral) { - ServiceMetadataUpgradeHelper upgradeHelper = ApplicationUtils.getBean(ServiceMetadataUpgradeHelper.class); - ServiceMetadata serviceMetadata = upgradeHelper.toV2ServiceMetadata(service, ephemeral); - DoubleWriteMetadataChangeToV2Task metadataTask = new DoubleWriteMetadataChangeToV2Task( - service.getNamespaceId(), service.getName(), ephemeral, serviceMetadata); - NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask(service.getName(), metadataTask); - } - - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/ServiceChangeV2Task.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/ServiceChangeV2Task.java deleted file mode 100644 index 8ae4e1e07..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/ServiceChangeV2Task.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite.delay; - -import com.alibaba.nacos.common.task.AbstractDelayTask; -import com.alibaba.nacos.common.task.NacosTask; -import com.alibaba.nacos.common.task.NacosTaskProcessor; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.execute.DoubleWriteInstanceChangeToV1Task; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.execute.DoubleWriteMetadataChangeToV1Task; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.execute.DoubleWriteServiceRemovalToV1Task; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.NamingExecuteTaskDispatcher; - -/** - * Double write delay task for service from v2 to v1 during downgrading. - * - * @author xiweng.yy - */ -public class ServiceChangeV2Task extends AbstractDelayTask { - - private final Service changedService; - - private DoubleWriteContent content; - - private DoubleWriteAction action; - - public ServiceChangeV2Task(Service service, DoubleWriteContent content) { - this(service, content, DoubleWriteAction.UPDATE); - } - - public ServiceChangeV2Task(Service service, DoubleWriteContent content, DoubleWriteAction action) { - this.changedService = service; - this.content = action == DoubleWriteAction.REMOVE ? DoubleWriteContent.BOTH : content; - this.action = action; - setTaskInterval(INTERVAL); - setLastProcessTime(System.currentTimeMillis()); - } - - public Service getChangedService() { - return changedService; - } - - public DoubleWriteContent getContent() { - return content; - } - - public DoubleWriteAction getAction() { - return action; - } - - @Override - public void merge(AbstractDelayTask task) { - if (!(task instanceof ServiceChangeV2Task)) { - return; - } - ServiceChangeV2Task oldTask = (ServiceChangeV2Task) task; - if (!action.equals(oldTask.getAction())) { - action = DoubleWriteAction.REMOVE; - content = DoubleWriteContent.BOTH; - return; - } - if (!content.equals(oldTask.getContent())) { - content = DoubleWriteContent.BOTH; - } - } - - public static String getKey(Service service) { - return "v2:" + service.getNamespace() + "_" + service.getGroupedServiceName() + "_" + service.isEphemeral(); - } - - public static class ServiceChangeV2TaskProcessor implements NacosTaskProcessor { - - @Override - public boolean process(NacosTask task) { - ServiceChangeV2Task serviceTask = (ServiceChangeV2Task) task; - Service changedService = serviceTask.getChangedService(); - if (serviceTask.getAction() == DoubleWriteAction.REMOVE) { - Loggers.SRV_LOG.info("double write removal of service {}", changedService); - dispatchRemoveServiceTask(changedService); - return true; - } - Loggers.SRV_LOG.info("double write for service {}, content {}", changedService, serviceTask.getContent()); - switch (serviceTask.getContent()) { - case INSTANCE: - dispatchInstanceChangeTask(changedService); - break; - case METADATA: - dispatchMetadataChangeTask(changedService); - break; - default: - dispatchAllTask(changedService); - } - return true; - } - - private void dispatchRemoveServiceTask(Service changedService) { - DoubleWriteServiceRemovalToV1Task serviceRemovalTask = new DoubleWriteServiceRemovalToV1Task(changedService); - NamingExecuteTaskDispatcher.getInstance() - .dispatchAndExecuteTask(changedService.getGroupedServiceName(), serviceRemovalTask); - } - - private void dispatchInstanceChangeTask(Service changedService) { - DoubleWriteInstanceChangeToV1Task instanceTask = new DoubleWriteInstanceChangeToV1Task(changedService); - NamingExecuteTaskDispatcher.getInstance() - .dispatchAndExecuteTask(changedService.getGroupedServiceName(), instanceTask); - } - - private void dispatchMetadataChangeTask(Service changedService) { - DoubleWriteMetadataChangeToV1Task metadataTask = new DoubleWriteMetadataChangeToV1Task(changedService); - NamingExecuteTaskDispatcher.getInstance() - .dispatchAndExecuteTask(changedService.getGroupedServiceName(), metadataTask); - } - - private void dispatchAllTask(Service changedService) { - dispatchMetadataChangeTask(changedService); - dispatchInstanceChangeTask(changedService); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/AsyncServicesCheckTask.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/AsyncServicesCheckTask.java deleted file mode 100644 index 5c765c601..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/AsyncServicesCheckTask.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite.execute; - -import com.alibaba.nacos.api.naming.pojo.ServiceInfo; -import com.alibaba.nacos.api.naming.utils.NamingUtils; -import com.alibaba.nacos.common.task.AbstractExecuteTask; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteAction; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteContent; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteDelayTaskEngine; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.ServiceChangeV1Task; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.sys.utils.ApplicationUtils; - -import java.util.HashMap; -import java.util.Map; - -/** - * Async services check task for upgrading. - * - * @author xiweng.yy - */ -public class AsyncServicesCheckTask extends AbstractExecuteTask { - - private final DoubleWriteDelayTaskEngine doubleWriteDelayTaskEngine; - - private final UpgradeJudgement upgradeJudgement; - - private static final int INITIALCAPACITY = 64; - - public AsyncServicesCheckTask(DoubleWriteDelayTaskEngine doubleWriteDelayTaskEngine, - UpgradeJudgement upgradeJudgement) { - this.doubleWriteDelayTaskEngine = doubleWriteDelayTaskEngine; - this.upgradeJudgement = upgradeJudgement; - } - - @Override - public void run() { - if (upgradeJudgement.isUseGrpcFeatures()) { - return; - } - try { - ServiceManager serviceManager = ApplicationUtils.getBean(ServiceManager.class); - ServiceStorage serviceStorage = ApplicationUtils.getBean(ServiceStorage.class); - Map v1Services = new HashMap<>(INITIALCAPACITY); - for (String each : serviceManager.getAllNamespaces()) { - for (Map.Entry entry : serviceManager.chooseServiceMap(each).entrySet()) { - v1Services.put(buildServiceKey(each, entry.getKey()), entry.getValue()); - checkService(each, entry.getKey(), entry.getValue(), serviceStorage); - } - } - Map v2Services = new HashMap<>(INITIALCAPACITY); - for (String each : com.alibaba.nacos.naming.core.v2.ServiceManager.getInstance().getAllNamespaces()) { - for (com.alibaba.nacos.naming.core.v2.pojo.Service serviceV2 - : com.alibaba.nacos.naming.core.v2.ServiceManager.getInstance().getSingletons(each)) { - v2Services.put(buildServiceKey(each, serviceV2.getGroupedServiceName()), serviceV2); - } - } - // only check v2 services when upgrading. - v2Services.keySet().removeIf(v1Services::containsKey); - if (v2Services.isEmpty()) { - return; - } - if (Loggers.SRV_LOG.isDebugEnabled()) { - Loggers.SRV_LOG.debug("{} service in v2 to removed.", v2Services.size()); - } - for (com.alibaba.nacos.naming.core.v2.pojo.Service service : v2Services.values()) { - deleteV2Service(service); - } - } catch (Exception e) { - Loggers.SRV_LOG.warn("async check for service error", e); - } - } - - private String buildServiceKey(String namespace, String fullServiceName) { - return namespace + "##" + fullServiceName; - } - - private void checkService(String namespace, String fullServiceName, Service serviceV1, - ServiceStorage serviceStorage) { - if (upgradeJudgement.isUseGrpcFeatures()) { - return; - } - String groupName = NamingUtils.getGroupName(serviceV1.getName()); - String serviceName = NamingUtils.getServiceName(fullServiceName); - com.alibaba.nacos.naming.core.v2.pojo.Service serviceV2 = com.alibaba.nacos.naming.core.v2.pojo.Service - .newService(namespace, groupName, serviceName); - ServiceInfo serviceInfo = serviceStorage.getData(serviceV2); - if (serviceV1.allIPs().size() != serviceInfo.getHosts().size()) { - boolean isEphemeral = serviceV1.allIPs(false).isEmpty(); - String key = ServiceChangeV1Task.getKey(namespace, fullServiceName, isEphemeral); - ServiceChangeV1Task task = new ServiceChangeV1Task(namespace, fullServiceName, isEphemeral, - DoubleWriteContent.INSTANCE); - doubleWriteDelayTaskEngine.addTask(key, task); - } - } - - private void deleteV2Service(com.alibaba.nacos.naming.core.v2.pojo.Service serviceV2) { - if (upgradeJudgement.isUseGrpcFeatures()) { - return; - } - String namespace = serviceV2.getNamespace(); - String serviceName = serviceV2.getGroupedServiceName(); - boolean ephemeral = serviceV2.isEphemeral(); - String key = ServiceChangeV1Task.getKey(namespace, serviceName, ephemeral); - ServiceChangeV1Task task = new ServiceChangeV1Task(namespace, serviceName, - ephemeral, DoubleWriteContent.BOTH, DoubleWriteAction.REMOVE); - doubleWriteDelayTaskEngine.addTask(key, task); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DefaultInstanceUpgradeHelper.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DefaultInstanceUpgradeHelper.java deleted file mode 100644 index e1d37ebe6..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DefaultInstanceUpgradeHelper.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite.execute; - -import com.alibaba.nacos.naming.core.Instance; -import org.springframework.beans.BeanUtils; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * A default implementation for instance upgrade/downgrade. - * - * @author gengtuo.ygt - * on 2021/2/25 - */ -public class DefaultInstanceUpgradeHelper implements InstanceUpgradeHelper { - - private static final String IGNORE_PROPERTIES = "metadata"; - - /** - * Fallback to default implementation when no other impls met. - */ - @Configuration - public static class Config { - - /** - * A default impl of instance upgrade helper. - * - * @return default impl of instance upgrade helper - */ - @Bean - @ConditionalOnMissingBean(InstanceUpgradeHelper.class) - public InstanceUpgradeHelper defaultInstanceUpgradeHelper() { - return new DefaultInstanceUpgradeHelper(); - } - - } - - @Override - public Instance toV1(com.alibaba.nacos.api.naming.pojo.Instance v2) { - Instance v1 = new Instance(v2.getIp(), v2.getPort(), v2.getClusterName()); - BeanUtils.copyProperties(v2, v1); - return v1; - } - - @Override - public com.alibaba.nacos.api.naming.pojo.Instance toV2(Instance v1) { - com.alibaba.nacos.api.naming.pojo.Instance v2 = new com.alibaba.nacos.api.naming.pojo.Instance(); - BeanUtils.copyProperties(v1, v2, IGNORE_PROPERTIES); - v2.getMetadata().putAll(v1.getMetadata()); - return v2; - } - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DefaultServiceMetadataUpgradeHelper.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DefaultServiceMetadataUpgradeHelper.java deleted file mode 100644 index c339125fe..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DefaultServiceMetadataUpgradeHelper.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite.execute; - -import com.alibaba.nacos.naming.core.Cluster; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; -import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import java.util.Map; - -/** - * A default implementation for service/cluster upgrade/downgrade. - * - * @author gengtuo.ygt - * on 2021/2/25 - */ -public class DefaultServiceMetadataUpgradeHelper implements ServiceMetadataUpgradeHelper { - - /** - * Fallback to default implementation when no other impls met. - */ - @Configuration - public static class Config { - - /** - * A default impl of service/cluster upgrade helper. - * - * @return default impl of service/cluster upgrade helper - */ - @Bean - @ConditionalOnMissingBean(ServiceMetadataUpgradeHelper.class) - public ServiceMetadataUpgradeHelper defaultServiceMetadataUpgradeHelper() { - return new DefaultServiceMetadataUpgradeHelper(); - } - - } - - @Override - public Service toV1Service(Service v1, com.alibaba.nacos.naming.core.v2.pojo.Service v2, ServiceMetadata v2meta) { - if (null == v1) { - v1 = new Service(v2.getGroupedServiceName()); - v1.setGroupName(v2.getGroup()); - v1.setNamespaceId(v2.getNamespace()); - } - v1.setSelector(v2meta.getSelector()); - v1.setProtectThreshold(v2meta.getProtectThreshold()); - v1.setMetadata(v2meta.getExtendData()); - for (Map.Entry entry : v2meta.getClusters().entrySet()) { - if (!v1.getClusterMap().containsKey(entry.getKey())) { - v1.addCluster(toV1Cluster(new Cluster(entry.getKey(), v1), entry.getValue())); - } else { - toV1Cluster(v1.getClusterMap().get(entry.getKey()), entry.getValue()); - } - } - return v1; - } - - @Override - public Cluster toV1Cluster(Cluster v1, ClusterMetadata v2meta) { - v1.setDefCkport(v2meta.getHealthyCheckPort()); - v1.setUseIPPort4Check(v2meta.isUseInstancePortForCheck()); - v1.setHealthChecker(v2meta.getHealthChecker()); - v1.setMetadata(v2meta.getExtendData()); - return v1; - } - - @Override - public ServiceMetadata toV2ServiceMetadata(Service service, boolean ephemeral) { - ServiceMetadata result = new ServiceMetadata(); - result.setEphemeral(ephemeral); - result.setProtectThreshold(service.getProtectThreshold()); - result.setSelector(service.getSelector()); - result.setExtendData(service.getMetadata()); - for (Map.Entry entry : service.getClusterMap().entrySet()) { - result.getClusters().put(entry.getKey(), toV2ClusterMetadata(entry.getValue())); - } - return result; - } - - @Override - public ClusterMetadata toV2ClusterMetadata(Cluster v1) { - ClusterMetadata result = new ClusterMetadata(); - result.setHealthyCheckPort(v1.getDefCkport()); - result.setUseInstancePortForCheck(v1.isUseIPPort4Check()); - result.setExtendData(v1.getMetadata()); - result.setHealthChecker(v1.getHealthChecker()); - result.setHealthyCheckType(v1.getHealthChecker().getType()); - return result; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteInstanceChangeToV1Task.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteInstanceChangeToV1Task.java deleted file mode 100644 index fec3a7d96..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteInstanceChangeToV1Task.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite.execute; - -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.alibaba.nacos.common.task.AbstractExecuteTask; -import com.alibaba.nacos.naming.consistency.ConsistencyService; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.core.Instances; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteContent; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteDelayTaskEngine; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.ServiceChangeV2Task; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.sys.utils.ApplicationUtils; - -/** - * Double write from 2.x to 1 task. - * - * @author xiweng.yy - */ -public class DoubleWriteInstanceChangeToV1Task extends AbstractExecuteTask { - - private final Service service; - - private static final String NAME = "consistencyDelegate"; - - public DoubleWriteInstanceChangeToV1Task(Service service) { - this.service = service; - } - - @Override - public void run() { - try { - ServiceManager serviceManager = ApplicationUtils.getBean(ServiceManager.class); - com.alibaba.nacos.naming.core.Service serviceV1 = serviceManager - .getService(service.getNamespace(), service.getGroupedServiceName()); - if (null == serviceV1) { - serviceManager.createEmptyService(service.getNamespace(), service.getGroupedServiceName(), - service.isEphemeral()); - } - Instances newInstances = getNewInstances(); - String key = KeyBuilder.buildInstanceListKey(service.getNamespace(), service.getGroupedServiceName(), - service.isEphemeral()); - ConsistencyService consistencyService = ApplicationUtils - .getBean(NAME, ConsistencyService.class); - consistencyService.put(key, newInstances); - } catch (Exception e) { - if (Loggers.SRV_LOG.isDebugEnabled()) { - Loggers.SRV_LOG.debug("Double write task for {} instance from 2 to 1 failed", service, e); - } - ServiceChangeV2Task retryTask = new ServiceChangeV2Task(service, DoubleWriteContent.INSTANCE); - retryTask.setTaskInterval(INTERVAL); - String taskKey = ServiceChangeV2Task.getKey(service); - ApplicationUtils.getBean(DoubleWriteDelayTaskEngine.class).addTask(taskKey, retryTask); - } - } - - private Instances getNewInstances() { - Instances result = new Instances(); - ServiceStorage serviceStorage = ApplicationUtils.getBean(ServiceStorage.class); - InstanceUpgradeHelper instanceUpgradeHelper = ApplicationUtils.getBean(InstanceUpgradeHelper.class); - long currentTimeStamp = System.currentTimeMillis(); - for (Instance each : serviceStorage.getData(service).getHosts()) { - com.alibaba.nacos.naming.core.Instance instance = instanceUpgradeHelper.toV1(each); - instance.setLastBeat(currentTimeStamp); - result.getInstanceList().add(instance); - } - return result; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteInstanceChangeToV2Task.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteInstanceChangeToV2Task.java deleted file mode 100644 index f673efb8b..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteInstanceChangeToV2Task.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite.execute; - -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.alibaba.nacos.common.task.AbstractExecuteTask; -import com.alibaba.nacos.naming.core.InstanceOperatorClientImpl; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteContent; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteDelayTaskEngine; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.ServiceChangeV1Task; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.sys.utils.ApplicationUtils; - -/** - * Double write from 1.x to 2 task. - * - * @author xiweng.yy - */ -public class DoubleWriteInstanceChangeToV2Task extends AbstractExecuteTask { - - private final String namespace; - - private final String serviceName; - - private final Instance instance; - - private final boolean register; - - public DoubleWriteInstanceChangeToV2Task(String namespace, String serviceName, Instance instance, - boolean register) { - this.register = register; - this.namespace = namespace; - this.serviceName = serviceName; - this.instance = instance; - } - - @Override - public void run() { - try { - InstanceOperatorClientImpl instanceOperator = ApplicationUtils.getBean(InstanceOperatorClientImpl.class); - if (register) { - instanceOperator.registerInstance(namespace, serviceName, instance); - } else { - instanceOperator.removeInstance(namespace, serviceName, instance); - } - } catch (Exception e) { - if (Loggers.SRV_LOG.isDebugEnabled()) { - Loggers.SRV_LOG - .debug("Double write task for {}#{} instance from 1 to 2 failed", namespace, serviceName, e); - } - ServiceChangeV1Task retryTask = new ServiceChangeV1Task(namespace, serviceName, instance.isEphemeral(), - DoubleWriteContent.INSTANCE); - retryTask.setTaskInterval(INTERVAL); - String taskKey = ServiceChangeV1Task.getKey(namespace, serviceName, instance.isEphemeral()); - ApplicationUtils.getBean(DoubleWriteDelayTaskEngine.class).addTask(taskKey, retryTask); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteMetadataChangeToV1Task.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteMetadataChangeToV1Task.java deleted file mode 100644 index 977e9a995..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteMetadataChangeToV1Task.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite.execute; - -import com.alibaba.nacos.common.task.AbstractExecuteTask; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; -import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteContent; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteDelayTaskEngine; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.ServiceChangeV2Task; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.sys.utils.ApplicationUtils; - -import java.util.Optional; - -/** - * Double write from 2.x to 1 task. - * - * @author xiweng.yy - */ -public class DoubleWriteMetadataChangeToV1Task extends AbstractExecuteTask { - - private final Service service; - - public DoubleWriteMetadataChangeToV1Task(Service service) { - this.service = service; - } - - @Override - public void run() { - try { - NamingMetadataManager metadataManager = ApplicationUtils.getBean(NamingMetadataManager.class); - Optional serviceMetadata = metadataManager.getServiceMetadata(service); - if (!serviceMetadata.isPresent()) { - return; - } - ServiceManager serviceManager = ApplicationUtils.getBean(ServiceManager.class); - com.alibaba.nacos.naming.core.Service serviceV1 = newServiceForV1(serviceManager, serviceMetadata.get()); - serviceManager.addOrReplaceService(serviceV1); - } catch (Exception e) { - if (Loggers.SRV_LOG.isDebugEnabled()) { - Loggers.SRV_LOG.debug("Double write task for {} metadata from 2 to 1 failed", service, e); - } - ServiceChangeV2Task retryTask = new ServiceChangeV2Task(service, DoubleWriteContent.METADATA); - retryTask.setTaskInterval(INTERVAL); - String taskKey = ServiceChangeV2Task.getKey(service); - ApplicationUtils.getBean(DoubleWriteDelayTaskEngine.class).addTask(taskKey, retryTask); - } - } - - private com.alibaba.nacos.naming.core.Service newServiceForV1(ServiceManager serviceManager, - ServiceMetadata serviceMetadata) { - com.alibaba.nacos.naming.core.Service result = serviceManager - .getService(service.getNamespace(), service.getGroupedServiceName()); - ServiceMetadataUpgradeHelper upgradeHelper = ApplicationUtils.getBean(ServiceMetadataUpgradeHelper.class); - result = upgradeHelper.toV1Service(result, service, serviceMetadata); - result.init(); - return result; - } - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteMetadataChangeToV2Task.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteMetadataChangeToV2Task.java deleted file mode 100644 index 150fca3fd..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteMetadataChangeToV2Task.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite.execute; - -import com.alibaba.nacos.api.naming.pojo.healthcheck.HealthCheckType; -import com.alibaba.nacos.api.naming.utils.NamingUtils; -import com.alibaba.nacos.common.task.AbstractExecuteTask; -import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; -import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataOperateService; -import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteContent; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteDelayTaskEngine; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.ServiceChangeV1Task; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.selector.NoneSelector; -import com.alibaba.nacos.sys.utils.ApplicationUtils; - -import java.util.Map; - -/** - * Double write from 1.x to 2 task. - * - * @author xiweng.yy - */ -public class DoubleWriteMetadataChangeToV2Task extends AbstractExecuteTask { - - private final Service service; - - private final ServiceMetadata serviceMetadata; - - private static final int PORT = 80; - - public DoubleWriteMetadataChangeToV2Task(String namespace, String serviceName, boolean ephemeral, - ServiceMetadata serviceMetadata) { - this.serviceMetadata = serviceMetadata; - String groupName = NamingUtils.getGroupName(serviceName); - String serviceNameWithoutGroup = NamingUtils.getServiceName(serviceName); - this.service = Service.newService(namespace, groupName, serviceNameWithoutGroup, ephemeral); - } - - @Override - public void run() { - try { - NamingMetadataOperateService metadataOperate = ApplicationUtils.getBean(NamingMetadataOperateService.class); - if (!isDefaultServiceMetadata()) { - metadataOperate.updateServiceMetadata(service, serviceMetadata); - } - for (Map.Entry entry : serviceMetadata.getClusters().entrySet()) { - if (!isDefaultClusterMetadata(entry.getValue())) { - metadataOperate.addClusterMetadata(service, entry.getKey(), entry.getValue()); - } - } - } catch (Exception e) { - if (Loggers.SRV_LOG.isDebugEnabled()) { - Loggers.SRV_LOG.debug("Double write task for {} metadata from 1 to 2 failed", service, e); - } - ServiceChangeV1Task retryTask = new ServiceChangeV1Task(service.getNamespace(), - service.getGroupedServiceName(), service.isEphemeral(), DoubleWriteContent.METADATA); - retryTask.setTaskInterval(INTERVAL); - String taskKey = ServiceChangeV1Task - .getKey(service.getNamespace(), service.getGroupedServiceName(), service.isEphemeral()); - ApplicationUtils.getBean(DoubleWriteDelayTaskEngine.class).addTask(taskKey, retryTask); - } - } - - private boolean isDefaultServiceMetadata() { - return serviceMetadata.getExtendData().isEmpty() && serviceMetadata.getProtectThreshold() == 0.0F - && serviceMetadata.getSelector() instanceof NoneSelector && serviceMetadata.isEphemeral(); - } - - private boolean isDefaultClusterMetadata(ClusterMetadata metadata) { - return HealthCheckType.TCP.name().equals(metadata.getHealthyCheckType()) && metadata.getExtendData().isEmpty() - && metadata.getHealthyCheckPort() == PORT && metadata.isUseInstancePortForCheck(); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteServiceRemovalToV1Task.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteServiceRemovalToV1Task.java deleted file mode 100644 index fcd1f5682..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteServiceRemovalToV1Task.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 1999-2021 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.nacos.naming.core.v2.upgrade.doublewrite.execute; - -import com.alibaba.nacos.common.task.AbstractExecuteTask; -import com.alibaba.nacos.naming.consistency.ConsistencyService; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteAction; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteContent; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteDelayTaskEngine; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.ServiceChangeV2Task; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.sys.utils.ApplicationUtils; - -/** - * Double write task for removal of service from v2 to v1. - * - * @author gengtuo.ygt - * on 2021/5/13 - */ -public class DoubleWriteServiceRemovalToV1Task extends AbstractExecuteTask { - - private final Service service; - - public DoubleWriteServiceRemovalToV1Task(Service service) { - this.service = service; - } - - @Override - public void run() { - try { - ServiceManager serviceManager = ApplicationUtils.getBean(ServiceManager.class); - com.alibaba.nacos.naming.core.Service serviceV1 = serviceManager - .getService(service.getNamespace(), service.getGroupedServiceName()); - if (serviceV1 == null) { - if (Loggers.SRV_LOG.isDebugEnabled()) { - Loggers.SRV_LOG.debug("Double write task is removing a non-exist service: {}", service); - } - return; - } - ConsistencyService consistencyService = ApplicationUtils - .getBean("consistencyDelegate", ConsistencyService.class); - // remove instances - String instanceListKey = KeyBuilder.buildInstanceListKey(service.getNamespace(), - service.getGroupedServiceName(), service.isEphemeral()); - consistencyService.remove(instanceListKey); - // remove metadata - serviceManager.easyRemoveService(service.getNamespace(), service.getGroupedServiceName()); - } catch (Exception e) { - if (Loggers.SRV_LOG.isDebugEnabled()) { - Loggers.SRV_LOG.debug("Double write task for removal of {} from 2 to 1 failed", service, e); - } - ServiceChangeV2Task retryTask = new ServiceChangeV2Task(service, - DoubleWriteContent.BOTH, DoubleWriteAction.REMOVE); - retryTask.setTaskInterval(INTERVAL); - String taskKey = ServiceChangeV2Task.getKey(service); - ApplicationUtils.getBean(DoubleWriteDelayTaskEngine.class).addTask(taskKey, retryTask); - } - } - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteServiceRemovalToV2Task.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteServiceRemovalToV2Task.java deleted file mode 100644 index 5b3d6b9d6..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/DoubleWriteServiceRemovalToV2Task.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 1999-2021 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.nacos.naming.core.v2.upgrade.doublewrite.execute; - -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.alibaba.nacos.api.naming.pojo.ServiceInfo; -import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.task.AbstractExecuteTask; -import com.alibaba.nacos.naming.core.InstanceOperatorClientImpl; -import com.alibaba.nacos.naming.core.v2.ServiceManager; -import com.alibaba.nacos.naming.core.v2.event.metadata.MetadataEvent; -import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; -import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteAction; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteContent; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteDelayTaskEngine; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.ServiceChangeV1Task; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.sys.utils.ApplicationUtils; - -import java.util.concurrent.TimeUnit; - -/** - * Double write task for removal of service from v1 to v2. - * - * @author gengtuo.ygt - * on 2021/5/13 - */ -public class DoubleWriteServiceRemovalToV2Task extends AbstractExecuteTask { - - private static final int MAX_WAIT_TIMES = 5; - - private final Service service; - - public DoubleWriteServiceRemovalToV2Task(Service service) { - this.service = service; - } - - @Override - public void run() { - try { - InstanceOperatorClientImpl instanceOperator = ApplicationUtils.getBean(InstanceOperatorClientImpl.class); - ClientServiceIndexesManager clientServiceIndexesManager = ApplicationUtils.getBean(ClientServiceIndexesManager.class); - ServiceStorage serviceStorage = ApplicationUtils.getBean(ServiceStorage.class); - ServiceInfo serviceInfo = serviceStorage.getPushData(service); - for (Instance instance : serviceInfo.getHosts()) { - instanceOperator.removeInstance(service.getNamespace(), service.getName(), instance); - } - int count = 0; - while (!clientServiceIndexesManager.getAllClientsRegisteredService(service).isEmpty() - && count < MAX_WAIT_TIMES) { - TimeUnit.MILLISECONDS.sleep(100); - count += 1; - } - clientServiceIndexesManager.removePublisherIndexesByEmptyService(service); - ServiceManager.getInstance().removeSingleton(service); - serviceStorage.removeData(service); - NotifyCenter.publishEvent(new MetadataEvent.ServiceMetadataEvent(service, true)); - } catch (Exception e) { - if (Loggers.SRV_LOG.isDebugEnabled()) { - Loggers.SRV_LOG.debug("Double write removal of {} from 1 to 2 failed", service, e); - } - ServiceChangeV1Task retryTask = new ServiceChangeV1Task(service.getNamespace(), - service.getGroupedServiceName(), service.isEphemeral(), - DoubleWriteContent.BOTH, DoubleWriteAction.REMOVE); - retryTask.setTaskInterval(INTERVAL); - String taskKey = ServiceChangeV1Task - .getKey(service.getNamespace(), service.getGroupedServiceName(), service.isEphemeral()); - ApplicationUtils.getBean(DoubleWriteDelayTaskEngine.class).addTask(taskKey, retryTask); - } - } - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/InstanceUpgradeHelper.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/InstanceUpgradeHelper.java deleted file mode 100644 index a2cfc608b..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/InstanceUpgradeHelper.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite.execute; - -import com.alibaba.nacos.naming.core.Instance; - -/** - * Help converting instance when upgrading/downgrading. - * - * @author gengtuo.ygt - * on 2021/2/25 - */ -public interface InstanceUpgradeHelper { - - /** - * Convert to v1 instance. - * - * @param v2 instance v2 - * @return instance v1 - */ - Instance toV1(com.alibaba.nacos.api.naming.pojo.Instance v2); - - /** - * Convert to v2 instance. - * - * @param v1 instance v1 - * @return instance v2 - */ - com.alibaba.nacos.api.naming.pojo.Instance toV2(Instance v1); - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/ServiceMetadataUpgradeHelper.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/ServiceMetadataUpgradeHelper.java deleted file mode 100644 index 3d8e5a887..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/execute/ServiceMetadataUpgradeHelper.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade.doublewrite.execute; - -import com.alibaba.nacos.naming.core.Cluster; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; -import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; - -/** - * Help converting service/cluster and its metadata when upgrading/downgrading. - * - * @author gengtuo.ygt - * on 2021/2/25 - */ -public interface ServiceMetadataUpgradeHelper { - - /** - * Convert to v1 service. - * - * @param v1 service v1, null if not exists, will create a new one - * @param v2 service v2 without metadata - * @param v2meta service v2 metadata - * @return service v1 - */ - Service toV1Service(Service v1, com.alibaba.nacos.naming.core.v2.pojo.Service v2, ServiceMetadata v2meta); - - /** - * Convert to v1 cluster. - * - * @param v1 cluster v1, null if not exists, will create a new one - * @param v2meta cluster v2 metadata - * @return cluster v1 - */ - Cluster toV1Cluster(Cluster v1, ClusterMetadata v2meta); - - /** - * Convert to v2 service metadata. - * - * @param service service v1 - * @param ephemeral is ephemeral - * @return service metadata v2 - */ - ServiceMetadata toV2ServiceMetadata(Service service, boolean ephemeral); - - /** - * Convert to v2 cluster metadata. - * - * @param v1 cluster v1 - * @return cluster metadata v2 - */ - ClusterMetadata toV2ClusterMetadata(Cluster v1); -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/ClientBeatCheckTask.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/ClientBeatCheckTask.java deleted file mode 100644 index 0fe552fcb..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/ClientBeatCheckTask.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * 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.nacos.naming.healthcheck; - -import com.alibaba.nacos.common.http.Callback; -import com.alibaba.nacos.common.model.RestResult; -import com.alibaba.nacos.common.utils.InternetAddressUtil; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.constants.FieldsConstants; -import com.alibaba.nacos.naming.core.DistroMapper; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import com.alibaba.nacos.naming.healthcheck.heartbeat.BeatCheckTask; -import com.alibaba.nacos.naming.misc.GlobalConfig; -import com.alibaba.nacos.naming.misc.HttpClient; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.NamingProxy; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.naming.push.UdpPushService; -import com.alibaba.nacos.sys.env.EnvUtil; -import com.alibaba.nacos.sys.utils.ApplicationUtils; -import com.fasterxml.jackson.annotation.JsonIgnore; - -import java.util.List; - -import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; - -/** - * Client beat check task of service for version 1.x. - * - * @author nkorange - */ -public class ClientBeatCheckTask implements BeatCheckTask { - - private Service service; - - public static final String EPHEMERAL = "true"; - - public ClientBeatCheckTask(Service service) { - this.service = service; - } - - @JsonIgnore - public UdpPushService getPushService() { - return ApplicationUtils.getBean(UdpPushService.class); - } - - @JsonIgnore - public DistroMapper getDistroMapper() { - return ApplicationUtils.getBean(DistroMapper.class); - } - - public GlobalConfig getGlobalConfig() { - return ApplicationUtils.getBean(GlobalConfig.class); - } - - public SwitchDomain getSwitchDomain() { - return ApplicationUtils.getBean(SwitchDomain.class); - } - - @Override - public String taskKey() { - return KeyBuilder.buildServiceMetaKey(service.getNamespaceId(), service.getName()); - } - - @Override - public void run() { - try { - // If upgrade to 2.0.X stop health check with v1 - if (ApplicationUtils.getBean(UpgradeJudgement.class).isUseGrpcFeatures()) { - return; - } - if (!getDistroMapper().responsible(service.getName())) { - return; - } - - if (!getSwitchDomain().isHealthCheckEnabled()) { - return; - } - - List instances = service.allIPs(true); - - // first set health status of instances: - for (Instance instance : instances) { - if (System.currentTimeMillis() - instance.getLastBeat() > instance.getInstanceHeartBeatTimeOut()) { - if (!instance.isMarked()) { - if (instance.isHealthy()) { - instance.setHealthy(false); - Loggers.EVT_LOG - .info("{POS} {IP-DISABLED} valid: {}:{}@{}@{}, region: {}, msg: client timeout after {}, last beat: {}", - instance.getIp(), instance.getPort(), instance.getClusterName(), - service.getName(), UtilsAndCommons.LOCALHOST_SITE, - instance.getInstanceHeartBeatTimeOut(), instance.getLastBeat()); - getPushService().serviceChanged(service); - } - } - } - } - - if (!getGlobalConfig().isExpireInstance()) { - return; - } - - // then remove obsolete instances: - for (Instance instance : instances) { - - if (instance.isMarked()) { - continue; - } - - if (System.currentTimeMillis() - instance.getLastBeat() > instance.getIpDeleteTimeout()) { - // delete instance - Loggers.SRV_LOG.info("[AUTO-DELETE-IP] service: {}, ip: {}", service.getName(), - JacksonUtils.toJson(instance)); - deleteIp(instance); - } - } - - } catch (Exception e) { - Loggers.SRV_LOG.warn("Exception while processing client beat time out.", e); - } - - } - - private void deleteIp(Instance instance) { - - try { - NamingProxy.Request request = NamingProxy.Request.newRequest(); - request.appendParam(FieldsConstants.IP, instance.getIp()) - .appendParam(FieldsConstants.PORT, String.valueOf(instance.getPort())) - .appendParam(FieldsConstants.EPHEMERAL, EPHEMERAL) - .appendParam(FieldsConstants.CLUSTER_NAME, instance.getClusterName()) - .appendParam(FieldsConstants.SERVICE_NAME, service.getName()) - .appendParam(FieldsConstants.NAME_SPACE_ID, service.getNamespaceId()); - - String url = HTTP_PREFIX + InternetAddressUtil.localHostIP() + InternetAddressUtil.IP_PORT_SPLITER + EnvUtil - .getPort() + EnvUtil.getContextPath() + UtilsAndCommons.NACOS_NAMING_CONTEXT - + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT + "?" + request.toUrl(); - - // delete instance asynchronously: - HttpClient.asyncHttpDelete(url, null, null, new Callback() { - @Override - public void onReceive(RestResult result) { - if (!result.ok()) { - Loggers.SRV_LOG - .error("[IP-DEAD] failed to delete ip automatically, ip: {}, caused {}, resp code: {}", - instance.toJson(), result.getMessage(), result.getCode()); - } - } - - @Override - public void onError(Throwable throwable) { - Loggers.SRV_LOG - .error("[IP-DEAD] failed to delete ip automatically, ip: {}, error: {}", instance.toJson(), - throwable); - } - - @Override - public void onCancel() { - - } - }); - - } catch (Exception e) { - Loggers.SRV_LOG - .error("[IP-DEAD] failed to delete ip automatically, ip: {}, error: {}", instance.toJson(), e); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/ClientBeatProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/ClientBeatProcessor.java deleted file mode 100644 index f2b7592ea..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/ClientBeatProcessor.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.nacos.naming.healthcheck; - -import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.trace.event.naming.HealthStateChangeTraceEvent; -import com.alibaba.nacos.naming.healthcheck.heartbeat.BeatProcessor; -import com.alibaba.nacos.sys.utils.ApplicationUtils; -import com.alibaba.nacos.naming.core.Cluster; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.naming.push.UdpPushService; -import com.fasterxml.jackson.annotation.JsonIgnore; - -import java.util.List; - -/** - * Thread to update ephemeral instance triggered by client beat for v1.x. - * - * @author nkorange - */ -public class ClientBeatProcessor implements BeatProcessor { - - private RsInfo rsInfo; - - private Service service; - - @JsonIgnore - public UdpPushService getPushService() { - return ApplicationUtils.getBean(UdpPushService.class); - } - - public RsInfo getRsInfo() { - return rsInfo; - } - - public void setRsInfo(RsInfo rsInfo) { - this.rsInfo = rsInfo; - } - - public Service getService() { - return service; - } - - public void setService(Service service) { - this.service = service; - } - - @Override - public void run() { - Service service = this.service; - if (Loggers.EVT_LOG.isDebugEnabled()) { - Loggers.EVT_LOG.debug("[CLIENT-BEAT] processing beat: {}", rsInfo.toString()); - } - - String ip = rsInfo.getIp(); - String clusterName = rsInfo.getCluster(); - int port = rsInfo.getPort(); - Cluster cluster = service.getClusterMap().get(clusterName); - List instances = cluster.allIPs(true); - - for (Instance instance : instances) { - if (instance.getIp().equals(ip) && instance.getPort() == port) { - if (Loggers.EVT_LOG.isDebugEnabled()) { - Loggers.EVT_LOG.debug("[CLIENT-BEAT] refresh beat: {}", rsInfo.toString()); - } - instance.setLastBeat(System.currentTimeMillis()); - if (!instance.isMarked() && !instance.isHealthy()) { - instance.setHealthy(true); - Loggers.EVT_LOG - .info("service: {} {POS} {IP-ENABLED} valid: {}:{}@{}, region: {}, msg: client beat ok", - cluster.getService().getName(), ip, port, cluster.getName(), - UtilsAndCommons.LOCALHOST_SITE); - getPushService().serviceChanged(service); - NotifyCenter.publishEvent(new HealthStateChangeTraceEvent(System.currentTimeMillis(), - service.getNamespaceId(), service.getGroupName(), service.getName(), instance.getIp(), - instance.getPort(), true, "client_beat")); - } - } - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckCommon.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckCommon.java deleted file mode 100644 index 56226d9db..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckCommon.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * 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.nacos.naming.healthcheck; - -import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.trace.event.naming.HealthStateChangeTraceEvent; -import com.alibaba.nacos.naming.core.Cluster; -import com.alibaba.nacos.naming.core.DistroMapper; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.naming.push.UdpPushService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Health check public methods. - * - * @author nkorange - * @since 1.0.0 - */ -@Component -@SuppressWarnings("PMD.ThreadPoolCreationRule") -public class HealthCheckCommon { - - @Autowired - private DistroMapper distroMapper; - - @Autowired - private SwitchDomain switchDomain; - - @Autowired - private UdpPushService pushService; - - /** - * Re-evaluate check responsce time. - * - * @param checkRT check response time - * @param task health check task - * @param params health params - */ - public void reEvaluateCheckRT(long checkRT, HealthCheckTask task, SwitchDomain.HealthParams params) { - task.setCheckRtLast(checkRT); - - if (checkRT > task.getCheckRtWorst()) { - task.setCheckRtWorst(checkRT); - } - - if (checkRT < task.getCheckRtBest()) { - task.setCheckRtBest(checkRT); - } - - checkRT = (long) ((params.getFactor() * task.getCheckRtNormalized()) + (1 - params.getFactor()) * checkRT); - - if (checkRT > params.getMax()) { - checkRT = params.getMax(); - } - - if (checkRT < params.getMin()) { - checkRT = params.getMin(); - } - - task.setCheckRtNormalized(checkRT); - } - - /** - * Health check pass. - * - * @param ip instance - * @param task health check task - * @param msg message - */ - public void checkOK(Instance ip, HealthCheckTask task, String msg) { - Cluster cluster = task.getCluster(); - - try { - if (!ip.isHealthy() || !ip.isMockValid()) { - if (ip.getOkCount().incrementAndGet() >= switchDomain.getCheckTimes()) { - if (distroMapper.responsible(cluster, ip)) { - ip.setHealthy(true); - ip.setMockValid(true); - - Service service = cluster.getService(); - service.setLastModifiedMillis(System.currentTimeMillis()); - pushService.serviceChanged(service); - - Loggers.EVT_LOG.info("serviceName: {} {POS} {IP-ENABLED} valid: {}:{}@{}, region: {}, msg: {}", - cluster.getService().getName(), ip.getIp(), ip.getPort(), cluster.getName(), - UtilsAndCommons.LOCALHOST_SITE, msg); - NotifyCenter.publishEvent(new HealthStateChangeTraceEvent(System.currentTimeMillis(), - service.getNamespaceId(), service.getGroupName(), service.getName(), ip.getIp(), ip.getPort(), - true, msg)); - } else { - if (!ip.isMockValid()) { - ip.setMockValid(true); - Loggers.EVT_LOG - .info("serviceName: {} {PROBE} {IP-ENABLED} valid: {}:{}@{}, region: {}, msg: {}", - cluster.getService().getName(), ip.getIp(), ip.getPort(), cluster.getName(), - UtilsAndCommons.LOCALHOST_SITE, msg); - } - } - } else { - Loggers.EVT_LOG.info("serviceName: {} {OTHER} {IP-ENABLED} pre-valid: {}:{}@{} in {}, msg: {}", - cluster.getService().getName(), ip.getIp(), ip.getPort(), cluster.getName(), - ip.getOkCount(), msg); - } - } - } catch (Throwable t) { - Loggers.SRV_LOG.error("[CHECK-OK] error when close check task.", t); - } - - ip.getFailCount().set(0); - ip.setBeingChecked(false); - } - - /** - * Health check fail, when instance check failed count more than max failed time, set unhealthy. - * - * @param ip instance - * @param task health check task - * @param msg message - */ - public void checkFail(Instance ip, HealthCheckTask task, String msg) { - Cluster cluster = task.getCluster(); - - try { - if (ip.isHealthy() || ip.isMockValid()) { - if (ip.getFailCount().incrementAndGet() >= switchDomain.getCheckTimes()) { - if (distroMapper.responsible(cluster, ip)) { - ip.setHealthy(false); - ip.setMockValid(false); - - Service service = cluster.getService(); - service.setLastModifiedMillis(System.currentTimeMillis()); - - pushService.serviceChanged(service); - - Loggers.EVT_LOG - .info("serviceName: {} {POS} {IP-DISABLED} invalid: {}:{}@{}, region: {}, msg: {}", - cluster.getService().getName(), ip.getIp(), ip.getPort(), cluster.getName(), - UtilsAndCommons.LOCALHOST_SITE, msg); - NotifyCenter.publishEvent(new HealthStateChangeTraceEvent(System.currentTimeMillis(), - service.getNamespaceId(), service.getGroupName(), service.getName(), ip.getIp(), - ip.getPort(), false, msg)); - } else { - Loggers.EVT_LOG - .info("serviceName: {} {PROBE} {IP-DISABLED} invalid: {}:{}@{}, region: {}, msg: {}", - cluster.getService().getName(), ip.getIp(), ip.getPort(), cluster.getName(), - UtilsAndCommons.LOCALHOST_SITE, msg); - } - - } else { - Loggers.EVT_LOG.info("serviceName: {} {OTHER} {IP-DISABLED} pre-invalid: {}:{}@{} in {}, msg: {}", - cluster.getService().getName(), ip.getIp(), ip.getPort(), cluster.getName(), - ip.getFailCount(), msg); - } - } - } catch (Throwable t) { - Loggers.SRV_LOG.error("[CHECK-FAIL] error when close check task.", t); - } - - ip.getOkCount().set(0); - - ip.setBeingChecked(false); - } - - /** - * Health check fail, set instance unhealthy directly. - * - * @param ip instance - * @param task health check task - * @param msg message - */ - public void checkFailNow(Instance ip, HealthCheckTask task, String msg) { - Cluster cluster = task.getCluster(); - try { - if (ip.isHealthy() || ip.isMockValid()) { - if (distroMapper.responsible(cluster, ip)) { - ip.setHealthy(false); - ip.setMockValid(false); - - Service service = cluster.getService(); - service.setLastModifiedMillis(System.currentTimeMillis()); - - pushService.serviceChanged(service); - - Loggers.EVT_LOG - .info("serviceName: {} {POS} {IP-DISABLED} invalid-now: {}:{}@{}, region: {}, msg: {}", - cluster.getService().getName(), ip.getIp(), ip.getPort(), cluster.getName(), - UtilsAndCommons.LOCALHOST_SITE, msg); - NotifyCenter.publishEvent(new HealthStateChangeTraceEvent(System.currentTimeMillis(), - service.getNamespaceId(), service.getGroupName(), service.getName(), ip.getIp(), - ip.getPort(), false, msg)); - - } else { - if (ip.isMockValid()) { - ip.setMockValid(false); - Loggers.EVT_LOG - .info("serviceName: {} {PROBE} {IP-DISABLED} invalid-now: {}:{}@{}, region: {}, msg: {}", - cluster.getService().getName(), ip.getIp(), ip.getPort(), cluster.getName(), - UtilsAndCommons.LOCALHOST_SITE, msg); - Service service = cluster.getService(); - } - - } - } - } catch (Throwable t) { - Loggers.SRV_LOG.error("[CHECK-FAIL-NOW] error when close check task.", t); - } - - ip.getOkCount().set(0); - ip.setBeingChecked(false); - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckProcessor.java deleted file mode 100644 index 09e9da3a7..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckProcessor.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.nacos.naming.healthcheck; - -/** - * Health check processor. - * - * @author nkorange - */ -public interface HealthCheckProcessor { - - /** - * Run check task for service. - * - * @param task check task - */ - void process(HealthCheckTask task); - - /** - * Get check task type, refer to enum HealthCheckType. - * - * @return check type - */ - String getType(); -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckProcessorDelegate.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckProcessorDelegate.java deleted file mode 100644 index 22dcdbb9d..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckProcessorDelegate.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.nacos.naming.healthcheck; - -import com.alibaba.nacos.naming.healthcheck.extend.HealthCheckExtendProvider; -import com.alibaba.nacos.naming.healthcheck.extend.HealthCheckProcessorExtendV1; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Delegate of health check. - * - * @author nacos - */ -@Component("healthCheckDelegate") -public class HealthCheckProcessorDelegate implements HealthCheckProcessor { - - private Map healthCheckProcessorMap = new HashMap<>(); - - public HealthCheckProcessorDelegate(HealthCheckExtendProvider provider, - HealthCheckProcessorExtendV1 healthCheckProcessorExtend) { - provider.setHealthCheckProcessorExtend(healthCheckProcessorExtend); - provider.init(); - } - - @Autowired - public void addProcessor(Collection processors) { - healthCheckProcessorMap.putAll(processors.stream().filter(processor -> processor.getType() != null) - .collect(Collectors.toMap(HealthCheckProcessor::getType, processor -> processor))); - } - - @Override - public void process(HealthCheckTask task) { - - String type = task.getCluster().getHealthChecker().getType(); - HealthCheckProcessor processor = healthCheckProcessorMap.get(type); - if (processor == null) { - processor = healthCheckProcessorMap.get(NoneHealthCheckProcessor.TYPE); - } - - processor.process(task); - } - - @Override - public String getType() { - return null; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckReactor.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckReactor.java index f2d1ccf61..c07716c85 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckReactor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckReactor.java @@ -37,17 +37,6 @@ public class HealthCheckReactor { private static Map futureMap = new ConcurrentHashMap<>(); - /** - * Schedule health check task. - * - * @param task health check task - * @return scheduled future - */ - public static ScheduledFuture scheduleCheck(HealthCheckTask task) { - task.setStartTime(System.currentTimeMillis()); - return GlobalExecutor.scheduleNamingHealth(task, task.getCheckRtNormalized(), TimeUnit.MILLISECONDS); - } - /** * Schedule health check task for v2. * diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckStatus.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckStatus.java index 9b1b891ee..26243f65c 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckStatus.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckStatus.java @@ -16,12 +16,7 @@ package com.alibaba.nacos.naming.healthcheck; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.misc.Loggers; - import java.io.Serializable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -41,44 +36,4 @@ public class HealthCheckStatus implements Serializable { public AtomicInteger checkOkCount = new AtomicInteger(0); public long checkRt = -1L; - - private static ConcurrentMap statusMap = new ConcurrentHashMap<>(); - - public static void reset(Instance instance) { - statusMap.put(buildKey(instance), new HealthCheckStatus()); - } - - /** - * Get health check status of instance. - * - * @param instance instance - * @return health check status - */ - public static HealthCheckStatus get(Instance instance) { - String key = buildKey(instance); - - if (!statusMap.containsKey(key)) { - statusMap.putIfAbsent(key, new HealthCheckStatus()); - } - - return statusMap.get(key); - } - - public static void remv(Instance instance) { - statusMap.remove(buildKey(instance)); - } - - private static String buildKey(Instance instance) { - try { - - String clusterName = instance.getClusterName(); - String serviceName = instance.getServiceName(); - String datumKey = instance.getDatumKey(); - return serviceName + ":" + clusterName + ":" + datumKey; - } catch (Throwable e) { - Loggers.SRV_LOG.error("[BUILD-KEY] Exception while set rt, ip {}, error: {}", instance.toJson(), e); - } - - return instance.getDefaultKey(); - } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckTask.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckTask.java deleted file mode 100644 index 5912f1a2b..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckTask.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * 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.nacos.naming.healthcheck; - -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import com.alibaba.nacos.sys.utils.ApplicationUtils; -import com.alibaba.nacos.naming.core.Cluster; -import com.alibaba.nacos.naming.core.DistroMapper; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.fasterxml.jackson.annotation.JsonIgnore; - -import com.alibaba.nacos.common.utils.RandomUtils; - -/** - * Health check task. - * - * @author nacos - */ -public class HealthCheckTask implements Runnable { - - private Cluster cluster; - - private long checkRtNormalized = -1; - - private long checkRtBest = -1; - - private long checkRtWorst = -1; - - private long checkRtLast = -1; - - private long checkRtLastLast = -1; - - private long startTime; - - private volatile boolean cancelled = false; - - @JsonIgnore - private final DistroMapper distroMapper; - - @JsonIgnore - private final SwitchDomain switchDomain; - - @JsonIgnore - private final HealthCheckProcessor healthCheckProcessor; - - public HealthCheckTask(Cluster cluster) { - this.cluster = cluster; - distroMapper = ApplicationUtils.getBean(DistroMapper.class); - switchDomain = ApplicationUtils.getBean(SwitchDomain.class); - healthCheckProcessor = ApplicationUtils.getBean(HealthCheckProcessorDelegate.class); - initCheckRT(); - } - - private void initCheckRT() { - // first check time delay - checkRtNormalized = - 2000 + RandomUtils.nextInt(0, RandomUtils.nextInt(0, switchDomain.getTcpHealthParams().getMax())); - checkRtBest = Long.MAX_VALUE; - checkRtWorst = 0L; - } - - @Override - public void run() { - - try { - // If upgrade to 2.0.X stop health check with v1 - if (ApplicationUtils.getBean(UpgradeJudgement.class).isUseGrpcFeatures()) { - return; - } - if (distroMapper.responsible(cluster.getService().getName()) && switchDomain - .isHealthCheckEnabled(cluster.getService().getName())) { - healthCheckProcessor.process(this); - if (Loggers.EVT_LOG.isDebugEnabled()) { - Loggers.EVT_LOG - .debug("[HEALTH-CHECK] schedule health check task: {}", cluster.getService().getName()); - } - } - } catch (Throwable e) { - Loggers.SRV_LOG - .error("[HEALTH-CHECK] error while process health check for {}:{}", cluster.getService().getName(), - cluster.getName(), e); - } finally { - if (!cancelled) { - HealthCheckReactor.scheduleCheck(this); - - // worst == 0 means never checked - if (this.getCheckRtWorst() > 0 && switchDomain.isHealthCheckEnabled(cluster.getService().getName()) - && distroMapper.responsible(cluster.getService().getName())) { - // TLog doesn't support float so we must convert it into long - long diff = - ((this.getCheckRtLast() - this.getCheckRtLastLast()) * 10000) / this.getCheckRtLastLast(); - - this.setCheckRtLastLast(this.getCheckRtLast()); - - Cluster cluster = this.getCluster(); - - if (Loggers.CHECK_RT.isDebugEnabled()) { - Loggers.CHECK_RT.debug("{}:{}@{}->normalized: {}, worst: {}, best: {}, last: {}, diff: {}", - cluster.getService().getName(), cluster.getName(), cluster.getHealthChecker().getType(), - this.getCheckRtNormalized(), this.getCheckRtWorst(), this.getCheckRtBest(), - this.getCheckRtLast(), diff); - } - } - } - } - } - - public Cluster getCluster() { - return cluster; - } - - public void setCluster(Cluster cluster) { - this.cluster = cluster; - } - - public long getCheckRtNormalized() { - return checkRtNormalized; - } - - public long getCheckRtBest() { - return checkRtBest; - } - - public long getCheckRtWorst() { - return checkRtWorst; - } - - public void setCheckRtWorst(long checkRtWorst) { - this.checkRtWorst = checkRtWorst; - } - - public void setCheckRtBest(long checkRtBest) { - this.checkRtBest = checkRtBest; - } - - public void setCheckRtNormalized(long checkRtNormalized) { - this.checkRtNormalized = checkRtNormalized; - } - - public boolean isCancelled() { - return cancelled; - } - - public void setCancelled(boolean cancelled) { - this.cancelled = cancelled; - } - - public long getStartTime() { - return startTime; - } - - public void setStartTime(long startTime) { - this.startTime = startTime; - } - - public long getCheckRtLast() { - return checkRtLast; - } - - public void setCheckRtLast(long checkRtLast) { - this.checkRtLast = checkRtLast; - } - - public long getCheckRtLastLast() { - return checkRtLastLast; - } - - public void setCheckRtLastLast(long checkRtLastLast) { - this.checkRtLastLast = checkRtLastLast; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HttpHealthCheckProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HttpHealthCheckProcessor.java deleted file mode 100644 index 0deaa7dd3..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HttpHealthCheckProcessor.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * 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.nacos.naming.healthcheck; - -import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Http; -import com.alibaba.nacos.common.http.Callback; -import com.alibaba.nacos.common.http.HttpUtils; -import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; -import com.alibaba.nacos.common.http.param.Header; -import com.alibaba.nacos.common.http.param.Query; -import com.alibaba.nacos.common.model.RestResult; -import com.alibaba.nacos.naming.core.Cluster; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.misc.HttpClientManager; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.monitor.MetricsMonitor; -import org.apache.commons.collections.CollectionUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.net.ConnectException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.List; -import java.util.Map; - -import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; -import static com.alibaba.nacos.naming.misc.Loggers.SRV_LOG; - -/** - * HTTP health check processor. - * - * @author xuanyin.zy - */ -@Component("httpHealthCheckProcessorV1") -public class HttpHealthCheckProcessor implements HealthCheckProcessor { - - public static final String TYPE = "HTTP"; - - @Autowired - private SwitchDomain switchDomain; - - @Autowired - private HealthCheckCommon healthCheckCommon; - - private static final NacosAsyncRestTemplate ASYNC_REST_TEMPLATE = HttpClientManager.getProcessorNacosAsyncRestTemplate(); - - @Override - public String getType() { - return TYPE; - } - - @Override - public void process(HealthCheckTask task) { - List ips = task.getCluster().allIPs(false); - if (CollectionUtils.isEmpty(ips)) { - return; - } - - if (!switchDomain.isHealthCheckEnabled()) { - return; - } - - Cluster cluster = task.getCluster(); - - for (Instance ip : ips) { - try { - - if (ip.isMarked()) { - if (SRV_LOG.isDebugEnabled()) { - SRV_LOG.debug("http check, ip is marked as to skip health check, ip: {}" + ip.getIp()); - } - continue; - } - - if (!ip.markChecking()) { - SRV_LOG.warn("http check started before last one finished, service: {}:{}:{}", - task.getCluster().getService().getName(), task.getCluster().getName(), ip.getIp()); - - healthCheckCommon.reEvaluateCheckRT(task.getCheckRtNormalized() * 2, task, - switchDomain.getHttpHealthParams()); - continue; - } - - Http healthChecker = (Http) cluster.getHealthChecker(); - - int ckPort = cluster.isUseIPPort4Check() ? ip.getPort() : cluster.getDefCkport(); - URL host = new URL(HTTP_PREFIX + ip.getIp() + ":" + ckPort); - URL target = new URL(host, healthChecker.getPath()); - Map customHeaders = healthChecker.getCustomHeaders(); - Header header = Header.newInstance(); - header.addAll(customHeaders); - - ASYNC_REST_TEMPLATE.get(target.toString(), header, Query.EMPTY, String.class, - new HttpHealthCheckCallback(ip, task)); - MetricsMonitor.getHttpHealthCheckMonitor().incrementAndGet(); - } catch (Throwable e) { - ip.setCheckRt(switchDomain.getHttpHealthParams().getMax()); - healthCheckCommon.checkFail(ip, task, "http:error:" + e.getMessage()); - healthCheckCommon.reEvaluateCheckRT(switchDomain.getHttpHealthParams().getMax(), task, - switchDomain.getHttpHealthParams()); - } - } - } - - private class HttpHealthCheckCallback implements Callback { - - private Instance ip; - - private HealthCheckTask task; - - private long startTime = System.currentTimeMillis(); - - public HttpHealthCheckCallback(Instance ip, HealthCheckTask task) { - this.ip = ip; - this.task = task; - } - - @Override - public void onReceive(RestResult result) { - ip.setCheckRt(System.currentTimeMillis() - startTime); - - int httpCode = result.getCode(); - if (HttpURLConnection.HTTP_OK == httpCode) { - healthCheckCommon.checkOK(ip, task, "http:" + httpCode); - healthCheckCommon.reEvaluateCheckRT(System.currentTimeMillis() - startTime, task, - switchDomain.getHttpHealthParams()); - } else if (HttpURLConnection.HTTP_UNAVAILABLE == httpCode - || HttpURLConnection.HTTP_MOVED_TEMP == httpCode) { - // server is busy, need verification later - healthCheckCommon.checkFail(ip, task, "http:" + httpCode); - healthCheckCommon - .reEvaluateCheckRT(task.getCheckRtNormalized() * 2, task, switchDomain.getHttpHealthParams()); - } else { - //probably means the state files has been removed by administrator - healthCheckCommon.checkFailNow(ip, task, "http:" + httpCode); - healthCheckCommon.reEvaluateCheckRT(switchDomain.getHttpHealthParams().getMax(), task, - switchDomain.getHttpHealthParams()); - } - - } - - @Override - public void onError(Throwable t) { - ip.setCheckRt(System.currentTimeMillis() - startTime); - - Throwable cause = t; - int maxStackDepth = 50; - for (int deepth = 0; deepth < maxStackDepth && cause != null; deepth++) { - if (HttpUtils.isTimeoutException(t)) { - - healthCheckCommon.checkFail(ip, task, "http:timeout:" + cause.getMessage()); - healthCheckCommon.reEvaluateCheckRT(task.getCheckRtNormalized() * 2, task, - switchDomain.getHttpHealthParams()); - - return; - } - - cause = cause.getCause(); - } - - // connection error, probably not reachable - if (t instanceof ConnectException) { - healthCheckCommon.checkFailNow(ip, task, "http:unable2connect:" + t.getMessage()); - healthCheckCommon.reEvaluateCheckRT(switchDomain.getHttpHealthParams().getMax(), task, - switchDomain.getHttpHealthParams()); - } else { - healthCheckCommon.checkFail(ip, task, "http:error:" + t.getMessage()); - healthCheckCommon.reEvaluateCheckRT(switchDomain.getHttpHealthParams().getMax(), task, - switchDomain.getHttpHealthParams()); - } - } - - @Override - public void onCancel() { - - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/MysqlHealthCheckProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/MysqlHealthCheckProcessor.java deleted file mode 100644 index 0b313d61f..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/MysqlHealthCheckProcessor.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * 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.nacos.naming.healthcheck; - -import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Mysql; -import com.alibaba.nacos.naming.core.Cluster; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.misc.GlobalExecutor; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.monitor.MetricsMonitor; -import org.apache.commons.collections.CollectionUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.net.ConnectException; -import java.net.SocketTimeoutException; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeoutException; - -import static com.alibaba.nacos.naming.misc.Loggers.SRV_LOG; - -/** - * MYSQL health check processor. - * - * @author nacos - */ -@Component("mysqlHealthCheckProcessorV1") -@SuppressWarnings("PMD.ThreadPoolCreationRule") -public class MysqlHealthCheckProcessor implements HealthCheckProcessor { - - public static final String TYPE = "MYSQL"; - - @Autowired - private HealthCheckCommon healthCheckCommon; - - @Autowired - private SwitchDomain switchDomain; - - public static final int CONNECT_TIMEOUT_MS = 500; - - private static final String CHECK_MYSQL_MASTER_SQL = "show global variables where variable_name='read_only'"; - - private static final String MYSQL_SLAVE_READONLY = "ON"; - - private static final ConcurrentMap CONNECTION_POOL = new ConcurrentHashMap(); - - public MysqlHealthCheckProcessor() { - } - - @Override - public String getType() { - return TYPE; - } - - @Override - public void process(HealthCheckTask task) { - List ips = task.getCluster().allIPs(false); - - SRV_LOG.debug("mysql check, ips:" + ips); - if (CollectionUtils.isEmpty(ips)) { - return; - } - - for (Instance ip : ips) { - try { - - if (ip.isMarked()) { - if (SRV_LOG.isDebugEnabled()) { - SRV_LOG.debug("mysql check, ip is marked as to skip health check, ip: {}", ip.getIp()); - } - continue; - } - - if (!ip.markChecking()) { - SRV_LOG.warn("mysql check started before last one finished, service: {}:{}:{}", - task.getCluster().getService().getName(), task.getCluster().getName(), ip.getIp()); - - healthCheckCommon.reEvaluateCheckRT(task.getCheckRtNormalized() * 2, task, - switchDomain.getMysqlHealthParams()); - continue; - } - - GlobalExecutor.executeMysqlCheckTask(new MysqlCheckTask(ip, task)); - MetricsMonitor.getMysqlHealthCheckMonitor().incrementAndGet(); - } catch (Exception e) { - ip.setCheckRt(switchDomain.getMysqlHealthParams().getMax()); - healthCheckCommon.checkFail(ip, task, "mysql:error:" + e.getMessage()); - healthCheckCommon.reEvaluateCheckRT(switchDomain.getMysqlHealthParams().getMax(), task, - switchDomain.getMysqlHealthParams()); - } - } - } - - private class MysqlCheckTask implements Runnable { - - private Instance ip; - - private HealthCheckTask task; - - private long startTime = System.currentTimeMillis(); - - public MysqlCheckTask(Instance ip, HealthCheckTask task) { - this.ip = ip; - this.task = task; - } - - @Override - public void run() { - - Statement statement = null; - ResultSet resultSet = null; - - try { - - Cluster cluster = task.getCluster(); - String key = cluster.getService().getName() + ":" + cluster.getName() + ":" + ip.getIp() + ":" + ip - .getPort(); - Connection connection = CONNECTION_POOL.get(key); - Mysql config = (Mysql) cluster.getHealthChecker(); - - if (connection == null || connection.isClosed()) { - String url = - "jdbc:mysql://" + ip.getIp() + ":" + ip.getPort() + "?connectTimeout=" + CONNECT_TIMEOUT_MS - + "&socketTimeout=" + CONNECT_TIMEOUT_MS + "&loginTimeout=" + 1; - connection = DriverManager.getConnection(url, config.getUser(), config.getPwd()); - CONNECTION_POOL.put(key, connection); - } - - statement = connection.createStatement(); - statement.setQueryTimeout(1); - - resultSet = statement.executeQuery(config.getCmd()); - int resultColumnIndex = 2; - - if (CHECK_MYSQL_MASTER_SQL.equals(config.getCmd())) { - resultSet.next(); - if (MYSQL_SLAVE_READONLY.equals(resultSet.getString(resultColumnIndex))) { - throw new IllegalStateException("current node is slave!"); - } - } - - healthCheckCommon.checkOK(ip, task, "mysql:+ok"); - healthCheckCommon.reEvaluateCheckRT(System.currentTimeMillis() - startTime, task, - switchDomain.getMysqlHealthParams()); - } catch (SQLException e) { - // fail immediately - healthCheckCommon.checkFailNow(ip, task, "mysql:" + e.getMessage()); - healthCheckCommon.reEvaluateCheckRT(switchDomain.getHttpHealthParams().getMax(), task, - switchDomain.getMysqlHealthParams()); - } catch (Throwable t) { - Throwable cause = t; - int maxStackDepth = 50; - for (int deepth = 0; deepth < maxStackDepth && cause != null; deepth++) { - if (cause instanceof SocketTimeoutException || cause instanceof ConnectException - || cause instanceof TimeoutException || cause.getCause() instanceof TimeoutException) { - - healthCheckCommon.checkFail(ip, task, "mysql:timeout:" + cause.getMessage()); - healthCheckCommon.reEvaluateCheckRT(task.getCheckRtNormalized() * 2, task, - switchDomain.getMysqlHealthParams()); - return; - } - - cause = cause.getCause(); - } - - // connection error, probably not reachable - healthCheckCommon.checkFail(ip, task, "mysql:error:" + t.getMessage()); - healthCheckCommon.reEvaluateCheckRT(switchDomain.getMysqlHealthParams().getMax(), task, - switchDomain.getMysqlHealthParams()); - } finally { - ip.setCheckRt(System.currentTimeMillis() - startTime); - if (statement != null) { - try { - statement.close(); - } catch (SQLException e) { - Loggers.SRV_LOG.error("[MYSQL-CHECK] failed to close statement:" + statement, e); - } - } - if (resultSet != null) { - try { - resultSet.close(); - } catch (SQLException e) { - Loggers.SRV_LOG.error("[MYSQL-CHECK] failed to close resultSet:" + resultSet, e); - } - } - } - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/TcpSuperSenseProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/TcpSuperSenseProcessor.java deleted file mode 100644 index ad6593786..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/TcpSuperSenseProcessor.java +++ /dev/null @@ -1,426 +0,0 @@ -/* - * 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.nacos.naming.healthcheck; - -import com.alibaba.nacos.naming.core.Cluster; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.misc.GlobalExecutor; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.monitor.MetricsMonitor; -import com.alibaba.nacos.sys.env.EnvUtil; -import org.apache.commons.collections.CollectionUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.net.ConnectException; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.SocketChannel; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; - -import static com.alibaba.nacos.naming.misc.Loggers.SRV_LOG; - -/** - * TCP health check processor. - * - * @author nacos - */ -@Component -@SuppressWarnings("PMD.ThreadPoolCreationRule") -public class TcpSuperSenseProcessor implements HealthCheckProcessor, Runnable { - - public static final String TYPE = "TCP"; - - @Autowired - private HealthCheckCommon healthCheckCommon; - - @Autowired - private SwitchDomain switchDomain; - - public static final int CONNECT_TIMEOUT_MS = 500; - - private Map keyMap = new ConcurrentHashMap<>(); - - private BlockingQueue taskQueue = new LinkedBlockingQueue(); - - /** - * this value has been carefully tuned, do not modify unless you're confident. - */ - private static final int NIO_THREAD_COUNT = EnvUtil.getAvailableProcessors(0.5); - - /** - * because some hosts doesn't support keep-alive connections, disabled temporarily. - */ - private static final long TCP_KEEP_ALIVE_MILLIS = 0; - - private Selector selector; - - /** - * Tcp super sense processor construct. - * - * @throws IllegalStateException when construct failed - */ - public TcpSuperSenseProcessor() { - try { - selector = Selector.open(); - - GlobalExecutor.submitTcpCheck(this); - - } catch (Exception e) { - throw new IllegalStateException("Error while initializing SuperSense(TM)."); - } - } - - @Override - public void process(HealthCheckTask task) { - List ips = task.getCluster().allIPs(false); - - if (CollectionUtils.isEmpty(ips)) { - return; - } - - for (Instance ip : ips) { - - if (ip.isMarked()) { - if (SRV_LOG.isDebugEnabled()) { - SRV_LOG.debug("tcp check, ip is marked as to skip health check, ip:" + ip.getIp()); - } - continue; - } - - if (!ip.markChecking()) { - SRV_LOG.warn("tcp check started before last one finished, service: " + task.getCluster().getService() - .getName() + ":" + task.getCluster().getName() + ":" + ip.getIp() + ":" + ip.getPort()); - - healthCheckCommon - .reEvaluateCheckRT(task.getCheckRtNormalized() * 2, task, switchDomain.getTcpHealthParams()); - continue; - } - - Beat beat = new Beat(ip, task); - taskQueue.add(beat); - MetricsMonitor.getTcpHealthCheckMonitor().incrementAndGet(); - } - } - - private void processTask() throws Exception { - Collection> tasks = new LinkedList<>(); - do { - Beat beat = taskQueue.poll(CONNECT_TIMEOUT_MS / 2, TimeUnit.MILLISECONDS); - if (beat == null) { - return; - } - - tasks.add(new TaskProcessor(beat)); - } while (taskQueue.size() > 0 && tasks.size() < NIO_THREAD_COUNT * 64); - - for (Future f : GlobalExecutor.invokeAllTcpSuperSenseTask(tasks)) { - f.get(); - } - } - - @Override - public void run() { - while (true) { - try { - processTask(); - - int readyCount = selector.selectNow(); - if (readyCount <= 0) { - continue; - } - - Iterator iter = selector.selectedKeys().iterator(); - while (iter.hasNext()) { - SelectionKey key = iter.next(); - iter.remove(); - - GlobalExecutor.executeTcpSuperSense(new PostProcessor(key)); - } - } catch (Throwable e) { - SRV_LOG.error("[HEALTH-CHECK] error while processing NIO task", e); - } - } - } - - public class PostProcessor implements Runnable { - - SelectionKey key; - - public PostProcessor(SelectionKey key) { - this.key = key; - } - - @Override - public void run() { - Beat beat = (Beat) key.attachment(); - SocketChannel channel = (SocketChannel) key.channel(); - try { - if (!beat.isHealthy()) { - //invalid beat means this server is no longer responsible for the current service - key.cancel(); - key.channel().close(); - - beat.finishCheck(); - return; - } - - if (key.isValid() && key.isConnectable()) { - //connected - channel.finishConnect(); - beat.finishCheck(true, false, System.currentTimeMillis() - beat.getTask().getStartTime(), - "tcp:ok+"); - } - - if (key.isValid() && key.isReadable()) { - //disconnected - ByteBuffer buffer = ByteBuffer.allocate(128); - if (channel.read(buffer) == -1) { - key.cancel(); - key.channel().close(); - } else { - // not terminate request, ignore - } - } - } catch (ConnectException e) { - // unable to connect, possibly port not opened - beat.finishCheck(false, true, switchDomain.getTcpHealthParams().getMax(), - "tcp:unable2connect:" + e.getMessage()); - } catch (Exception e) { - beat.finishCheck(false, false, switchDomain.getTcpHealthParams().getMax(), - "tcp:error:" + e.getMessage()); - - try { - key.cancel(); - key.channel().close(); - } catch (Exception ignore) { - } - } - } - } - - private class Beat { - - Instance ip; - - HealthCheckTask task; - - long startTime = System.currentTimeMillis(); - - Beat(Instance ip, HealthCheckTask task) { - this.ip = ip; - this.task = task; - } - - public void setStartTime(long time) { - startTime = time; - } - - public long getStartTime() { - return startTime; - } - - public Instance getIp() { - return ip; - } - - public HealthCheckTask getTask() { - return task; - } - - public boolean isHealthy() { - return System.currentTimeMillis() - startTime < TimeUnit.SECONDS.toMillis(30L); - } - - /** - * finish check only, no ip state will be changed. - */ - public void finishCheck() { - ip.setBeingChecked(false); - } - - public void finishCheck(boolean success, boolean now, long rt, String msg) { - ip.setCheckRt(System.currentTimeMillis() - startTime); - - if (success) { - healthCheckCommon.checkOK(ip, task, msg); - } else { - if (now) { - healthCheckCommon.checkFailNow(ip, task, msg); - } else { - healthCheckCommon.checkFail(ip, task, msg); - } - - keyMap.remove(task.toString()); - } - - healthCheckCommon.reEvaluateCheckRT(rt, task, switchDomain.getTcpHealthParams()); - } - - @Override - public String toString() { - return task.getCluster().getService().getName() + ":" + task.getCluster().getName() + ":" + ip.getIp() + ":" - + ip.getPort(); - } - - @Override - public int hashCode() { - return Objects.hash(ip.toJson()); - } - - @Override - public boolean equals(Object obj) { - if (obj == null || !(obj instanceof Beat)) { - return false; - } - - return this.toString().equals(obj.toString()); - } - } - - private static class BeatKey { - - public SelectionKey key; - - public long birthTime; - - public BeatKey(SelectionKey key) { - this.key = key; - this.birthTime = System.currentTimeMillis(); - } - } - - private static class TimeOutTask implements Runnable { - - SelectionKey key; - - public TimeOutTask(SelectionKey key) { - this.key = key; - } - - @Override - public void run() { - if (key != null && key.isValid()) { - SocketChannel channel = (SocketChannel) key.channel(); - Beat beat = (Beat) key.attachment(); - - if (channel.isConnected()) { - return; - } - - try { - channel.finishConnect(); - } catch (Exception ignore) { - } - - try { - beat.finishCheck(false, false, beat.getTask().getCheckRtNormalized() * 2, "tcp:timeout"); - key.cancel(); - key.channel().close(); - } catch (Exception ignore) { - } - } - } - } - - private class TaskProcessor implements Callable { - - private static final int MAX_WAIT_TIME_MILLISECONDS = 500; - - Beat beat; - - public TaskProcessor(Beat beat) { - this.beat = beat; - } - - @Override - public Void call() { - long waited = System.currentTimeMillis() - beat.getStartTime(); - if (waited > MAX_WAIT_TIME_MILLISECONDS) { - Loggers.SRV_LOG.warn("beat task waited too long: " + waited + "ms"); - } - - SocketChannel channel = null; - try { - Instance instance = beat.getIp(); - - BeatKey beatKey = keyMap.get(beat.toString()); - if (beatKey != null && beatKey.key.isValid()) { - if (System.currentTimeMillis() - beatKey.birthTime < TCP_KEEP_ALIVE_MILLIS) { - instance.setBeingChecked(false); - return null; - } - - beatKey.key.cancel(); - beatKey.key.channel().close(); - } - - channel = SocketChannel.open(); - channel.configureBlocking(false); - // only by setting this can we make the socket close event asynchronous - channel.socket().setSoLinger(false, -1); - channel.socket().setReuseAddress(true); - channel.socket().setKeepAlive(true); - channel.socket().setTcpNoDelay(true); - - Cluster cluster = beat.getTask().getCluster(); - int port = cluster.isUseIPPort4Check() ? instance.getPort() : cluster.getDefCkport(); - channel.connect(new InetSocketAddress(instance.getIp(), port)); - - SelectionKey key = channel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ); - key.attach(beat); - keyMap.put(beat.toString(), new BeatKey(key)); - - beat.setStartTime(System.currentTimeMillis()); - - GlobalExecutor - .scheduleTcpSuperSenseTask(new TimeOutTask(key), CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); - } catch (Exception e) { - beat.finishCheck(false, false, switchDomain.getTcpHealthParams().getMax(), - "tcp:error:" + e.getMessage()); - - if (channel != null) { - try { - channel.close(); - } catch (Exception ignore) { - } - } - } - - return null; - } - } - - @Override - public String getType() { - return TYPE; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/extend/AbstractHealthCheckProcessorExtend.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/extend/AbstractHealthCheckProcessorExtend.java index b9c1ce94f..47c7040c8 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/extend/AbstractHealthCheckProcessorExtend.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/extend/AbstractHealthCheckProcessorExtend.java @@ -34,7 +34,7 @@ public abstract class AbstractHealthCheckProcessorExtend implements BeanFactoryA protected SingletonBeanRegistry registry; /** - * Add HealthCheckProcessor Or HealthCheckProcessorV2. + * Add HealthCheckProcessorV2. * * @param origin Origin Checker Type * @return Extend Processor Type diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/extend/HealthCheckProcessorExtendV1.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/extend/HealthCheckProcessorExtendV1.java deleted file mode 100644 index 822419415..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/extend/HealthCheckProcessorExtendV1.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.nacos.naming.healthcheck.extend; - -import com.alibaba.nacos.common.spi.NacosServiceLoader; -import com.alibaba.nacos.naming.healthcheck.HealthCheckProcessor; -import org.springframework.stereotype.Component; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -/** - * Health Check Processor Extend V1. - * - * @author sunmengying - */ -@Component -public class HealthCheckProcessorExtendV1 extends AbstractHealthCheckProcessorExtend { - - private final Collection processors = NacosServiceLoader.load(HealthCheckProcessor.class); - - @Override - public Set addProcessor(Set origin) { - Iterator processorIt = processors.iterator(); - Set processorType = new HashSet<>(origin); - while (processorIt.hasNext()) { - HealthCheckProcessor processor = processorIt.next(); - String type = processor.getType(); - if (processorType.contains(type)) { - throw new RuntimeException( - "More than one processor of the same type was found : [type=\"" + type + "\"]"); - } - processorType.add(type); - registry.registerSingleton(lowerFirstChar(processor.getClass().getSimpleName()), processor); - } - return processorType; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/interceptor/HealthCheckEnableInterceptor.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/interceptor/HealthCheckEnableInterceptor.java index 8e43dd2d4..36ebe6e61 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/interceptor/HealthCheckEnableInterceptor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/interceptor/HealthCheckEnableInterceptor.java @@ -16,7 +16,6 @@ package com.alibaba.nacos.naming.healthcheck.interceptor; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; import com.alibaba.nacos.naming.healthcheck.NacosHealthCheckTask; import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.sys.utils.ApplicationUtils; @@ -31,8 +30,7 @@ public class HealthCheckEnableInterceptor extends AbstractHealthCheckInterceptor @Override public boolean intercept(NacosHealthCheckTask object) { try { - return !ApplicationUtils.getBean(SwitchDomain.class).isHealthCheckEnabled() || !ApplicationUtils - .getBean(UpgradeJudgement.class).isUseGrpcFeatures(); + return !ApplicationUtils.getBean(SwitchDomain.class).isHealthCheckEnabled(); } catch (Exception e) { return true; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/v2/processor/HealthCheckCommonV2.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/v2/processor/HealthCheckCommonV2.java index 8af25739f..0f84faac0 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/v2/processor/HealthCheckCommonV2.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/v2/processor/HealthCheckCommonV2.java @@ -51,7 +51,7 @@ public class HealthCheckCommonV2 { private PersistentHealthStatusSynchronizer healthStatusSynchronizer; /** - * Re-evaluate check responsce time. + * Re-evaluate check response time. * * @param checkRT check response time * @param task health check task diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/v2/processor/HealthCheckProcessorV2Delegate.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/v2/processor/HealthCheckProcessorV2Delegate.java index 9a5c4f88d..9a22d78fe 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/v2/processor/HealthCheckProcessorV2Delegate.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/v2/processor/HealthCheckProcessorV2Delegate.java @@ -18,7 +18,6 @@ package com.alibaba.nacos.naming.healthcheck.v2.processor; import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.healthcheck.NoneHealthCheckProcessor; import com.alibaba.nacos.naming.healthcheck.extend.HealthCheckExtendProvider; import com.alibaba.nacos.naming.healthcheck.extend.HealthCheckProcessorExtendV2; import com.alibaba.nacos.naming.healthcheck.v2.HealthCheckTaskV2; @@ -41,7 +40,7 @@ public class HealthCheckProcessorV2Delegate implements HealthCheckProcessorV2 { private final Map healthCheckProcessorMap = new HashMap<>(); public HealthCheckProcessorV2Delegate(HealthCheckExtendProvider provider, - HealthCheckProcessorExtendV2 healthCheckProcessorExtend) { + HealthCheckProcessorExtendV2 healthCheckProcessorExtend) { provider.setHealthCheckProcessorExtend(healthCheckProcessorExtend); provider.init(); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalConfig.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalConfig.java index c6ccf110a..b5c1770b3 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalConfig.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalConfig.java @@ -16,22 +16,15 @@ package com.alibaba.nacos.naming.misc; -import com.alibaba.nacos.core.distributed.distro.DistroConfig; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.stereotype.Component; -import javax.annotation.PostConstruct; - import static com.alibaba.nacos.naming.constants.Constants.DATA_WARMUP; -import static com.alibaba.nacos.naming.constants.Constants.DISTRO_BATCH_SYNC_KEY_COUNT; -import static com.alibaba.nacos.naming.constants.Constants.DISTRO_SYNC_RETRY_DELAY; -import static com.alibaba.nacos.naming.constants.Constants.DISTRO_TASK_DISPATCH_PERIOD; import static com.alibaba.nacos.naming.constants.Constants.EMPTY_SERVICE_CLEAN_INTERVAL; import static com.alibaba.nacos.naming.constants.Constants.EMPTY_SERVICE_EXPIRED_TIME; import static com.alibaba.nacos.naming.constants.Constants.EXPIRED_METADATA_CLEAN_INTERVAL; import static com.alibaba.nacos.naming.constants.Constants.EXPIRED_METADATA_EXPIRED_TIME; import static com.alibaba.nacos.naming.constants.Constants.EXPIRE_INSTANCE; -import static com.alibaba.nacos.naming.constants.Constants.LOAD_DATA_RETRY_DELAY_MILLIS; /** * Stores some configurations for Distro protocol. @@ -42,51 +35,6 @@ import static com.alibaba.nacos.naming.constants.Constants.LOAD_DATA_RETRY_DELAY @Component public class GlobalConfig { - @PostConstruct - public void printGlobalConfig() { - overrideDistroConfiguration(); - } - - private void overrideDistroConfiguration() { - Loggers.SRV_LOG.warn("naming.distro config will be removed, please use core.protocol.distro replace."); - Loggers.SRV_LOG.warn("Using naming.distro config to replace core.distro config"); - DistroConfig.getInstance().setSyncDelayMillis(getTaskDispatchPeriod()); - DistroConfig.getInstance().setSyncRetryDelayMillis(getSyncRetryDelay()); - DistroConfig.getInstance().setLoadDataRetryDelayMillis(getLoadDataRetryDelayMillis()); - } - - /** - * Get distro task dispatch period. - * - * @return period - * @deprecated Will remove on v2.1.X, see {@link DistroConfig#getSyncDelayMillis()}. - */ - @Deprecated - public int getTaskDispatchPeriod() { - return EnvUtil.getProperty(DISTRO_TASK_DISPATCH_PERIOD, Integer.class, 2000); - } - - /** - * Get batch count for sync key. - * - * @return batch count - * @deprecated will remove on v2.1.X. - */ - @Deprecated - public int getBatchSyncKeyCount() { - return EnvUtil.getProperty(DISTRO_BATCH_SYNC_KEY_COUNT, Integer.class, 1000); - } - - /** - * Get distro task sync retry delay after sync fail. - * - * @return distro task sync retry delay - * @deprecated will remove on v2.1.X, see {@link DistroConfig#getSyncRetryDelayMillis()}. - */ - public long getSyncRetryDelay() { - return EnvUtil.getProperty(DISTRO_SYNC_RETRY_DELAY, Long.class, 5000L); - } - public boolean isDataWarmup() { return EnvUtil.getProperty(DATA_WARMUP, Boolean.class, false); } @@ -95,16 +43,6 @@ public class GlobalConfig { return EnvUtil.getProperty(EXPIRE_INSTANCE, Boolean.class, true); } - /** - * Get Distro load data retry delay after load fail. - * - * @return load retry delay time - * @deprecated will remove on v2.1.X, see {@link DistroConfig#getLoadDataRetryDelayMillis()} - */ - public long getLoadDataRetryDelayMillis() { - return EnvUtil.getProperty(LOAD_DATA_RETRY_DELAY_MILLIS, Long.class, 60000L); - } - public static Long getEmptyServiceCleanInterval() { return EnvUtil.getProperty(EMPTY_SERVICE_CLEAN_INTERVAL, Long.class, 60000L); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalExecutor.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalExecutor.java index ddecf03ca..c23cb07bf 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalExecutor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalExecutor.java @@ -39,14 +39,6 @@ import java.util.concurrent.TimeUnit; @SuppressWarnings({"checkstyle:indentation", "PMD.ThreadPoolCreationRule"}) public class GlobalExecutor { - public static final long HEARTBEAT_INTERVAL_MS = TimeUnit.SECONDS.toMillis(5L); - - public static final long LEADER_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(15L); - - public static final long RANDOM_MS = TimeUnit.SECONDS.toMillis(5L); - - public static final long TICK_PERIOD_MS = TimeUnit.MILLISECONDS.toMillis(500L); - private static final long SERVER_STATUS_UPDATE_PERIOD = TimeUnit.SECONDS.toMillis(5); public static final int DEFAULT_THREAD_COUNT = EnvUtil.getAvailableProcessors(0.5); @@ -55,58 +47,6 @@ public class GlobalExecutor { .newScheduledExecutorService(ClassUtils.getCanonicalName(NamingApp.class), EnvUtil.getAvailableProcessors(2), new NameThreadFactory("com.alibaba.nacos.naming.timer")); - private static final ScheduledExecutorService SERVER_STATUS_EXECUTOR = ExecutorFactory.Managed - .newSingleScheduledExecutorService(ClassUtils.getCanonicalName(NamingApp.class), - new NameThreadFactory("com.alibaba.nacos.naming.status.worker")); - - /** - * Service synchronization executor. - * - * @deprecated will remove in v2.1.x. - */ - @Deprecated - private static final ScheduledExecutorService SERVICE_SYNCHRONIZATION_EXECUTOR = ExecutorFactory.Managed - .newSingleScheduledExecutorService(ClassUtils.getCanonicalName(NamingApp.class), - new NameThreadFactory("com.alibaba.nacos.naming.service.worker")); - - /** - * Service update manager executor. - * - * @deprecated will remove in v2.1.x. - */ - @Deprecated - public static final ScheduledExecutorService SERVICE_UPDATE_MANAGER_EXECUTOR = ExecutorFactory.Managed - .newSingleScheduledExecutorService(ClassUtils.getCanonicalName(NamingApp.class), - new NameThreadFactory("com.alibaba.nacos.naming.service.update.processor")); - - /** - * thread pool that processes getting service detail from other server asynchronously. - * - * @deprecated will remove in v2.1.x. - */ - @Deprecated - private static final ExecutorService SERVICE_UPDATE_EXECUTOR = ExecutorFactory.Managed - .newFixedExecutorService(ClassUtils.getCanonicalName(NamingApp.class), 2, - new NameThreadFactory("com.alibaba.nacos.naming.service.update.http.handler")); - - /** - * Empty service auto clean executor. - * - * @deprecated will remove in v2.1.x. - */ - @Deprecated - private static final ScheduledExecutorService EMPTY_SERVICE_AUTO_CLEAN_EXECUTOR = ExecutorFactory.Managed - .newSingleScheduledExecutorService(ClassUtils.getCanonicalName(NamingApp.class), - new NameThreadFactory("com.alibaba.nacos.naming.service.empty.auto-clean")); - - private static final ScheduledExecutorService DISTRO_NOTIFY_EXECUTOR = ExecutorFactory.Managed - .newSingleScheduledExecutorService(ClassUtils.getCanonicalName(NamingApp.class), - new NameThreadFactory("com.alibaba.nacos.naming.distro.notifier")); - - private static final ScheduledExecutorService NAMING_HEALTH_CHECK_EXECUTOR = ExecutorFactory.Managed - .newSingleScheduledExecutorService(ClassUtils.getCanonicalName(NamingApp.class), - new NameThreadFactory("com.alibaba.nacos.naming.health-check.notifier")); - private static final ExecutorService MYSQL_CHECK_EXECUTOR = ExecutorFactory.Managed .newFixedExecutorService(ClassUtils.getCanonicalName(NamingApp.class), DEFAULT_THREAD_COUNT, new NameThreadFactory("com.alibaba.nacos.naming.mysql.checker")); @@ -143,106 +83,10 @@ public class GlobalExecutor { private static final ExecutorService PUSH_CALLBACK_EXECUTOR = ExecutorFactory.Managed .newSingleExecutorService("Push", new NameThreadFactory("com.alibaba.nacos.naming.push.callback")); - /** - * Register raft leader election executor. - * - * @param runnable leader election executor - * @return future - * @deprecated will removed with old raft - */ - @Deprecated - public static ScheduledFuture registerMasterElection(Runnable runnable) { - return NAMING_TIMER_EXECUTOR.scheduleAtFixedRate(runnable, 0, TICK_PERIOD_MS, TimeUnit.MILLISECONDS); - } - - public static void registerServerInfoUpdater(Runnable runnable) { - NAMING_TIMER_EXECUTOR.scheduleAtFixedRate(runnable, 0, 2, TimeUnit.SECONDS); - } - - public static void registerServerStatusReporter(Runnable runnable, long delay) { - SERVER_STATUS_EXECUTOR.schedule(runnable, delay, TimeUnit.MILLISECONDS); - } - public static void registerServerStatusUpdater(Runnable runnable) { NAMING_TIMER_EXECUTOR.scheduleAtFixedRate(runnable, 0, SERVER_STATUS_UPDATE_PERIOD, TimeUnit.MILLISECONDS); } - /** - * Register raft heart beat executor. - * - * @param runnable heart beat executor - * @return future - * @deprecated will removed with old raft - */ - @Deprecated - public static ScheduledFuture registerHeartbeat(Runnable runnable) { - return NAMING_TIMER_EXECUTOR.scheduleWithFixedDelay(runnable, 0, TICK_PERIOD_MS, TimeUnit.MILLISECONDS); - } - - public static void scheduleMcpPushTask(Runnable runnable, long initialDelay, long period) { - NAMING_TIMER_EXECUTOR.scheduleAtFixedRate(runnable, initialDelay, period, TimeUnit.MILLISECONDS); - } - - public static ScheduledFuture submitClusterVersionJudge(Runnable runnable, long delay) { - return NAMING_TIMER_EXECUTOR.schedule(runnable, delay, TimeUnit.MILLISECONDS); - } - - public static void submitDistroNotifyTask(Runnable runnable) { - DISTRO_NOTIFY_EXECUTOR.submit(runnable); - } - - /** - * Submit service update for v1.x. - * - * @param runnable runnable - * @deprecated will remove in v2.1.x. - */ - @Deprecated - public static void submitServiceUpdate(Runnable runnable) { - SERVICE_UPDATE_EXECUTOR.execute(runnable); - } - - /** - * Schedule empty service auto clean for v1.x. - * - * @param runnable runnable - * @param initialDelay initial delay milliseconds - * @param period period between twice clean - * @deprecated will remove in v2.1.x. - */ - @Deprecated - public static void scheduleServiceAutoClean(Runnable runnable, long initialDelay, long period) { - EMPTY_SERVICE_AUTO_CLEAN_EXECUTOR.scheduleAtFixedRate(runnable, initialDelay, period, TimeUnit.MILLISECONDS); - } - - /** - * submitServiceUpdateManager. - * - * @param runnable runnable - * @deprecated will remove in v2.1.x. - */ - @Deprecated - public static void submitServiceUpdateManager(Runnable runnable) { - SERVICE_UPDATE_MANAGER_EXECUTOR.submit(runnable); - } - - /** - * scheduleServiceReporter. - * - * @param command command - * @param delay delay - * @param unit time unit - * @deprecated will remove in v2.1.x. - */ - @Deprecated - public static void scheduleServiceReporter(Runnable command, long delay, TimeUnit unit) { - SERVICE_SYNCHRONIZATION_EXECUTOR.schedule(command, delay, unit); - } - - public static void scheduleNamingHealthCheck(Runnable command, long delay, TimeUnit unit) { - NAMING_HEALTH_CHECK_EXECUTOR.schedule(command, delay, unit); - } - public static void executeMysqlCheckTask(Runnable runnable) { MYSQL_CHECK_EXECUTOR.execute(runnable); } @@ -273,10 +117,6 @@ public class GlobalExecutor { return NAMING_HEALTH_EXECUTOR.scheduleWithFixedDelay(command, initialDelay, delay, unit); } - public static void scheduleRetransmitter(Runnable runnable, long initialDelay, long delay, TimeUnit unit) { - RETRANSMITTER_EXECUTOR.scheduleWithFixedDelay(runnable, initialDelay, delay, unit); - } - public static void scheduleRetransmitter(Runnable runnable, long delay, TimeUnit unit) { RETRANSMITTER_EXECUTOR.schedule(runnable, delay, unit); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/NamingProxy.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/NamingProxy.java deleted file mode 100644 index 83a23538a..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/NamingProxy.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * 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.nacos.naming.misc; - -import com.alibaba.nacos.common.constant.HttpHeaderConsts; -import com.alibaba.nacos.common.http.Callback; -import com.alibaba.nacos.common.model.RestResult; -import com.alibaba.nacos.common.utils.InternetAddressUtil; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.common.utils.VersionUtils; -import com.alibaba.nacos.sys.env.EnvUtil; -import com.alibaba.nacos.common.utils.StringUtils; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; - -/** - * Naming http proxy. - * - * @author nacos - */ -public class NamingProxy { - - private static final String DATA_ON_SYNC_URL = "/distro/datum"; - - private static final String DATA_GET_URL = "/distro/datum"; - - private static final String ALL_DATA_GET_URL = "/distro/datums"; - - private static final String TIMESTAMP_SYNC_URL = "/distro/checksum"; - - /** - * Synchronize check sums. - * - * @param checksumMap checksum map - * @param server server address - */ - public static void syncCheckSums(Map checksumMap, String server) { - syncCheckSums(JacksonUtils.toJsonBytes(checksumMap), server); - } - - /** - * Synchronize check sums. - * - * @param checksums checksum map bytes - * @param server server address - */ - public static void syncCheckSums(byte[] checksums, String server) { - try { - Map headers = new HashMap<>(128); - - headers.put(HttpHeaderConsts.CLIENT_VERSION_HEADER, VersionUtils.version); - headers.put(HttpHeaderConsts.USER_AGENT_HEADER, UtilsAndCommons.SERVER_VERSION); - headers.put(HttpHeaderConsts.CONNECTION, "Keep-Alive"); - - HttpClient.asyncHttpPutLarge( - HTTP_PREFIX + server + EnvUtil.getContextPath() + UtilsAndCommons.NACOS_NAMING_CONTEXT - + TIMESTAMP_SYNC_URL + "?source=" + NetUtils.localServer(), headers, checksums, - new Callback() { - @Override - public void onReceive(RestResult result) { - if (!result.ok()) { - Loggers.DISTRO.error("failed to req API: {}, code: {}, msg: {}", - HTTP_PREFIX + server + EnvUtil.getContextPath() - + UtilsAndCommons.NACOS_NAMING_CONTEXT + TIMESTAMP_SYNC_URL, - result.getCode(), result.getMessage()); - } - } - - @Override - public void onError(Throwable throwable) { - Loggers.DISTRO.error("failed to req API:" + HTTP_PREFIX + server + EnvUtil.getContextPath() - + UtilsAndCommons.NACOS_NAMING_CONTEXT + TIMESTAMP_SYNC_URL, throwable); - } - - @Override - public void onCancel() { - - } - }); - } catch (Exception e) { - Loggers.DISTRO.warn("NamingProxy", e); - } - } - - /** - * Get Data from other server. - * - * @param keys keys of datum - * @param server target server address - * @return datum byte array - * @throws Exception exception - */ - public static byte[] getData(List keys, String server) throws Exception { - - Map params = new HashMap<>(8); - params.put("keys", StringUtils.join(keys, ",")); - RestResult result = HttpClient.httpGetLarge( - HTTP_PREFIX + server + EnvUtil.getContextPath() + UtilsAndCommons.NACOS_NAMING_CONTEXT + DATA_GET_URL, - new HashMap<>(8), JacksonUtils.toJson(params)); - - if (result.ok()) { - return result.getData().getBytes(); - } - - throw new IOException("failed to req API: " + HTTP_PREFIX + server + EnvUtil.getContextPath() - + UtilsAndCommons.NACOS_NAMING_CONTEXT + DATA_GET_URL + ". code: " + result.getCode() + " msg: " - + result.getMessage()); - } - - /** - * Get all datum from target server. - * - * @param server target server address - * @return all datum byte array - * @throws Exception exception - */ - public static byte[] getAllData(String server) throws Exception { - - Map params = new HashMap<>(8); - RestResult result = HttpClient.httpGet( - HTTP_PREFIX + server + EnvUtil.getContextPath() + UtilsAndCommons.NACOS_NAMING_CONTEXT + ALL_DATA_GET_URL, - new ArrayList<>(), params); - - if (result.ok()) { - return result.getData().getBytes(); - } - - throw new IOException("failed to req API: " + HTTP_PREFIX + server + EnvUtil.getContextPath() - + UtilsAndCommons.NACOS_NAMING_CONTEXT + ALL_DATA_GET_URL + ". code: " + result.getCode() + " msg: " - + result.getMessage()); - } - - /** - * Synchronize datum to target server. - * - * @param data datum - * @param curServer target server address - * @return true if sync successfully, otherwise false - */ - public static boolean syncData(byte[] data, String curServer) { - Map headers = new HashMap<>(128); - - headers.put(HttpHeaderConsts.CLIENT_VERSION_HEADER, VersionUtils.version); - headers.put(HttpHeaderConsts.USER_AGENT_HEADER, UtilsAndCommons.SERVER_VERSION); - headers.put(HttpHeaderConsts.ACCEPT_ENCODING, "gzip,deflate,sdch"); - headers.put(HttpHeaderConsts.CONNECTION, "Keep-Alive"); - headers.put(HttpHeaderConsts.CONTENT_ENCODING, "gzip"); - - try { - RestResult result = HttpClient.httpPutLarge( - HTTP_PREFIX + curServer + EnvUtil.getContextPath() + UtilsAndCommons.NACOS_NAMING_CONTEXT - + DATA_ON_SYNC_URL, headers, data); - if (result.ok()) { - return true; - } - if (HttpURLConnection.HTTP_NOT_MODIFIED == result.getCode()) { - return true; - } - throw new IOException("failed to req API:" + HTTP_PREFIX + curServer + EnvUtil.getContextPath() - + UtilsAndCommons.NACOS_NAMING_CONTEXT + DATA_ON_SYNC_URL + ". code:" + result.getCode() + " msg: " - + result.getData()); - } catch (Exception e) { - Loggers.SRV_LOG.warn("NamingProxy", e); - } - return false; - } - - /** - * request api. - * - * @param api api path - * @param params parameters of api - * @param curServer target server address - * @return content if request successfully and response has content, otherwise {@link StringUtils#EMPTY} - * @throws Exception exception - */ - public static String reqApi(String api, Map params, String curServer) throws Exception { - try { - List headers = Arrays.asList(HttpHeaderConsts.CLIENT_VERSION_HEADER, VersionUtils.version, - HttpHeaderConsts.USER_AGENT_HEADER, UtilsAndCommons.SERVER_VERSION, "Accept-Encoding", - "gzip,deflate,sdch", "Connection", "Keep-Alive", "Content-Encoding", "gzip"); - - RestResult result; - - if (!InternetAddressUtil.containsPort(curServer)) { - curServer = curServer + InternetAddressUtil.IP_PORT_SPLITER + EnvUtil.getPort(); - } - - result = HttpClient.httpGet(HTTP_PREFIX + curServer + api, headers, params); - - if (result.ok()) { - return result.getData(); - } - - if (HttpURLConnection.HTTP_NOT_MODIFIED == result.getCode()) { - return StringUtils.EMPTY; - } - - throw new IOException( - "failed to req API:" + HTTP_PREFIX + curServer + api + ". code:" + result.getCode() + " msg: " - + result.getMessage()); - } catch (Exception e) { - Loggers.SRV_LOG.warn("NamingProxy", e); - } - return StringUtils.EMPTY; - } - - /** - * request api. - * - * @param api api path - * @param params parameters of api - * @param curServer target server address - * @param isPost whether use post method to request - * @return content if request successfully and response has content, otherwise {@link StringUtils#EMPTY} - * @throws Exception exception - */ - public static String reqApi(String api, Map params, String curServer, boolean isPost) - throws Exception { - try { - List headers = Arrays.asList(HttpHeaderConsts.CLIENT_VERSION_HEADER, VersionUtils.version, - HttpHeaderConsts.USER_AGENT_HEADER, UtilsAndCommons.SERVER_VERSION, "Accept-Encoding", - "gzip,deflate,sdch", "Connection", "Keep-Alive", "Content-Encoding", "gzip"); - - RestResult result; - - if (!InternetAddressUtil.containsPort(curServer)) { - curServer = curServer + InternetAddressUtil.IP_PORT_SPLITER + EnvUtil.getPort(); - } - - if (isPost) { - result = HttpClient.httpPost( - HTTP_PREFIX + curServer + EnvUtil.getContextPath() + UtilsAndCommons.NACOS_NAMING_CONTEXT - + "/api/" + api, headers, params); - } else { - result = HttpClient.httpGet( - HTTP_PREFIX + curServer + EnvUtil.getContextPath() + UtilsAndCommons.NACOS_NAMING_CONTEXT - + "/api/" + api, headers, params); - } - - if (result.ok()) { - return result.getData(); - } - - if (HttpURLConnection.HTTP_NOT_MODIFIED == result.getCode()) { - return StringUtils.EMPTY; - } - - throw new IOException("failed to req API:" + HTTP_PREFIX + curServer + EnvUtil.getContextPath() - + UtilsAndCommons.NACOS_NAMING_CONTEXT + "/api/" + api + ". code:" + result.getCode() + " msg: " - + result.getMessage()); - } catch (Exception e) { - Loggers.SRV_LOG.warn("NamingProxy", e); - } - return StringUtils.EMPTY; - } - - /** - * request api with common way. - * - * @param path api path - * @param params parameters - * @param curServer target server address - * @param isPost whether use post method to request - * @return content if request successfully and response has content, otherwise {@link StringUtils#EMPTY} - * @throws Exception exception - */ - public static String reqCommon(String path, Map params, String curServer, boolean isPost) - throws Exception { - try { - List headers = Arrays.asList("Client-Version", UtilsAndCommons.SERVER_VERSION, "User-Agent", - UtilsAndCommons.SERVER_VERSION, "Accept-Encoding", "gzip,deflate,sdch", "Connection", "Keep-Alive", - "Content-Encoding", "gzip"); - - RestResult result; - - if (!InternetAddressUtil.containsPort(curServer)) { - curServer = curServer + InternetAddressUtil.IP_PORT_SPLITER + EnvUtil.getPort(); - } - - if (isPost) { - result = HttpClient.httpPost( - HTTP_PREFIX + curServer + EnvUtil.getContextPath() + UtilsAndCommons.NACOS_NAMING_CONTEXT + path, - headers, params); - } else { - result = HttpClient.httpGet( - HTTP_PREFIX + curServer + EnvUtil.getContextPath() + UtilsAndCommons.NACOS_NAMING_CONTEXT + path, - headers, params); - } - - if (result.ok()) { - return result.getData(); - } - - if (HttpURLConnection.HTTP_NOT_MODIFIED == result.getCode()) { - return StringUtils.EMPTY; - } - - throw new IOException("failed to req API:" + HTTP_PREFIX + curServer + EnvUtil.getContextPath() - + UtilsAndCommons.NACOS_NAMING_CONTEXT + path + ". code:" + result.getCode() + " msg: " + result - .getMessage()); - } catch (Exception e) { - Loggers.SRV_LOG.warn("NamingProxy", e); - } - return StringUtils.EMPTY; - } - - public static class Request { - - private Map params = new HashMap<>(8); - - public static Request newRequest() { - return new Request(); - } - - public Request appendParam(String key, String value) { - params.put(key, value); - return this; - } - - /** - * Transfer to Url string. - * - * @return request url string - */ - public String toUrl() { - StringBuilder sb = new StringBuilder(); - for (Map.Entry entry : params.entrySet()) { - sb.append(entry.getKey()).append('=').append(entry.getValue()).append('&'); - } - return sb.toString(); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/NoneHealthCheckProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/NamingTraceEventInitializer.java similarity index 56% rename from naming/src/main/java/com/alibaba/nacos/naming/healthcheck/NoneHealthCheckProcessor.java rename to naming/src/main/java/com/alibaba/nacos/naming/misc/NamingTraceEventInitializer.java index ee65b45e5..a9fc06fa8 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/NoneHealthCheckProcessor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/misc/NamingTraceEventInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2018 Alibaba Group Holding Ltd. + * Copyright 1999-2021 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. @@ -14,27 +14,24 @@ * limitations under the License. */ -package com.alibaba.nacos.naming.healthcheck; +package com.alibaba.nacos.naming.misc; +import com.alibaba.nacos.common.trace.event.naming.NamingTraceEvent; +import com.alibaba.nacos.core.trace.NacosCombinedTraceSubscriber; import org.springframework.stereotype.Component; +import javax.annotation.PostConstruct; + /** - * Health checker that does nothing. + * Naming Trace event initializer. * - * @author nkorange - * @since 1.0.0 + * @author xiweng.yy */ @Component -public class NoneHealthCheckProcessor implements HealthCheckProcessor { +public class NamingTraceEventInitializer { - public static final String TYPE = "NONE"; - - @Override - public void process(HealthCheckTask task) { - } - - @Override - public String getType() { - return TYPE; + @PostConstruct + public void registerSubscriberForNamingEvent() { + new NacosCombinedTraceSubscriber(NamingTraceEvent.class); } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/NetUtils.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/NetUtils.java deleted file mode 100644 index 513ea10b0..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/NetUtils.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.nacos.naming.misc; - -import com.alibaba.nacos.common.utils.InternetAddressUtil; -import com.alibaba.nacos.sys.env.EnvUtil; -import com.alibaba.nacos.sys.utils.InetUtils; - -/** - * Net Utils. - * - * @author nacos - */ -public class NetUtils { - - /** - * Get local server address. - * - * @return local server address - */ - public static String localServer() { - return InetUtils.getSelfIP() + InternetAddressUtil.IP_PORT_SPLITER + EnvUtil.getPort(); - } - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/ServerStatusSynchronizer.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/ServerStatusSynchronizer.java deleted file mode 100644 index 872d372f4..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/ServerStatusSynchronizer.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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.nacos.naming.misc; - -import com.alibaba.nacos.common.http.Callback; -import com.alibaba.nacos.common.model.RestResult; -import com.alibaba.nacos.common.utils.InternetAddressUtil; -import com.alibaba.nacos.naming.constants.FieldsConstants; -import com.alibaba.nacos.sys.env.EnvUtil; -import org.springframework.util.StringUtils; - -import java.util.HashMap; -import java.util.Map; - -import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; - -/** - * Report local server status to other server. - * - * @author nacos - * @deprecated 1.3.0 This object will be deleted sometime after version 1.3.0 - */ -public class ServerStatusSynchronizer implements Synchronizer { - - @Override - public void send(final String serverIp, Message msg) { - if (StringUtils.isEmpty(serverIp)) { - return; - } - - final Map params = new HashMap(2); - - params.put(FieldsConstants.SERVICE_STATUS, msg.getData()); - - String url = HTTP_PREFIX + serverIp + ":" + EnvUtil.getPort() + EnvUtil.getContextPath() - + UtilsAndCommons.NACOS_NAMING_CONTEXT + "/operator/server/status"; - - if (InternetAddressUtil.containsPort(serverIp)) { - url = HTTP_PREFIX + serverIp + EnvUtil.getContextPath() + UtilsAndCommons.NACOS_NAMING_CONTEXT - + "/operator/server/status"; - } - - try { - HttpClient.asyncHttpGet(url, null, params, new Callback() { - @Override - public void onReceive(RestResult result) { - if (!result.ok()) { - Loggers.SRV_LOG.warn("[STATUS-SYNCHRONIZE] failed to request serverStatus, remote server: {}", - serverIp); - } - } - - @Override - public void onError(Throwable throwable) { - Loggers.SRV_LOG.warn("[STATUS-SYNCHRONIZE] failed to request serverStatus, remote server: {}", serverIp, throwable); - } - - @Override - public void onCancel() { - - } - }); - } catch (Exception e) { - Loggers.SRV_LOG.warn("[STATUS-SYNCHRONIZE] failed to request serverStatus, remote server: {}", serverIp, e); - } - } - - @Override - public Message get(String server, String key) { - return null; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/ServiceStatusSynchronizer.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/ServiceStatusSynchronizer.java deleted file mode 100644 index 454abc785..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/ServiceStatusSynchronizer.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.nacos.naming.misc; - -import com.alibaba.nacos.common.http.Callback; -import com.alibaba.nacos.common.model.RestResult; -import com.alibaba.nacos.common.utils.InternetAddressUtil; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.naming.constants.FieldsConstants; -import com.alibaba.nacos.sys.env.EnvUtil; -import com.alibaba.nacos.common.utils.StringUtils; - -import java.util.HashMap; -import java.util.Map; - -import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; - -/** - * Service status ynchronizer. - * - * @author nacos - */ -public class ServiceStatusSynchronizer implements Synchronizer { - - @Override - public void send(final String serverIp, Message msg) { - if (serverIp == null) { - return; - } - - Map params = new HashMap(10); - - params.put(FieldsConstants.STATUSES, msg.getData()); - params.put(FieldsConstants.CLIENT_IP, NetUtils.localServer()); - - String url = HTTP_PREFIX + serverIp + ":" + EnvUtil.getPort() + EnvUtil.getContextPath() - + UtilsAndCommons.NACOS_NAMING_CONTEXT + "/service/status"; - - if (InternetAddressUtil.containsPort(serverIp)) { - url = HTTP_PREFIX + serverIp + EnvUtil.getContextPath() + UtilsAndCommons.NACOS_NAMING_CONTEXT - + "/service/status"; - } - - try { - HttpClient.asyncHttpPostLarge(url, null, JacksonUtils.toJson(params), new Callback() { - @Override - public void onReceive(RestResult result) { - if (!result.ok()) { - Loggers.SRV_LOG.warn("[STATUS-SYNCHRONIZE] failed to request serviceStatus, remote server: {}", - serverIp); - - } - } - - @Override - public void onError(Throwable throwable) { - Loggers.SRV_LOG.warn("[STATUS-SYNCHRONIZE] failed to request serviceStatus, remote server: " + serverIp, throwable); - } - - @Override - public void onCancel() { - - } - }); - } catch (Exception e) { - Loggers.SRV_LOG.warn("[STATUS-SYNCHRONIZE] failed to request serviceStatus, remote server: " + serverIp, e); - } - - } - - @Override - public Message get(String serverIp, String key) { - if (serverIp == null) { - return null; - } - - Map params = new HashMap<>(1); - String keyStr = "key"; - params.put(keyStr, key); - - String result; - try { - if (Loggers.SRV_LOG.isDebugEnabled()) { - Loggers.SRV_LOG.debug("[STATUS-SYNCHRONIZE] sync service status from: {}, service: {}", serverIp, key); - } - result = NamingProxy - .reqApi(EnvUtil.getContextPath() + UtilsAndCommons.NACOS_NAMING_CONTEXT + "/instance/" - + "statuses", params, serverIp); - } catch (Exception e) { - Loggers.SRV_LOG.warn("[STATUS-SYNCHRONIZE] Failed to get service status from " + serverIp, e); - return null; - } - - if (result == null || result.equals(StringUtils.EMPTY)) { - return null; - } - - Message msg = new Message(); - msg.setData(result); - - return msg; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/SwitchDomain.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/SwitchDomain.java index 452871bbb..71bf9b110 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/SwitchDomain.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/misc/SwitchDomain.java @@ -80,8 +80,6 @@ public class SwitchDomain implements Record, Cloneable { private boolean lightBeatEnabled = true; - private boolean doubleWriteEnabled = true; - private Map limitedUrlMap = new HashMap<>(); /** @@ -391,14 +389,6 @@ public class SwitchDomain implements Record, Cloneable { this.lightBeatEnabled = lightBeatEnabled; } - public boolean isDoubleWriteEnabled() { - return doubleWriteEnabled; - } - - public void setDoubleWriteEnabled(boolean doubleWriteEnabled) { - this.doubleWriteEnabled = doubleWriteEnabled; - } - @Override public String toString() { return JacksonUtils.toJson(this); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/SwitchManager.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/SwitchManager.java index dd4d8c883..ce50b7211 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/SwitchManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/misc/SwitchManager.java @@ -20,12 +20,11 @@ import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.ConvertUtils; import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.naming.consistency.ConsistencyService; import com.alibaba.nacos.naming.consistency.Datum; import com.alibaba.nacos.naming.consistency.KeyBuilder; import com.alibaba.nacos.naming.consistency.RecordListener; -import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -49,7 +48,7 @@ public class SwitchManager implements RecordListener { @Autowired private SwitchDomain switchDomain; - @Resource(name = "consistencyDelegate") + @Resource(name = "persistentConsistencyServiceDelegate") private ConsistencyService consistencyService; ReentrantLock lock = new ReentrantLock(); @@ -289,14 +288,6 @@ public class SwitchManager implements RecordListener { switchDomain.setAutoChangeHealthCheckEnabled(ConvertUtils.toBoolean(value)); } - if (entry.equals(SwitchEntry.DOUBLE_WRITE_ENABLED)) { - if (!EnvUtil.isSupportUpgradeFrom1X()) { - throw new IllegalAccessException("Upgrade from 1X feature has closed, " - + "please set `nacos.core.support.upgrade.from.1x=true` in application.properties"); - } - switchDomain.setDoubleWriteEnabled(ConvertUtils.toBoolean(value)); - } - if (debug) { update(switchDomain); } else { @@ -347,7 +338,6 @@ public class SwitchManager implements RecordListener { switchDomain.setOverriddenServerStatus(newSwitchDomain.getOverriddenServerStatus()); switchDomain.setDefaultInstanceEphemeral(newSwitchDomain.isDefaultInstanceEphemeral()); switchDomain.setLightBeatEnabled(newSwitchDomain.isLightBeatEnabled()); - switchDomain.setDoubleWriteEnabled(newSwitchDomain.isDoubleWriteEnabled()); } public SwitchDomain getSwitchDomain() { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/Synchronizer.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/Synchronizer.java deleted file mode 100644 index ed3951376..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/Synchronizer.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.nacos.naming.misc; - -/** - * Synchronizer. - * - * @author nacos - */ -public interface Synchronizer { - - /** - * Send message to server. - * - * @param serverIp target server address - * @param msg message to send - */ - void send(String serverIp, Message msg); - - /** - * Get message from server using message key. - * - * @param serverIp source server address - * @param key message key - * @return message - */ - Message get(String serverIp, String key); -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/UtilsAndCommons.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/UtilsAndCommons.java index 792e2b45b..5573c2a8b 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/UtilsAndCommons.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/misc/UtilsAndCommons.java @@ -17,14 +17,17 @@ package com.alibaba.nacos.naming.misc; import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.selector.SelectorType; import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.VersionUtils; import com.alibaba.nacos.naming.selector.LabelSelector; import com.alibaba.nacos.naming.selector.NoneSelector; import com.alibaba.nacos.sys.env.EnvUtil; import com.fasterxml.jackson.core.type.TypeReference; -import com.alibaba.nacos.common.utils.StringUtils; +import org.springframework.http.HttpStatus; import java.io.File; import java.util.HashMap; @@ -63,9 +66,7 @@ public class UtilsAndCommons { public static final String NACOS_NAMING_HEALTH_CONTEXT = "/health"; - public static final String NACOS_NAMING_RAFT_CONTEXT = "/raft"; - - public static final String NACOS_NAMING_PARTITION_CONTEXT = "/distro"; + public static final String NACOS_NAMING_CLIENT_CONTEXT = "/client"; public static final String NACOS_NAMING_OPERATOR_CONTEXT = "/operator"; @@ -75,12 +76,6 @@ public class UtilsAndCommons { public static final String NACOS_VERSION = VersionUtils.version; - public static final String SUPER_TOKEN = "xy"; - - public static final String DOMAINS_DATA_ID_PRE = "com.alibaba.nacos.naming.domains.meta."; - - public static final String IPADDRESS_DATA_ID_PRE = "com.alibaba.nacos.naming.iplist."; - public static final String SWITCH_DOMAIN_NAME = "00-00---000-NACOS_SWITCH_DOMAIN-000---00-00"; public static final String CIDR_REGEX = "[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}/[0-9]+"; @@ -91,8 +86,6 @@ public class UtilsAndCommons { public static final String LOCALHOST_SITE = UtilsAndCommons.UNKNOWN_SITE; - public static final int RAFT_PUBLISH_TIMEOUT = 5000; - public static final String SERVER_VERSION = NACOS_SERVER_HEADER + ":" + NACOS_VERSION; public static final String SELF_SERVICE_CLUSTER_ENV = "naming_self_service_cluster_ips"; @@ -178,7 +171,8 @@ public class UtilsAndCommons { for (String data : datas) { String[] kv = data.split("="); if (kv.length != 2) { - throw new NacosException(NacosException.INVALID_PARAM, "metadata format incorrect:" + metadata); + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.INSTANCE_METADATA_ERROR, + "metadata format incorrect:" + metadata); } metadataMap.put(kv[0], kv[1]); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/model/form/InstanceForm.java b/naming/src/main/java/com/alibaba/nacos/naming/model/form/InstanceForm.java new file mode 100644 index 000000000..09c3cba40 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/model/form/InstanceForm.java @@ -0,0 +1,227 @@ +/* + * Copyright 1999-2022 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.nacos.naming.model.form; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import org.springframework.http.HttpStatus; + +import java.io.Serializable; +import java.util.Objects; + +/** + * InstanceForm. + * @author dongyafei + * @date 2022/9/7 + */ +public class InstanceForm implements Serializable { + + private static final long serialVersionUID = -3760300561436525429L; + + private String namespaceId; + + private String groupName; + + private String serviceName; + + private String ip; + + private String clusterName; + + private Integer port; + + private Boolean healthy; + + private Double weight; + + private Boolean enabled; + + private String metadata; + + private Boolean ephemeral; + + public InstanceForm() { + } + + /** + * check param. + * + * @throws NacosApiException NacosApiException + */ + public void validate() throws NacosApiException { + fillDefaultValue(); + if (StringUtils.isBlank(serviceName)) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'serviceName' type String is not present"); + } + if (StringUtils.isBlank(ip)) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'ip' type String is not present"); + } + if (port == null) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'port' type Integer is not present"); + } + } + + /** + * fill default value. + */ + public void fillDefaultValue() { + if (StringUtils.isBlank(namespaceId)) { + namespaceId = Constants.DEFAULT_NAMESPACE_ID; + } + if (StringUtils.isBlank(groupName)) { + groupName = Constants.DEFAULT_GROUP; + } + if (StringUtils.isBlank(clusterName)) { + clusterName = UtilsAndCommons.DEFAULT_CLUSTER_NAME; + } + if (healthy == null) { + healthy = true; + } + if (weight == null) { + weight = 1.0; + } + if (enabled == null) { + enabled = true; + } + } + + public String getNamespaceId() { + return namespaceId; + } + + public void setNamespaceId(String namespaceId) { + this.namespaceId = namespaceId; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getClusterName() { + return clusterName; + } + + public void setClusterName(String clusterName) { + this.clusterName = clusterName; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public Boolean getHealthy() { + return healthy; + } + + public void setHealthy(Boolean healthy) { + this.healthy = healthy; + } + + public Double getWeight() { + return weight; + } + + public void setWeight(Double weight) { + this.weight = weight; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public String getMetadata() { + return metadata; + } + + public void setMetadata(String metadata) { + this.metadata = metadata; + } + + public Boolean getEphemeral() { + return ephemeral; + } + + public void setEphemeral(Boolean ephemeral) { + this.ephemeral = ephemeral; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InstanceForm that = (InstanceForm) o; + return Objects.equals(namespaceId, that.namespaceId) && Objects.equals(groupName, that.groupName) && Objects + .equals(serviceName, that.serviceName) && Objects.equals(ip, that.ip) && Objects + .equals(clusterName, that.clusterName) && Objects.equals(port, that.port) && Objects + .equals(healthy, that.healthy) && Objects.equals(weight, that.weight) && Objects + .equals(enabled, that.enabled) && Objects.equals(metadata, that.metadata) && Objects + .equals(ephemeral, that.ephemeral); + } + + @Override + public int hashCode() { + return Objects + .hash(namespaceId, groupName, serviceName, ip, clusterName, port, healthy, weight, enabled, metadata, + ephemeral); + } + + @Override + public String toString() { + return "InstanceForm{" + "namespaceId='" + namespaceId + '\'' + ", groupName='" + groupName + '\'' + + ", serviceName='" + serviceName + '\'' + ", ip='" + ip + '\'' + ", clusterName='" + clusterName + '\'' + + ", port=" + port + ", healthy=" + healthy + ", weight=" + weight + ", enabled=" + enabled + + ", metadata='" + metadata + '\'' + ", ephemeral=" + ephemeral + '}'; + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/model/form/InstanceMetadataBatchOperationForm.java b/naming/src/main/java/com/alibaba/nacos/naming/model/form/InstanceMetadataBatchOperationForm.java new file mode 100644 index 000000000..d1e9d1fc8 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/model/form/InstanceMetadataBatchOperationForm.java @@ -0,0 +1,160 @@ +/* + * Copyright 1999-2022 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.nacos.naming.model.form; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.common.utils.StringUtils; +import org.springframework.http.HttpStatus; + +import java.io.Serializable; +import java.util.Objects; + +/** + * InstanceMetadataBatchOperationForm. + * @author dongyafei + * @date 2022/9/7 + */ +public class InstanceMetadataBatchOperationForm implements Serializable { + + private static final long serialVersionUID = -1183494730406348717L; + + private String namespaceId; + + private String groupName; + + private String serviceName; + + private String consistencyType; + + private String instances; + + private String metadata; + + public InstanceMetadataBatchOperationForm() { + } + + /** + * check param. + * + * @throws NacosApiException NacosApiException + */ + public void validate() throws NacosApiException { + fillDefaultValue(); + if (StringUtils.isBlank(serviceName)) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'serviceName' type String is not present"); + } + if (StringUtils.isBlank(metadata)) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'metadata' type String is not present"); + } + } + + /** + * fill default value. + */ + public void fillDefaultValue() { + if (StringUtils.isBlank(namespaceId)) { + namespaceId = Constants.DEFAULT_NAMESPACE_ID; + } + if (StringUtils.isBlank(groupName)) { + groupName = Constants.DEFAULT_GROUP; + } + if (StringUtils.isBlank(consistencyType)) { + consistencyType = ""; + } + if (StringUtils.isBlank(instances)) { + instances = ""; + } + } + + public String getNamespaceId() { + return namespaceId; + } + + public void setNamespaceId(String namespaceId) { + this.namespaceId = namespaceId; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getConsistencyType() { + return consistencyType; + } + + public void setConsistencyType(String consistencyType) { + this.consistencyType = consistencyType; + } + + public String getInstances() { + return instances; + } + + public void setInstances(String instances) { + this.instances = instances; + } + + public String getMetadata() { + return metadata; + } + + public void setMetadata(String metadata) { + this.metadata = metadata; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InstanceMetadataBatchOperationForm that = (InstanceMetadataBatchOperationForm) o; + return Objects.equals(namespaceId, that.namespaceId) && Objects.equals(groupName, that.groupName) && Objects + .equals(serviceName, that.serviceName) && Objects.equals(consistencyType, that.consistencyType) + && Objects.equals(instances, that.instances) && Objects.equals(metadata, that.metadata); + } + + @Override + public int hashCode() { + return Objects.hash(namespaceId, groupName, serviceName, consistencyType, instances, metadata); + } + + @Override + public String toString() { + return "InstanceMetadataBatchOperationForm{" + "namespaceId='" + namespaceId + '\'' + ", groupName='" + + groupName + '\'' + ", serviceName='" + serviceName + '\'' + ", consistencyType='" + consistencyType + + '\'' + ", instances='" + instances + '\'' + ", metadata='" + metadata + '\'' + '}'; + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/model/form/ServiceForm.java b/naming/src/main/java/com/alibaba/nacos/naming/model/form/ServiceForm.java new file mode 100644 index 000000000..e3895cb4c --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/model/form/ServiceForm.java @@ -0,0 +1,173 @@ +/* + * Copyright 1999-2022 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.nacos.naming.model.form; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.common.utils.StringUtils; +import org.springframework.http.HttpStatus; + +import java.io.Serializable; +import java.util.Objects; + +/** + * ServiceForm. + * @author dongyafei + * @date 2022/9/7 + */ +public class ServiceForm implements Serializable { + + private static final long serialVersionUID = -4905650083916616115L; + + private String namespaceId; + + private String serviceName; + + private String groupName; + + private Boolean ephemeral; + + private Float protectThreshold; + + private String metadata; + + private String selector; + + public ServiceForm() { + } + + /** + * check param. + * + * @throws NacosApiException NacosApiException + */ + public void validate() throws NacosApiException { + fillDefaultValue(); + if (StringUtils.isBlank(serviceName)) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'serviceName' type String is not present"); + } + } + + /** + * fill default value. + */ + public void fillDefaultValue() { + if (StringUtils.isBlank(namespaceId)) { + namespaceId = Constants.DEFAULT_NAMESPACE_ID; + } + if (StringUtils.isBlank(groupName)) { + groupName = Constants.DEFAULT_GROUP; + } + if (ephemeral == null) { + ephemeral = false; + } + if (protectThreshold == null) { + protectThreshold = 0.0F; + } + if (StringUtils.isBlank(metadata)) { + metadata = StringUtils.EMPTY; + } + if (StringUtils.isBlank(selector)) { + selector = StringUtils.EMPTY; + } + } + + public String getNamespaceId() { + return namespaceId; + } + + public void setNamespaceId(String namespaceId) { + this.namespaceId = namespaceId; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public Boolean getEphemeral() { + return ephemeral; + } + + public void setEphemeral(Boolean ephemeral) { + this.ephemeral = ephemeral; + } + + public Float getProtectThreshold() { + return protectThreshold; + } + + public void setProtectThreshold(Float protectThreshold) { + this.protectThreshold = protectThreshold; + } + + public String getMetadata() { + return metadata; + } + + public void setMetadata(String metadata) { + this.metadata = metadata; + } + + public String getSelector() { + return selector; + } + + public void setSelector(String selector) { + this.selector = selector; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ServiceForm that = (ServiceForm) o; + return Objects.equals(namespaceId, that.namespaceId) && Objects.equals(serviceName, that.serviceName) && Objects + .equals(groupName, that.groupName) && Objects.equals(ephemeral, that.ephemeral) && Objects + .equals(protectThreshold, that.protectThreshold) && Objects.equals(metadata, that.metadata) && Objects + .equals(selector, that.selector); + } + + @Override + public int hashCode() { + return Objects.hash(namespaceId, serviceName, groupName, ephemeral, protectThreshold, metadata, selector); + } + + @Override + public String toString() { + return "ServiceForm{" + "namespaceId='" + namespaceId + '\'' + ", serviceName='" + serviceName + '\'' + + ", groupName='" + groupName + '\'' + ", ephemeral=" + ephemeral + ", protectThreshold=" + + protectThreshold + ", metadata='" + metadata + '\'' + ", selector='" + selector + '\'' + '}'; + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/model/form/UpdateHealthForm.java b/naming/src/main/java/com/alibaba/nacos/naming/model/form/UpdateHealthForm.java new file mode 100644 index 000000000..dce4c3f3a --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/model/form/UpdateHealthForm.java @@ -0,0 +1,174 @@ +/* + * Copyright 1999-2022 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.nacos.naming.model.form; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import org.springframework.http.HttpStatus; + +import java.util.Objects; + +/** + * UpdateHealthForm. + * @author dongyafei + * @date 2022/9/15 + */ +public class UpdateHealthForm { + + private Boolean healthy; + + private String groupName; + + private String serviceName; + + private String namespaceId; + + private String clusterName; + + private String ip; + + private Integer port; + + public UpdateHealthForm() { + } + + /** + * check param. + * + * @throws NacosApiException NacosApiException + */ + public void validate() throws NacosApiException { + fillDefaultValue(); + if (healthy == null) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'healthy' type Boolean is not present"); + } + if (StringUtils.isBlank(serviceName)) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'serviceName' type String is not present"); + } + if (StringUtils.isBlank(ip)) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'ip' type String is not present"); + } + if (port == null) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'port' type Integer is not present"); + } + } + + /** + * fill default value. + */ + public void fillDefaultValue() { + if (StringUtils.isBlank(namespaceId)) { + namespaceId = Constants.DEFAULT_NAMESPACE_ID; + } + if (StringUtils.isBlank(groupName)) { + groupName = Constants.DEFAULT_GROUP; + } + if (StringUtils.isBlank(clusterName)) { + clusterName = UtilsAndCommons.DEFAULT_CLUSTER_NAME; + } + } + + public Boolean getHealthy() { + return healthy; + } + + public void setHealthy(Boolean healthy) { + this.healthy = healthy; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getNamespaceId() { + return namespaceId; + } + + public void setNamespaceId(String namespaceId) { + this.namespaceId = namespaceId; + } + + public String getClusterName() { + return clusterName; + } + + public void setClusterName(String clusterName) { + this.clusterName = clusterName; + } + + 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; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + UpdateHealthForm that = (UpdateHealthForm) o; + return Objects.equals(healthy, that.healthy) && Objects.equals(groupName, that.groupName) && Objects + .equals(serviceName, that.serviceName) && Objects.equals(namespaceId, that.namespaceId) && Objects + .equals(clusterName, that.clusterName) && Objects.equals(ip, that.ip) && Objects + .equals(port, that.port); + } + + @Override + public int hashCode() { + return Objects.hash(healthy, groupName, serviceName, namespaceId, clusterName, ip, port); + } + + @Override + public String toString() { + return "UpdateHealthForm{" + "healthy=" + healthy + ", groupName='" + groupName + '\'' + ", serviceName='" + + serviceName + '\'' + ", namespaceId='" + namespaceId + '\'' + ", clusterName='" + clusterName + '\'' + + ", ip='" + ip + '\'' + ", port=" + port + '}'; + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/model/form/UpdateSwitchForm.java b/naming/src/main/java/com/alibaba/nacos/naming/model/form/UpdateSwitchForm.java new file mode 100644 index 000000000..d0beca5b2 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/model/form/UpdateSwitchForm.java @@ -0,0 +1,107 @@ +/* + * Copyright 1999-2022 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.nacos.naming.model.form; + +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.common.utils.StringUtils; +import org.springframework.http.HttpStatus; + +import java.io.Serializable; +import java.util.Objects; + +/** + * UpdateSwitchForm. + * @author dongyafei + * @date 2022/9/15 + */ +public class UpdateSwitchForm implements Serializable { + + private static final long serialVersionUID = -1580959130954136990L; + + private Boolean debug; + + private String entry; + + private String value; + + public UpdateSwitchForm() { + } + + /** + * check param. + * + * @throws NacosApiException NacosApiException + */ + public void validate() throws NacosApiException { + if (StringUtils.isBlank(entry)) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'entry' type String is not present"); + } + if (StringUtils.isBlank(value)) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, + "Required parameter 'value' type String is not present"); + } + } + + public Boolean getDebug() { + return debug; + } + + public void setDebug(Boolean debug) { + this.debug = debug; + } + + public String getEntry() { + return entry; + } + + public void setEntry(String entry) { + this.entry = entry; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + UpdateSwitchForm that = (UpdateSwitchForm) o; + return Objects.equals(debug, that.debug) && Objects.equals(entry, that.entry) && Objects + .equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(debug, entry, value); + } + + @Override + public String toString() { + return "UpdateSwitchForm{" + "debug=" + debug + ", entry='" + entry + '\'' + ", value='" + value + '\'' + '}'; + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/model/vo/InstanceDetailInfoVo.java b/naming/src/main/java/com/alibaba/nacos/naming/model/vo/InstanceDetailInfoVo.java new file mode 100644 index 000000000..b9f50a570 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/model/vo/InstanceDetailInfoVo.java @@ -0,0 +1,113 @@ +/* + * Copyright 1999-2022 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.nacos.naming.model.vo; + +import java.io.Serializable; +import java.util.Map; + +/** + * InstanceDetailInfoVo. + * @author dongyafei + * @date 2022/9/7 + */ +public class InstanceDetailInfoVo implements Serializable { + + private static final long serialVersionUID = -8983967044228959560L; + + private String serviceName; + + private String ip; + + private Integer port; + + private String clusterName; + + private Double weight; + + private Boolean healthy; + + private String instanceId; + + private Map metadata; + + public InstanceDetailInfoVo() { + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + 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 getClusterName() { + return clusterName; + } + + public void setClusterName(String clusterName) { + this.clusterName = clusterName; + } + + public Double getWeight() { + return weight; + } + + public void setWeight(Double weight) { + this.weight = weight; + } + + public Boolean getHealthy() { + return healthy; + } + + public void setHealthy(Boolean healthy) { + this.healthy = healthy; + } + + public String getInstanceId() { + return instanceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/model/vo/InstanceMetadataBatchOperationVo.java b/naming/src/main/java/com/alibaba/nacos/naming/model/vo/InstanceMetadataBatchOperationVo.java new file mode 100644 index 000000000..d3d5e0fb5 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/model/vo/InstanceMetadataBatchOperationVo.java @@ -0,0 +1,47 @@ +/* + * Copyright 1999-2022 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.nacos.naming.model.vo; + +import java.io.Serializable; +import java.util.List; + +/** + * InstanceUpdateVo. + * @author dongyafei + * @date 2022/9/7 + */ +public class InstanceMetadataBatchOperationVo implements Serializable { + + private static final long serialVersionUID = -5793871911227035729L; + + private List updated; + + public InstanceMetadataBatchOperationVo() { + } + + public InstanceMetadataBatchOperationVo(List updated) { + this.updated = updated; + } + + public List getUpdated() { + return updated; + } + + public void setUpdated(List updated) { + this.updated = updated; + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/model/vo/MetricsInfoVo.java b/naming/src/main/java/com/alibaba/nacos/naming/model/vo/MetricsInfoVo.java new file mode 100644 index 000000000..112fe256a --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/model/vo/MetricsInfoVo.java @@ -0,0 +1,155 @@ +/* + * Copyright 1999-2022 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.nacos.naming.model.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.io.Serializable; + +/** + * Metrics Information. + * @author dongyafei + * @date 2022/9/15 + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class MetricsInfoVo implements Serializable { + + private static final long serialVersionUID = -5064297490423743871L; + + private String status; + + private Integer serviceCount; + + private Integer instanceCount; + + private Integer subscribeCount; + + private Integer clientCount; + + private Integer connectionBasedClientCount; + + private Integer ephemeralIpPortClientCount; + + private Integer persistentIpPortClientCount; + + private Integer responsibleClientCount; + + private Float cpu; + + private Float load; + + private Float mem; + + public MetricsInfoVo() { + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Integer getServiceCount() { + return serviceCount; + } + + public void setServiceCount(Integer serviceCount) { + this.serviceCount = serviceCount; + } + + public Integer getInstanceCount() { + return instanceCount; + } + + public void setInstanceCount(Integer instanceCount) { + this.instanceCount = instanceCount; + } + + public Integer getSubscribeCount() { + return subscribeCount; + } + + public void setSubscribeCount(Integer subscribeCount) { + this.subscribeCount = subscribeCount; + } + + public Integer getClientCount() { + return clientCount; + } + + public void setClientCount(Integer clientCount) { + this.clientCount = clientCount; + } + + public Integer getConnectionBasedClientCount() { + return connectionBasedClientCount; + } + + public void setConnectionBasedClientCount(Integer connectionBasedClientCount) { + this.connectionBasedClientCount = connectionBasedClientCount; + } + + public Integer getEphemeralIpPortClientCount() { + return ephemeralIpPortClientCount; + } + + public void setEphemeralIpPortClientCount(Integer ephemeralIpPortClientCount) { + this.ephemeralIpPortClientCount = ephemeralIpPortClientCount; + } + + public Integer getPersistentIpPortClientCount() { + return persistentIpPortClientCount; + } + + public void setPersistentIpPortClientCount(Integer persistentIpPortClientCount) { + this.persistentIpPortClientCount = persistentIpPortClientCount; + } + + public Integer getResponsibleClientCount() { + return responsibleClientCount; + } + + public void setResponsibleClientCount(Integer responsibleClientCount) { + this.responsibleClientCount = responsibleClientCount; + } + + public Float getCpu() { + return cpu; + } + + public void setCpu(Float cpu) { + this.cpu = cpu; + } + + public Float getLoad() { + return load; + } + + public void setLoad(Float load) { + this.load = load; + } + + public Float getMem() { + return mem; + } + + public void setMem(Float mem) { + this.mem = mem; + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/monitor/PerformanceLoggerThread.java b/naming/src/main/java/com/alibaba/nacos/naming/monitor/PerformanceLoggerThread.java index 8c12bb2e5..32b8d359b 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/monitor/PerformanceLoggerThread.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/monitor/PerformanceLoggerThread.java @@ -18,16 +18,10 @@ package com.alibaba.nacos.naming.monitor; import com.alibaba.nacos.core.distributed.distro.monitor.DistroRecord; import com.alibaba.nacos.core.distributed.distro.monitor.DistroRecordsHolder; -import com.alibaba.nacos.naming.consistency.KeyBuilder; import com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientDataProcessor; -import com.alibaba.nacos.naming.consistency.persistent.ClusterVersionJudgement; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftCore; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeer; -import com.alibaba.nacos.naming.core.ServiceManager; import com.alibaba.nacos.naming.misc.GlobalExecutor; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.NamingExecuteTaskDispatcher; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -44,15 +38,6 @@ import java.util.concurrent.TimeUnit; @Component public class PerformanceLoggerThread { - @Autowired - private ServiceManager serviceManager; - - @Autowired - private RaftCore raftCore; - - @Autowired - private ClusterVersionJudgement versionJudgement; - private static final long PERIOD = 60; @PostConstruct @@ -80,23 +65,6 @@ public class PerformanceLoggerThread { public void collectMetrics() { MetricsMonitor.getDomCountMonitor().set(com.alibaba.nacos.naming.core.v2.ServiceManager.getInstance().size()); MetricsMonitor.getAvgPushCostMonitor().set(getAvgPushCost()); - metricsRaftLeader(); - } - - /** - * Will deprecated after v1.4.x - */ - @Deprecated - private void metricsRaftLeader() { - if (!versionJudgement.allMemberIsNewVersion()) { - if (raftCore.isLeader()) { - MetricsMonitor.getLeaderStatusMonitor().set(1); - } else if (raftCore.getPeerSet().local().state == RaftPeer.State.FOLLOWER) { - MetricsMonitor.getLeaderStatusMonitor().set(0); - } else { - MetricsMonitor.getLeaderStatusMonitor().set(2); - } - } } class PerformanceLogTask implements Runnable { @@ -136,14 +104,6 @@ public class PerformanceLoggerThread { } private void printDistroMonitor() { - Optional v1Record = DistroRecordsHolder.getInstance() - .getRecordIfExist(KeyBuilder.INSTANCE_LIST_KEY_PREFIX); - long v1SyncDone = 0; - long v1SyncFail = 0; - if (v1Record.isPresent()) { - v1SyncDone = v1Record.get().getSuccessfulSyncCount(); - v1SyncFail = v1Record.get().getFailedSyncCount(); - } Optional v2Record = DistroRecordsHolder.getInstance() .getRecordIfExist(DistroClientDataProcessor.TYPE); long v2SyncDone = 0; @@ -154,8 +114,7 @@ public class PerformanceLoggerThread { v2SyncFail = v2Record.get().getFailedSyncCount(); v2VerifyFail = v2Record.get().getFailedVerifyCount(); } - Loggers.PERFORMANCE_LOG - .info("DISTRO:|{}|{}|{}|{}|{}|", v1SyncDone, v1SyncFail, v2SyncDone, v2SyncFail, v2VerifyFail); + Loggers.PERFORMANCE_LOG.info("DISTRO:|{}|{}|{}|", v2SyncDone, v2SyncFail, v2VerifyFail); } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/pojo/ClusterStateView.java b/naming/src/main/java/com/alibaba/nacos/naming/pojo/ClusterStateView.java deleted file mode 100644 index a7dc28534..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/pojo/ClusterStateView.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.nacos.naming.pojo; - -/** - * Cluster state view. - * - * @author: universefeeler - */ -public class ClusterStateView { - - private String nodeIp; - - private String nodeState; - - private long clusterTerm; - - private long leaderDueMs; - - private String voteFor; - - private long heartbeatDueMs; - - public long getLeaderDueMs() { - return leaderDueMs; - } - - public void setLeaderDueMs(long leaderDueMs) { - this.leaderDueMs = leaderDueMs; - } - - public long getHeartbeatDueMs() { - return heartbeatDueMs; - } - - public void setHeartbeatDueMs(long heartbeatDueMs) { - this.heartbeatDueMs = heartbeatDueMs; - } - - public String getVoteFor() { - return voteFor; - } - - public void setVoteFor(String voteFor) { - this.voteFor = voteFor; - } - - public String getNodeIp() { - return nodeIp; - } - - public void setNodeIp(String nodeIp) { - this.nodeIp = nodeIp; - } - - public String getNodeState() { - return nodeState; - } - - public void setNodeState(String nodeState) { - this.nodeState = nodeState; - } - - public long getClusterTerm() { - return clusterTerm; - } - - public void setClusterTerm(long clusterTerm) { - this.clusterTerm = clusterTerm; - } - - @Override - public String toString() { - return "ClusterStateView{" + "nodeIp='" + nodeIp + '\'' + ", nodeState='" + nodeState + '\'' + ", clusterTerm=" - + clusterTerm + ", leaderDueMs=" + leaderDueMs + ", voteFor='" + voteFor + '\'' + ", heartbeatDueMs=" - + heartbeatDueMs + '}'; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/pojo/InstanceOperationContext.java b/naming/src/main/java/com/alibaba/nacos/naming/pojo/InstanceOperationContext.java deleted file mode 100644 index 7d1ad3b45..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/pojo/InstanceOperationContext.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.nacos.naming.pojo; - -import com.alibaba.nacos.naming.core.Instance; - -import java.util.List; - -/** - * InstanceOperationContext. used in instance batch operation's consumer. - * - * @author horizonzy - * @since 1.4.0 - */ -public class InstanceOperationContext { - - public InstanceOperationContext() { - } - - public InstanceOperationContext(String namespace, String serviceName, Boolean ephemeral, Boolean all) { - this.namespace = namespace; - this.serviceName = serviceName; - this.ephemeral = ephemeral; - this.all = all; - } - - public InstanceOperationContext(String namespace, String serviceName, Boolean ephemeral, Boolean all, - List instances) { - this.namespace = namespace; - this.serviceName = serviceName; - this.ephemeral = ephemeral; - this.all = all; - this.instances = instances; - } - - private String namespace; - - private String serviceName; - - private Boolean ephemeral; - - private Boolean all; - - private List instances; - - public String getNamespace() { - return namespace; - } - - public String getServiceName() { - return serviceName; - } - - public Boolean getEphemeral() { - return ephemeral; - } - - public Boolean getAll() { - return all; - } - - public List getInstances() { - return instances; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/pojo/InstanceOperationInfo.java b/naming/src/main/java/com/alibaba/nacos/naming/pojo/InstanceOperationInfo.java index 06881019a..eeb833b8c 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/pojo/InstanceOperationInfo.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/pojo/InstanceOperationInfo.java @@ -44,10 +44,6 @@ public class InstanceOperationInfo { /** * consistencyType. it helps to operate all instances from consistencyService, value = ephemeral or persist. - *

- * ephemeral = all ephemeral instances in {@link com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl} - * persist = all persist instances in {@link com.alibaba.nacos.naming.consistency.persistent.raft.RaftConsistencyServiceImpl} - *

*/ private String consistencyType; diff --git a/naming/src/main/java/com/alibaba/nacos/naming/pojo/ServiceDetailView.java b/naming/src/main/java/com/alibaba/nacos/naming/pojo/ServiceDetailView.java deleted file mode 100644 index 2b879ad01..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/pojo/ServiceDetailView.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.nacos.naming.pojo; - -import com.alibaba.nacos.api.naming.pojo.Cluster; -import com.alibaba.nacos.api.naming.pojo.Service; - -import java.util.List; - -/** - * Service manager. - * - * @author nkorange - */ -public class ServiceDetailView { - - private Service service; - - private List clusters; - - public Service getService() { - return service; - } - - public void setService(Service service) { - this.service = service; - } - - public List getClusters() { - return clusters; - } - - public void setClusters(List clusters) { - this.clusters = clusters; - } - - @Override - public String toString() { - return "ServiceDetailView{" + "service=" + service + ", clusters=" + clusters + '}'; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v1/ClientInfo.java b/naming/src/main/java/com/alibaba/nacos/naming/push/ClientInfo.java similarity index 97% rename from naming/src/main/java/com/alibaba/nacos/naming/push/v1/ClientInfo.java rename to naming/src/main/java/com/alibaba/nacos/naming/push/ClientInfo.java index d00f879ae..c76396b09 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v1/ClientInfo.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/ClientInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2020 Alibaba Group Holding Ltd. + * Copyright 1999-2021 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.alibaba.nacos.naming.push.v1; +package com.alibaba.nacos.naming.push; import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.alibaba.nacos.common.utils.StringUtils; diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingSubscriberServiceLocalImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingSubscriberServiceLocalImpl.java index f748460c8..37d233c0e 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingSubscriberServiceLocalImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingSubscriberServiceLocalImpl.java @@ -18,7 +18,6 @@ package com.alibaba.nacos.naming.push; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.pojo.Subscriber; -import com.alibaba.nacos.naming.push.v1.NamingSubscriberServiceV1Impl; import com.alibaba.nacos.naming.push.v2.NamingSubscriberServiceV2Impl; import java.util.Collection; @@ -28,51 +27,33 @@ import java.util.HashSet; * Naming subscriber service for local. * * @author xiweng.yy - * @deprecated Will be removed with {@link com.alibaba.nacos.naming.push.v1.NamingSubscriberServiceV1Impl} */ @org.springframework.stereotype.Service -@Deprecated public class NamingSubscriberServiceLocalImpl implements NamingSubscriberService { - private final NamingSubscriberServiceV1Impl namingSubscriberServiceV1; - private final NamingSubscriberServiceV2Impl namingSubscriberServiceV2; - public NamingSubscriberServiceLocalImpl(NamingSubscriberServiceV1Impl namingSubscriberServiceV1, - NamingSubscriberServiceV2Impl namingSubscriberServiceV2) { - this.namingSubscriberServiceV1 = namingSubscriberServiceV1; + public NamingSubscriberServiceLocalImpl(NamingSubscriberServiceV2Impl namingSubscriberServiceV2) { this.namingSubscriberServiceV2 = namingSubscriberServiceV2; } @Override public Collection getSubscribers(String namespaceId, String serviceName) { - Collection result = new HashSet<>(); - result.addAll(namingSubscriberServiceV1.getSubscribers(namespaceId, serviceName)); - result.addAll(namingSubscriberServiceV2.getSubscribers(namespaceId, serviceName)); - return result; + return new HashSet<>(namingSubscriberServiceV2.getSubscribers(namespaceId, serviceName)); } @Override public Collection getSubscribers(Service service) { - Collection result = new HashSet<>(); - result.addAll(namingSubscriberServiceV1.getSubscribers(service)); - result.addAll(namingSubscriberServiceV2.getSubscribers(service)); - return result; + return new HashSet<>(namingSubscriberServiceV2.getSubscribers(service)); } @Override public Collection getFuzzySubscribers(String namespaceId, String serviceName) { - Collection result = new HashSet<>(); - result.addAll(namingSubscriberServiceV1.getFuzzySubscribers(namespaceId, serviceName)); - result.addAll(namingSubscriberServiceV2.getFuzzySubscribers(namespaceId, serviceName)); - return result; + return new HashSet<>(namingSubscriberServiceV2.getFuzzySubscribers(namespaceId, serviceName)); } @Override public Collection getFuzzySubscribers(Service service) { - Collection result = new HashSet<>(); - result.addAll(namingSubscriberServiceV1.getFuzzySubscribers(service)); - result.addAll(namingSubscriberServiceV2.getFuzzySubscribers(service)); - return result; + return new HashSet<>(namingSubscriberServiceV2.getFuzzySubscribers(service)); } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/UdpPushService.java b/naming/src/main/java/com/alibaba/nacos/naming/push/UdpPushService.java index fce15db72..7042c39a8 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/UdpPushService.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/UdpPushService.java @@ -19,47 +19,23 @@ package com.alibaba.nacos.naming.push; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import com.alibaba.nacos.naming.misc.GlobalExecutor; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.naming.monitor.MetricsMonitor; import com.alibaba.nacos.naming.pojo.Subscriber; -import com.alibaba.nacos.naming.push.v1.ClientInfo; -import com.alibaba.nacos.naming.push.v1.NamingSubscriberServiceV1Impl; -import com.alibaba.nacos.naming.push.v1.PushClient; -import com.alibaba.nacos.naming.push.v1.ServiceChangeEvent; import com.alibaba.nacos.naming.remote.udp.AckEntry; -import com.alibaba.nacos.naming.remote.udp.AckPacket; import com.alibaba.nacos.naming.remote.udp.UdpConnector; -import com.alibaba.nacos.naming.constants.Constants; -import com.alibaba.nacos.sys.utils.ApplicationUtils; import org.apache.commons.collections.MapUtils; import org.codehaus.jackson.util.VersionUtil; -import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.DatagramPacket; -import java.net.DatagramSocket; import java.net.InetSocketAddress; -import java.net.SocketException; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; import java.util.zip.GZIPOutputStream; /** @@ -69,125 +45,17 @@ import java.util.zip.GZIPOutputStream; */ @Component @SuppressWarnings("PMD.ThreadPoolCreationRule") -public class UdpPushService implements ApplicationContextAware, ApplicationListener { +public class UdpPushService { @Autowired private SwitchDomain switchDomain; - @Autowired - private NamingSubscriberServiceV1Impl subscriberServiceV1; - - private ApplicationContext applicationContext; - - private static volatile ConcurrentMap ackMap = new ConcurrentHashMap<>(); - - private static volatile ConcurrentMap udpSendTimeMap = new ConcurrentHashMap<>(); - - private static DatagramSocket udpSocket; - private final UdpConnector udpConnector; - private static ConcurrentMap futureMap = new ConcurrentHashMap<>(); - - static { - try { - udpSocket = new DatagramSocket(); - - Receiver receiver = new Receiver(); - - Thread inThread = new Thread(receiver); - inThread.setDaemon(true); - inThread.setName("com.alibaba.nacos.naming.push.receiver"); - inThread.start(); - - } catch (SocketException e) { - Loggers.SRV_LOG.error("[NACOS-PUSH] failed to init push service"); - } - } - public UdpPushService(UdpConnector udpConnector) { this.udpConnector = udpConnector; } - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - } - - @Override - public void onApplicationEvent(ServiceChangeEvent event) { - // If upgrade to 2.0.X, do not push for v1. - if (ApplicationUtils.getBean(UpgradeJudgement.class).isUseGrpcFeatures()) { - return; - } - Service service = event.getService(); - String serviceName = service.getName(); - String namespaceId = service.getNamespaceId(); - //merge some change events to reduce the push frequency: - if (futureMap.containsKey(UtilsAndCommons.assembleFullServiceName(namespaceId, serviceName))) { - return; - } - Future future = GlobalExecutor.scheduleUdpSender(() -> { - try { - Loggers.PUSH.info(serviceName + " is changed, add it to push queue."); - ConcurrentMap clients = subscriberServiceV1.getClientMap() - .get(UtilsAndCommons.assembleFullServiceName(namespaceId, serviceName)); - if (MapUtils.isEmpty(clients)) { - return; - } - - Map cache = new HashMap<>(16); - long lastRefTime = System.nanoTime(); - for (PushClient client : clients.values()) { - if (client.zombie()) { - Loggers.PUSH.debug("client is zombie: " + client); - clients.remove(client.toString()); - Loggers.PUSH.debug("client is zombie: " + client); - continue; - } - - AckEntry ackEntry; - Loggers.PUSH.debug("push serviceName: {} to client: {}", serviceName, client); - String key = getPushCacheKey(serviceName, client.getIp(), client.getAgent()); - byte[] compressData = null; - Map data = null; - if (switchDomain.getDefaultPushCacheMillis() >= 20000 && cache.containsKey(key)) { - org.javatuples.Pair pair = (org.javatuples.Pair) cache.get(key); - compressData = (byte[]) (pair.getValue0()); - data = (Map) pair.getValue1(); - - Loggers.PUSH.debug("[PUSH-CACHE] cache hit: {}:{}", serviceName, client.getAddrStr()); - } - - if (compressData != null) { - ackEntry = prepareAckEntry(client, compressData, data, lastRefTime); - } else { - ackEntry = prepareAckEntry(client, prepareHostsData(client), lastRefTime); - if (ackEntry != null) { - cache.put(key, - new org.javatuples.Pair<>(ackEntry.getOrigin().getData(), ackEntry.getData())); - } - } - - Loggers.PUSH.info("serviceName: {} changed, schedule push for: {}, agent: {}, key: {}", - client.getServiceName(), client.getAddrStr(), client.getAgent(), - (ackEntry == null ? null : ackEntry.getKey())); - - udpPush(ackEntry); - } - } catch (Exception e) { - Loggers.PUSH.error("[NACOS-PUSH] failed to push serviceName: {} to client, error: {}", serviceName, e); - - } finally { - futureMap.remove(UtilsAndCommons.assembleFullServiceName(namespaceId, serviceName)); - } - - }, 1000, TimeUnit.MILLISECONDS); - - futureMap.put(UtilsAndCommons.assembleFullServiceName(namespaceId, serviceName), future); - - } - /** * Push Data without callback. * @@ -233,10 +101,6 @@ public class UdpPushService implements ApplicationContextAware, ApplicationListe return prepareAckEntry(socketAddress, prepareHostsData(JacksonUtils.toJson(serviceInfo)), lastRefTime); } - private static AckEntry prepareAckEntry(PushClient client, Map data, long lastRefTime) { - return prepareAckEntry(client.getSocketAddr(), data, lastRefTime); - } - private static AckEntry prepareAckEntry(InetSocketAddress socketAddress, Map data, long lastRefTime) { if (MapUtils.isEmpty(data)) { @@ -256,11 +120,6 @@ public class UdpPushService implements ApplicationContextAware, ApplicationListe } } - private static AckEntry prepareAckEntry(PushClient client, byte[] dataBytes, Map data, - long lastRefTime) { - return prepareAckEntry(client.getSocketAddr(), dataBytes, data, lastRefTime); - } - private static AckEntry prepareAckEntry(InetSocketAddress socketAddress, byte[] dataBytes, Map data, long lastRefTime) { String key = AckEntry @@ -279,19 +138,6 @@ public class UdpPushService implements ApplicationContextAware, ApplicationListe return null; } - public static String getPushCacheKey(String serviceName, String clientIP, String agent) { - return serviceName + UtilsAndCommons.CACHE_KEY_SPLITTER + agent; - } - - /** - * Service changed. - * - * @param service service - */ - public void serviceChanged(Service service) { - this.applicationContext.publishEvent(new ServiceChangeEvent(this, service)); - } - /** * Judge whether this agent is supported to push. * @@ -326,14 +172,6 @@ public class UdpPushService implements ApplicationContextAware, ApplicationListe return false; } - public static List getFailedPushes() { - return new ArrayList<>(ackMap.values()); - } - - public static void resetPushState() { - ackMap.clear(); - } - private static byte[] compressIfNecessary(byte[] dataBytes) throws IOException { // enable compression when data is larger than 1KB int maxDataSizeUncompress = 1024; @@ -349,121 +187,10 @@ public class UdpPushService implements ApplicationContextAware, ApplicationListe return out.toByteArray(); } - private static Map prepareHostsData(PushClient client) throws Exception { - return prepareHostsData(client.getDataSource().getData(client)); - } - private static Map prepareHostsData(String dataContent) { Map result = new HashMap(2); result.put("type", "dom"); result.put("data", dataContent); return result; } - - private static AckEntry udpPush(AckEntry ackEntry) { - if (ackEntry == null) { - Loggers.PUSH.error("[NACOS-PUSH] ackEntry is null."); - return null; - } - - if (ackEntry.getRetryTimes() > Constants.UDP_MAX_RETRY_TIMES) { - Loggers.PUSH.warn("max re-push times reached, retry times {}, key: {}", ackEntry.getRetryTimes(), - ackEntry.getKey()); - ackMap.remove(ackEntry.getKey()); - udpSendTimeMap.remove(ackEntry.getKey()); - MetricsMonitor.incrementFailPush(); - return ackEntry; - } - - try { - if (!ackMap.containsKey(ackEntry.getKey())) { - MetricsMonitor.incrementPush(); - } - ackMap.put(ackEntry.getKey(), ackEntry); - udpSendTimeMap.put(ackEntry.getKey(), System.currentTimeMillis()); - - Loggers.PUSH.info("send udp packet: " + ackEntry.getKey()); - udpSocket.send(ackEntry.getOrigin()); - - ackEntry.increaseRetryTime(); - - GlobalExecutor.scheduleRetransmitter(new Retransmitter(ackEntry), - TimeUnit.NANOSECONDS.toMillis(Constants.ACK_TIMEOUT_NANOS), TimeUnit.MILLISECONDS); - - return ackEntry; - } catch (Exception e) { - Loggers.PUSH.error("[NACOS-PUSH] failed to push data: {} to client: {}, error: {}", ackEntry.getData(), - ackEntry.getOrigin().getAddress().getHostAddress(), e); - ackMap.remove(ackEntry.getKey()); - udpSendTimeMap.remove(ackEntry.getKey()); - MetricsMonitor.incrementFailPush(); - - return null; - } - } - - public static class Retransmitter implements Runnable { - - AckEntry ackEntry; - - public Retransmitter(AckEntry ackEntry) { - this.ackEntry = ackEntry; - } - - @Override - public void run() { - if (ackMap.containsKey(ackEntry.getKey())) { - Loggers.PUSH.info("retry to push data, key: " + ackEntry.getKey()); - udpPush(ackEntry); - } - } - } - - public static class Receiver implements Runnable { - - @Override - public void run() { - while (true) { - byte[] buffer = new byte[1024 * 64]; - DatagramPacket packet = new DatagramPacket(buffer, buffer.length); - - try { - udpSocket.receive(packet); - - String json = new String(packet.getData(), 0, packet.getLength(), StandardCharsets.UTF_8).trim(); - AckPacket ackPacket = JacksonUtils.toObj(json, AckPacket.class); - - InetSocketAddress socketAddress = (InetSocketAddress) packet.getSocketAddress(); - String ip = socketAddress.getAddress().getHostAddress(); - int port = socketAddress.getPort(); - - if (System.nanoTime() - ackPacket.lastRefTime > Constants.ACK_TIMEOUT_NANOS) { - Loggers.PUSH.warn("ack takes too long from {} ack json: {}", packet.getSocketAddress(), json); - } - - String ackKey = AckEntry.getAckKey(ip, port, ackPacket.lastRefTime); - AckEntry ackEntry = ackMap.remove(ackKey); - if (ackEntry == null) { - throw new IllegalStateException( - "unable to find ackEntry for key: " + ackKey + ", ack json: " + json); - } - - long pushCost = System.currentTimeMillis() - udpSendTimeMap.get(ackKey); - - Loggers.PUSH - .info("received ack: {} from: {}:{}, cost: {} ms, unacked: {}, total push: {}", json, ip, - port, pushCost, ackMap.size(), MetricsMonitor.getTotalPushMonitor().get()); - - MetricsMonitor.incrementPushCost(pushCost); - - udpSendTimeMap.remove(ackKey); - - } catch (Throwable e) { - Loggers.PUSH.error("[NACOS-PUSH] error while receiving ack data", e); - } - } - } - - } - } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v1/DataSource.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v1/DataSource.java deleted file mode 100644 index 7f3f55fed..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v1/DataSource.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.push.v1; - -/** - * Data source for naming push. - * - * @author nacos - */ -public interface DataSource { - - /** - * Get push data for a specified client. - * - * @param client target client - * @return data to push - * @throws Exception exception - */ - String getData(PushClient client) throws Exception; -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v1/NamingSubscriberServiceV1Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v1/NamingSubscriberServiceV1Impl.java deleted file mode 100644 index 3cca49890..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v1/NamingSubscriberServiceV1Impl.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.push.v1; - -import com.alibaba.nacos.api.naming.utils.NamingUtils; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.misc.GlobalExecutor; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.naming.pojo.Subscriber; -import com.alibaba.nacos.naming.push.NamingSubscriberService; - -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; - -/** - * Naming subscriber service for v1.x. - * - * @author xiweng.yy - * @deprecated Will be removed in v2.1.x version - */ -@org.springframework.stereotype.Service -@Deprecated -public class NamingSubscriberServiceV1Impl implements NamingSubscriberService { - - private final ConcurrentMap> clientMap = new ConcurrentHashMap<>(); - - public NamingSubscriberServiceV1Impl() { - GlobalExecutor.scheduleRetransmitter(() -> { - try { - removeClientIfZombie(); - } catch (Throwable e) { - Loggers.PUSH.warn("[NACOS-PUSH] failed to remove client zombie"); - } - }, 0, 20, TimeUnit.SECONDS); - } - - private void removeClientIfZombie() { - int size = 0; - for (Map.Entry> entry : clientMap.entrySet()) { - ConcurrentMap clientConcurrentMap = entry.getValue(); - for (Map.Entry entry1 : clientConcurrentMap.entrySet()) { - PushClient client = entry1.getValue(); - if (client.zombie()) { - clientConcurrentMap.remove(entry1.getKey()); - } - } - size += clientConcurrentMap.size(); - } - if (Loggers.PUSH.isDebugEnabled()) { - Loggers.PUSH.debug("[NACOS-PUSH] clientMap size: {}", size); - } - } - - public ConcurrentMap> getClientMap() { - return clientMap; - } - - @Override - public Collection getSubscribers(String namespaceId, String serviceName) { - String serviceKey = UtilsAndCommons.assembleFullServiceName(namespaceId, serviceName); - ConcurrentMap clientConcurrentMap = clientMap.get(serviceKey); - if (Objects.isNull(clientConcurrentMap)) { - return Collections.emptyList(); - } - Collection result = new ArrayList<>(); - clientConcurrentMap.forEach((key, client) -> result - .add(new Subscriber(client.getAddrStr(), client.getAgent(), client.getApp(), client.getIp(), - namespaceId, serviceName, client.getPort()))); - return result; - } - - @Override - public Collection getSubscribers(Service service) { - return getSubscribers(service.getNamespace(), service.getGroupedServiceName()); - } - - @Override - public Collection getFuzzySubscribers(String namespaceId, String serviceName) { - Collection result = new ArrayList<>(); - clientMap.forEach((outKey, clientConcurrentMap) -> { - //get groupedName from key - String serviceFullName = outKey.split(UtilsAndCommons.NAMESPACE_SERVICE_CONNECTOR)[1]; - //get groupName - String groupName = NamingUtils.getGroupName(serviceFullName); - //get serviceName - String name = NamingUtils.getServiceName(serviceFullName); - //fuzzy match - if (outKey.startsWith(namespaceId) && name.indexOf(NamingUtils.getServiceName(serviceName)) >= 0 - && groupName.indexOf(NamingUtils.getGroupName(serviceName)) >= 0) { - clientConcurrentMap.forEach((key, client) -> { - result.add(new Subscriber(client.getAddrStr(), client.getAgent(), client.getApp(), client.getIp(), - namespaceId, serviceFullName, client.getPort())); - }); - } - }); - return result; - } - - @Override - public Collection getFuzzySubscribers(Service service) { - return getFuzzySubscribers(service.getNamespace(), service.getGroupedServiceName()); - } - - /** - * Add push target client. - * - * @param namespaceId namespace id - * @param serviceName service name - * @param clusters cluster - * @param agent agent information - * @param socketAddr client address - * @param dataSource datasource of push data - * @param tenant tenant - * @param app app - */ - public void addClient(String namespaceId, String serviceName, String clusters, String agent, - InetSocketAddress socketAddr, DataSource dataSource, String tenant, String app) { - - PushClient client = new PushClient(namespaceId, serviceName, clusters, agent, socketAddr, dataSource, tenant, - app); - addClient(client); - } - - /** - * Add push target client. - * - * @param client push target client - */ - public void addClient(PushClient client) { - // client is stored by key 'serviceName' because notify event is driven by serviceName change - String serviceKey = UtilsAndCommons.assembleFullServiceName(client.getNamespaceId(), client.getServiceName()); - ConcurrentMap clients = clientMap.get(serviceKey); - if (clients == null) { - clientMap.putIfAbsent(serviceKey, new ConcurrentHashMap<>(1024)); - clients = clientMap.get(serviceKey); - } - - PushClient oldClient = clients.get(client.toString()); - if (oldClient != null) { - oldClient.refresh(); - } else { - PushClient res = clients.putIfAbsent(client.toString(), client); - if (res != null) { - Loggers.PUSH.warn("client: {} already associated with key {}", res.getAddrStr(), res); - } - Loggers.PUSH.debug("client: {} added for serviceName: {}", client.getAddrStr(), client.getServiceName()); - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v1/PushClient.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v1/PushClient.java deleted file mode 100644 index 29bacc3ad..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v1/PushClient.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.push.v1; - -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.sys.utils.ApplicationUtils; - -import java.net.InetSocketAddress; -import java.util.Map; -import java.util.Objects; - -/** - * Push Client for v1.x. - * - * @author xiweng.yy - */ -public class PushClient { - - private String namespaceId; - - private String serviceName; - - private String clusters; - - private String agent; - - private String tenant; - - private String app; - - private InetSocketAddress socketAddr; - - private DataSource dataSource; - - private Map params; - - public Map getParams() { - return params; - } - - public void setParams(Map params) { - this.params = params; - } - - public long lastRefTime = System.currentTimeMillis(); - - public PushClient(String namespaceId, String serviceName, String clusters, String agent, - InetSocketAddress socketAddr, DataSource dataSource, String tenant, String app) { - this.namespaceId = namespaceId; - this.serviceName = serviceName; - this.clusters = clusters; - this.agent = agent; - this.socketAddr = socketAddr; - this.dataSource = dataSource; - this.tenant = tenant; - this.app = app; - } - - public DataSource getDataSource() { - return dataSource; - } - - public boolean zombie() { - return System.currentTimeMillis() - lastRefTime > ApplicationUtils.getBean(SwitchDomain.class) - .getPushCacheMillis(serviceName); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("serviceName: ").append(serviceName).append(", clusters: ").append(clusters).append(", address: ") - .append(socketAddr).append(", agent: ").append(agent); - return sb.toString(); - } - - public String getAgent() { - return agent; - } - - public String getAddrStr() { - return socketAddr.getAddress().getHostAddress() + ":" + socketAddr.getPort(); - } - - public String getIp() { - return socketAddr.getAddress().getHostAddress(); - } - - public int getPort() { - return socketAddr.getPort(); - } - - @Override - public int hashCode() { - return Objects.hash(serviceName, clusters, socketAddr); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof PushClient)) { - return false; - } - - PushClient other = (PushClient) obj; - - return serviceName.equals(other.serviceName) && clusters.equals(other.clusters) && socketAddr - .equals(other.socketAddr); - } - - public String getClusters() { - return clusters; - } - - public void setClusters(String clusters) { - this.clusters = clusters; - } - - public String getNamespaceId() { - return namespaceId; - } - - public void setNamespaceId(String namespaceId) { - this.namespaceId = namespaceId; - } - - public String getServiceName() { - return serviceName; - } - - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } - - public String getTenant() { - return tenant; - } - - public void setTenant(String tenant) { - this.tenant = tenant; - } - - public String getApp() { - return app; - } - - public void setApp(String app) { - this.app = app; - } - - public InetSocketAddress getSocketAddr() { - return socketAddr; - } - - public void refresh() { - lastRefTime = System.currentTimeMillis(); - } - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v1/ServiceChangeEvent.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v1/ServiceChangeEvent.java deleted file mode 100644 index d31fbc876..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v1/ServiceChangeEvent.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.push.v1; - -import com.alibaba.nacos.naming.core.Service; -import org.springframework.context.ApplicationEvent; - -/** - * Service change event. - * - * @author pbting - * @date 2019-07-10 5:41 PM - */ -public class ServiceChangeEvent extends ApplicationEvent { - - private Service service; - - public ServiceChangeEvent(Object source, Service service) { - super(source); - this.service = service; - } - - public Service getService() { - return service; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java index 0841b4197..6933779d0 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java @@ -28,7 +28,6 @@ import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.naming.monitor.MetricsMonitor; import com.alibaba.nacos.naming.pojo.Subscriber; @@ -59,15 +58,11 @@ public class NamingSubscriberServiceV2Impl extends SmartSubscriber implements Na private final PushDelayTaskExecuteEngine delayTaskEngine; - private final UpgradeJudgement upgradeJudgement; - public NamingSubscriberServiceV2Impl(ClientManagerDelegate clientManager, ClientServiceIndexesManager indexesManager, ServiceStorage serviceStorage, - NamingMetadataManager metadataManager, PushExecutorDelegate pushExecutor, UpgradeJudgement upgradeJudgement, - SwitchDomain switchDomain) { + NamingMetadataManager metadataManager, PushExecutorDelegate pushExecutor, SwitchDomain switchDomain) { this.clientManager = clientManager; this.indexesManager = indexesManager; - this.upgradeJudgement = upgradeJudgement; this.delayTaskEngine = new PushDelayTaskExecuteEngine(clientManager, indexesManager, serviceStorage, metadataManager, pushExecutor, switchDomain); NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance()); @@ -118,9 +113,6 @@ public class NamingSubscriberServiceV2Impl extends SmartSubscriber implements Na @Override public void onEvent(Event event) { - if (!upgradeJudgement.isUseGrpcFeatures()) { - return; - } if (event instanceof ServiceEvent.ServiceChangedEvent) { // If service changed, push to all subscribers. ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event; diff --git a/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/filter/GrpcRequestFilter.java b/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/filter/GrpcRequestFilter.java deleted file mode 100644 index 0473f86d2..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/filter/GrpcRequestFilter.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.remote.rpc.filter; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest; -import com.alibaba.nacos.api.remote.request.Request; -import com.alibaba.nacos.api.remote.request.RequestMeta; -import com.alibaba.nacos.api.remote.response.Response; -import com.alibaba.nacos.core.remote.AbstractRequestFilter; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Grpc request filter. - * - * @author majorhe - */ -@Component -public class GrpcRequestFilter extends AbstractRequestFilter { - - @Autowired - private UpgradeJudgement upgradeJudgement; - - @Override - protected Response filter(Request request, RequestMeta meta, Class handlerClazz) throws NacosException { - if (request instanceof AbstractNamingRequest && !upgradeJudgement.isUseGrpcFeatures()) { - Response response = getDefaultResponseInstance(handlerClazz); - response.setErrorInfo(NacosException.SERVER_ERROR, - "Nacos cluster is running with 1.X mode, can't accept gRPC request temporarily. Please check the server status or close Double write to force open 2.0 mode. Detail https://nacos.io/en-us/docs/2.0.0-upgrading.html."); - return response; - } - return null; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/selector/LabelSelector.java b/naming/src/main/java/com/alibaba/nacos/naming/selector/LabelSelector.java index a172b445e..161fe936f 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/selector/LabelSelector.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/selector/LabelSelector.java @@ -35,8 +35,6 @@ import java.util.Set; import java.util.stream.Collectors; /** - * The implement of {@link com.alibaba.nacos.naming.selector.v1.LabelSelector} at new version. - * The main logic is same with {@link com.alibaba.nacos.naming.selector.v1.LabelSelector}. * The {@link LabelSelector} will return the instances labels in {@link #labels} and providers' label value is same with consumer. * If none matched, then will return all providers instead of. * diff --git a/naming/src/main/java/com/alibaba/nacos/naming/selector/NoneSelector.java b/naming/src/main/java/com/alibaba/nacos/naming/selector/NoneSelector.java index 4b0982ce4..363945cf1 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/selector/NoneSelector.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/selector/NoneSelector.java @@ -24,7 +24,7 @@ import com.alibaba.nacos.api.selector.Selector; import java.util.List; /** - * Selector with no filtering. The logic is same to {@link com.alibaba.nacos.naming.selector.v1.NoneSelector}. + * Selector with no filtering. * * @author chenglu * @date 2021-08-04 13:28 diff --git a/naming/src/main/java/com/alibaba/nacos/naming/selector/v1/LabelSelector.java b/naming/src/main/java/com/alibaba/nacos/naming/selector/v1/LabelSelector.java deleted file mode 100644 index c04c2f1b0..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/selector/v1/LabelSelector.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright 1999-2021 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.nacos.naming.selector.v1; - -import com.alibaba.nacos.api.cmdb.pojo.PreservedEntityTypes; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.alibaba.nacos.api.selector.ExpressionSelector; -import com.alibaba.nacos.api.selector.SelectorType; -import com.alibaba.nacos.cmdb.service.CmdbReader; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.sys.utils.ApplicationUtils; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; - -import com.alibaba.nacos.common.utils.StringUtils; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * A selector to implement a so called same-label-prior rule for service discovery. - *

Backgroup

- * Consider service providers are deployed in two sites i.e. site A and site B, and consumers of this service provider - * are also deployed in site A and site B. So the consumers may want to visit the service provider in current site, thus - * consumers in site A visit service providers in site A and consumers in site B visit service providers in site B. This - * is quite useful to reduce the transfer delay of RPC. This is called same-site-prior strategy. - *

Same Label Prior

- * The same-site-prior strategy covers many circumstances in large companies and we can abstract it to a higher level - * strategy: same-label-prior. - * - *

So the idea is that presumed we have built a self-defined or integrated a third-party idc CMDB which stores all - * the labels of all IPs. Then we can filter provider IPs by the consumer IP and we only return the providers who have - * the same label values with consumer. We can define the labels we want to include in the comparison. - * - *

If no provider has the same label value with the consumer, we fall back to give all providers to the consumer. - * Note that this fallback strategy may also be abstracted in future to introduce more kinds of behaviors. - * - * @author nkorange - * @see CmdbReader - * @since 0.7.0 - */ -@Deprecated -@JsonTypeInfo(use = Id.NAME, property = "type") -public class LabelSelector extends ExpressionSelector implements Selector { - - private static final long serialVersionUID = -7381912003505096093L; - - /** - * The labels relevant to this the selector. - * - * @see com.alibaba.nacos.api.cmdb.pojo.Label - */ - private Set labels; - - private static final Set SUPPORTED_INNER_CONNCETORS = new HashSet<>(); - - private static final Set SUPPORTED_OUTER_CONNCETORS = new HashSet<>(); - - private static final String CONSUMER_PREFIX = "CONSUMER.label."; - - private static final String PROVIDER_PREFIX = "PROVIDER.label."; - - private static final char CEQUAL = '='; - - private static final char CAND = '&'; - - static { - SUPPORTED_INNER_CONNCETORS.add(String.valueOf(CEQUAL)); - SUPPORTED_OUTER_CONNCETORS.add(String.valueOf(CAND)); - JacksonUtils.registerSubtype(LabelSelector.class, SelectorType.label.name()); - } - - public Set getLabels() { - return labels; - } - - public void setLabels(Set labels) { - this.labels = labels; - } - - public LabelSelector() { - super(); - } - - private CmdbReader getCmdbReader() { - return ApplicationUtils.getBean(CmdbReader.class); - } - - public static Set parseExpression(String expression) throws NacosException { - return ExpressionInterpreter.parseExpression(expression); - } - - @Override - public List select(String consumer, List providers) { - if (labels.isEmpty()) { - return providers; - } - - List instanceList = new ArrayList<>(); - for (T instance : providers) { - - boolean matched = true; - for (String labelName : getLabels()) { - - String consumerLabelValue = getCmdbReader() - .queryLabel(consumer, PreservedEntityTypes.ip.name(), labelName); - - if (StringUtils.isNotBlank(consumerLabelValue) && !StringUtils.equals(consumerLabelValue, - getCmdbReader().queryLabel(instance.getIp(), PreservedEntityTypes.ip.name(), labelName))) { - matched = false; - break; - } - } - if (matched) { - instanceList.add(instance); - } - } - - if (instanceList.isEmpty()) { - return providers; - } - - return instanceList; - } - - /** - * Expression interpreter for label selector. - * - *

For now it supports very limited set of syntax rules. - */ - public static class ExpressionInterpreter { - - /** - * Parse the label expression. - * - *

Currently we support the very single type of expression: - *

-         *     consumer.labelA = provider.labelA & consumer.labelB = provider.labelB
-         * 
- * Later we will implement a interpreter to parse this expression in a standard LL parser way. - * - * @param expression the label expression to parse - * @return collection of labels - */ - public static Set parseExpression(String expression) throws NacosException { - - if (StringUtils.isBlank(expression)) { - return new HashSet<>(); - } - - expression = StringUtils.deleteWhitespace(expression); - - List elements = getTerms(expression); - Set gotLabels = new HashSet<>(); - int index = 0; - - index = checkInnerSyntax(elements, index); - - if (index == -1) { - throw new NacosException(NacosException.INVALID_PARAM, "parse expression failed!"); - } - - gotLabels.add(elements.get(index++).split(PROVIDER_PREFIX)[1]); - - while (index < elements.size()) { - - index = checkOuterSyntax(elements, index); - - if (index >= elements.size()) { - return gotLabels; - } - - if (index == -1) { - throw new NacosException(NacosException.INVALID_PARAM, "parse expression failed!"); - } - - gotLabels.add(elements.get(index++).split(PROVIDER_PREFIX)[1]); - } - - return gotLabels; - } - - public static List getTerms(String expression) { - - List terms = new ArrayList<>(); - - Set characters = new HashSet<>(); - characters.add(CEQUAL); - characters.add(CAND); - - char[] chars = expression.toCharArray(); - - int lastIndex = 0; - for (int index = 0; index < chars.length; index++) { - char ch = chars[index]; - if (characters.contains(ch)) { - terms.add(expression.substring(lastIndex, index)); - terms.add(expression.substring(index, index + 1)); - index++; - lastIndex = index; - } - } - - terms.add(expression.substring(lastIndex, chars.length)); - - return terms; - } - - private static int skipEmpty(List elements, int start) { - while (start < elements.size() && StringUtils.isBlank(elements.get(start))) { - start++; - } - return start; - } - - private static int checkOuterSyntax(List elements, int start) { - - int index = start; - - index = skipEmpty(elements, index); - if (index >= elements.size()) { - return index; - } - - if (!SUPPORTED_OUTER_CONNCETORS.contains(elements.get(index++))) { - return -1; - } - - return checkInnerSyntax(elements, index); - } - - private static int checkInnerSyntax(List elements, int start) { - - int index = start; - - index = skipEmpty(elements, index); - if (index >= elements.size()) { - return -1; - } - - if (!elements.get(index).startsWith(CONSUMER_PREFIX)) { - return -1; - } - - final String labelConsumer = elements.get(index++).split(CONSUMER_PREFIX)[1]; - - index = skipEmpty(elements, index); - if (index >= elements.size()) { - return -1; - } - - if (!SUPPORTED_INNER_CONNCETORS.contains(elements.get(index++))) { - return -1; - } - - index = skipEmpty(elements, index); - if (index >= elements.size()) { - return -1; - } - - if (!elements.get(index).startsWith(PROVIDER_PREFIX)) { - return -1; - } - - final String labelProvider = elements.get(index).split(PROVIDER_PREFIX)[1]; - - if (!labelConsumer.equals(labelProvider)) { - return -1; - } - - return index; - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/selector/v1/NoneSelector.java b/naming/src/main/java/com/alibaba/nacos/naming/selector/v1/NoneSelector.java deleted file mode 100644 index 03de51621..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/selector/v1/NoneSelector.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 1999-2021 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.nacos.naming.selector.v1; - -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.alibaba.nacos.api.selector.SelectorType; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; - -import java.util.List; - -/** - * Selector with no filtering. - * - * @author nkorange - * @since 0.7.0 - */ -@Deprecated -@JsonTypeInfo(use = Id.NAME, property = "type") -public class NoneSelector extends com.alibaba.nacos.api.selector.NoneSelector implements Selector { - - private static final long serialVersionUID = -3752116616221930677L; - - static { - JacksonUtils.registerSubtype(NoneSelector.class, SelectorType.none.name()); - } - - @Override - public List select(String consumer, List providers) { - return providers; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/selector/v1/Selector.java b/naming/src/main/java/com/alibaba/nacos/naming/selector/v1/Selector.java deleted file mode 100644 index 6e9b4d704..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/selector/v1/Selector.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 1999-2021 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.nacos.naming.selector.v1; - -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; - -import java.io.Serializable; -import java.util.List; - -/** - * Selector defines a rule for load-balance for service discovery. - * - *

Every service in Nacos can apply an existing selector and uses it to give the consumer a subset of selected - * providers. - * - *

This selector itself does not implement any specific behavior of load-balance, every real life selector should - * extend this class and implement the select method. - * - *

Every extended selector should also register its type to class SelectorType so Nacos recognizes it and can - * correctly create this type of selector. - * - *

Sub class should register their type to JacksonUtil. - * - * @author nkorange - * @see com.alibaba.nacos.api.selector.SelectorType - * @see com.alibaba.nacos.common.utils.JacksonUtils - * @since 0.7.0 - */ -@Deprecated -@JsonTypeInfo(use = Id.NAME, property = "type", defaultImpl = NoneSelector.class) -public interface Selector extends Serializable { - - /** - * Get the type of this selector. - * - * @return type of selector - */ - String getType(); - - /** - * Select qualified instances from providers. - * - * @param consumer consumer address - * @param providers candidate provider addresses - * @return selected provider addresses - */ - List select(String consumer, List providers); -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/utils/DistroUtils.java b/naming/src/main/java/com/alibaba/nacos/naming/utils/DistroUtils.java new file mode 100644 index 000000000..16ce61376 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/utils/DistroUtils.java @@ -0,0 +1,180 @@ +/* + * Copyright 1999-2022 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.nacos.naming.utils; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.common.utils.MD5Utils; +import com.alibaba.nacos.naming.core.v2.client.Client; +import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; +import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import org.apache.commons.lang.StringUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import static com.alibaba.nacos.naming.constants.Constants.DEFAULT_INSTANCE_WEIGHT; +import static com.alibaba.nacos.naming.constants.Constants.PUBLISH_INSTANCE_ENABLE; +import static com.alibaba.nacos.naming.constants.Constants.PUBLISH_INSTANCE_WEIGHT; +import static com.alibaba.nacos.naming.misc.UtilsAndCommons.DEFAULT_CLUSTER_NAME; + +/** + * Utils to generate revision/checksum of distro clients. + * + * @author Pixy Yuan + * on 2021/10/9 + */ +public class DistroUtils { + + /** + * Build service key. + */ + public static String serviceKey(Service service) { + return service.getNamespace() + + "##" + + service.getGroupedServiceName() + + "##" + + service.isEphemeral(); + } + + /** + * Calculate hash of unique string built by client's metadata. + */ + public static int stringHash(Client client) { + String s = buildUniqueString(client); + if (s == null) { + return 0; + } + return s.hashCode(); + } + + /** + * Calculate hash for client. Reduce strings in memory and cpu costs. + */ + public static int hash(Client client) { + if (!(client instanceof IpPortBasedClient)) { + return 0; + } + return Objects.hash(client.getClientId(), + client.getAllPublishedService().stream() + .map(s -> { + InstancePublishInfo ip = client.getInstancePublishInfo(s); + double weight = getWeight(ip); + Boolean enabled = getEnabled(ip); + String cluster = StringUtils.defaultIfBlank(ip.getCluster(), DEFAULT_CLUSTER_NAME); + return Objects.hash( + s.getNamespace(), + s.getGroup(), + s.getName(), + s.isEphemeral(), + ip.getIp(), + ip.getPort(), + weight, + ip.isHealthy(), + enabled, + cluster, + ip.getExtendDatum() + ); + }) + .collect(Collectors.toSet())); + } + + /** + * Calculate checksum for client. + */ + public static String checksum(Client client) { + String s = buildUniqueString(client); + if (s == null) { + return "0"; + } + return MD5Utils.md5Hex(s, Constants.ENCODE); + } + + /** + * Calculate unique string for client. + */ + public static String buildUniqueString(Client client) { + if (!(client instanceof IpPortBasedClient)) { + return null; + } + StringBuilder sb = new StringBuilder(); + sb.append(client.getClientId()).append('|'); + client.getAllPublishedService().stream() + .sorted(Comparator.comparing(DistroUtils::serviceKey)) + .forEach(s -> { + InstancePublishInfo ip = client.getInstancePublishInfo(s); + double weight = getWeight(ip); + Boolean enabled = getEnabled(ip); + String cluster = StringUtils.defaultIfBlank(ip.getCluster(), DEFAULT_CLUSTER_NAME); + sb.append(serviceKey(s)).append('_') + .append(ip.getIp()).append(':').append(ip.getPort()).append('_') + .append(weight).append('_') + .append(ip.isHealthy()).append('_') + .append(enabled).append('_') + .append(cluster).append('_') + .append(convertMap2String(ip.getExtendDatum())) + .append(','); + }); + return sb.toString(); + } + + private static boolean getEnabled(InstancePublishInfo ip) { + Object enabled0 = ip.getExtendDatum().get(PUBLISH_INSTANCE_ENABLE); + if (!(enabled0 instanceof Boolean)) { + return true; + } else { + return (Boolean) enabled0; + } + } + + private static double getWeight(InstancePublishInfo ip) { + Object weight0 = ip.getExtendDatum().get(PUBLISH_INSTANCE_WEIGHT); + if (!(weight0 instanceof Number)) { + return DEFAULT_INSTANCE_WEIGHT; + } else { + return ((Number) weight0).doubleValue(); + } + } + + /** + * Convert Map to KV string with ':'. + * + * @param map map need to be converted + * @return KV string with ':' + */ + private static String convertMap2String(Map map) { + if (map == null || map.isEmpty()) { + return StringUtils.EMPTY; + } + StringBuilder sb = new StringBuilder(); + List keys = new ArrayList<>(map.keySet()); + Collections.sort(keys); + for (String key : keys) { + sb.append(key); + sb.append(':'); + sb.append(map.get(key)); + sb.append(','); + } + return sb.toString(); + } + +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/utils/ServiceUtil.java b/naming/src/main/java/com/alibaba/nacos/naming/utils/ServiceUtil.java index 7fe228b27..411f83460 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/utils/ServiceUtil.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/utils/ServiceUtil.java @@ -18,27 +18,20 @@ package com.alibaba.nacos.naming.utils; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; -import com.alibaba.nacos.api.selector.SelectorType; -import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.core.Service; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.selector.SelectorManager; import com.alibaba.nacos.sys.utils.ApplicationUtils; -import com.fasterxml.jackson.databind.JsonNode; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -49,104 +42,6 @@ import java.util.stream.Collectors; */ public final class ServiceUtil { - /** - * Select service name with group name. - * - * @param services service map - * @param groupName group name - * @return service names with group name - */ - public static Map selectServiceWithGroupName(Map services, String groupName) { - if (null == services || services.isEmpty()) { - return new HashMap<>(0); - } - Map result = new HashMap<>(services.size()); - String groupKey = groupName + Constants.SERVICE_INFO_SPLITER; - for (Map.Entry each : services.entrySet()) { - if (each.getKey().startsWith(groupKey)) { - result.put(each.getKey(), each.getValue()); - } - } - return result; - } - - /** - * Select service name by selector. - * - * @param serviceMap service name list - * @param selectorString selector serialize string - * @return service names filter by group name - */ - public static Map selectServiceBySelector(Map serviceMap, String selectorString) { - Map result = serviceMap; - if (StringUtils.isNotBlank(selectorString)) { - - JsonNode selectorJson = JacksonUtils.toObj(selectorString); - - SelectorType selectorType = SelectorType.valueOf(selectorJson.get("type").asText()); - String expression = selectorJson.get("expression").asText(); - - if (SelectorType.label.equals(selectorType) && StringUtils.isNotBlank(expression)) { - expression = StringUtils.deleteWhitespace(expression); - // Now we only support the following expression: - // INSTANCE.metadata.xxx = 'yyy' or - // SERVICE.metadata.xxx = 'yyy' - String[] terms = expression.split("="); - String[] factors = terms[0].split("\\."); - switch (factors[0]) { - case "INSTANCE": - result = filterInstanceMetadata(serviceMap, factors[factors.length - 1], - terms[1].replace("'", "")); - break; - case "SERVICE": - result = filterServiceMetadata(serviceMap, factors[factors.length - 1], - terms[1].replace("'", "")); - break; - default: - break; - } - } - } - return result; - } - - private static Map filterInstanceMetadata(Map serviceMap, String key, - String value) { - Map result = new HashMap<>(serviceMap.size()); - for (Map.Entry each : serviceMap.entrySet()) { - for (Instance address : each.getValue().allIPs()) { - if (address.getMetadata() != null && value.equals(address.getMetadata().get(key))) { - result.put(each.getKey(), each.getValue()); - break; - } - } - } - return result; - } - - private static Map filterServiceMetadata(Map serviceMap, String key, - String value) { - Map result = new HashMap<>(serviceMap.size()); - for (Map.Entry each : serviceMap.entrySet()) { - if (value.equals(each.getValue().getMetadata().get(key))) { - result.put(each.getKey(), each.getValue()); - } - } - return result; - } - - /** - * Page service name. - * - * @param pageNo page number - * @param pageSize size per page - * @param serviceMap service source map - * @return service name list by paged - */ - public static List pageServiceName(int pageNo, int pageSize, Map serviceMap) { - return pageServiceName(pageNo, pageSize, serviceMap.keySet()); - } - /** * Page service name. * diff --git a/naming/src/main/java/com/alibaba/nacos/naming/web/ClientAttributesFilter.java b/naming/src/main/java/com/alibaba/nacos/naming/web/ClientAttributesFilter.java new file mode 100644 index 000000000..d89407a5a --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/web/ClientAttributesFilter.java @@ -0,0 +1,132 @@ +/* + * Copyright 1999-2020 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.nacos.naming.web; + +import com.alibaba.nacos.common.constant.HttpHeaderConsts; +import com.alibaba.nacos.common.utils.HttpMethod; +import com.alibaba.nacos.common.utils.InternetAddressUtil; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.core.utils.WebUtils; +import com.alibaba.nacos.naming.core.v2.client.ClientAttributes; +import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; +import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.misc.Loggers; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + *

+ * collect client attributes for 1.x. + *

+ * + * @author hujun + */ +public class ClientAttributesFilter implements Filter { + + private static final String BEAT_URI = "/beat"; + + private static final String IP = "ip"; + + private static final String PORT = "port"; + + private static final String ZERO = "0"; + + @Autowired + private ClientManager clientManager; + + public static ThreadLocal threadLocalClientAttributes = new ThreadLocal<>(); + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + try { + try { + if ((UtilsAndCommons.NACOS_SERVER_CONTEXT + UtilsAndCommons.NACOS_NAMING_CONTEXT + + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT).equals(request.getRequestURI()) + && request.getMethod().equals(HttpMethod.POST)) { + //register + ClientAttributes requestClientAttributes = getClientAttributes(request); + threadLocalClientAttributes.set(requestClientAttributes); + } else if ((UtilsAndCommons.NACOS_SERVER_CONTEXT + UtilsAndCommons.NACOS_NAMING_CONTEXT + + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT + BEAT_URI).equals(request.getRequestURI())) { + //beat + String ip = WebUtils.optional(request, IP, StringUtils.EMPTY); + int port = Integer.parseInt(WebUtils.optional(request, PORT, ZERO)); + String clientId = IpPortBasedClient.getClientId(ip + InternetAddressUtil.IP_PORT_SPLITER + port, + true); + IpPortBasedClient client = (IpPortBasedClient) clientManager.getClient(clientId); + if (client != null) { + ClientAttributes requestClientAttributes = getClientAttributes(request); + //update clientAttributes,when client version attributes is null,then update. + if (canUpdateClientAttributes(client, requestClientAttributes)) { + client.setAttributes(requestClientAttributes); + } + } + } + } catch (Exception e) { + Loggers.SRV_LOG.error("handler client attributes error", e); + } + try { + filterChain.doFilter(request, servletResponse); + } catch (ServletException e) { + throw new RuntimeException(e); + } + } finally { + if (threadLocalClientAttributes.get() != null) { + threadLocalClientAttributes.remove(); + } + } + } + + private static boolean canUpdateClientAttributes(IpPortBasedClient client, + ClientAttributes requestClientAttributes) { + if (requestClientAttributes.getClientAttribute(HttpHeaderConsts.CLIENT_VERSION_HEADER) == null) { + return false; + } + if (client.getClientAttributes() != null + && client.getClientAttributes().getClientAttribute(HttpHeaderConsts.CLIENT_VERSION_HEADER) != null) { + return false; + } + return true; + } + + public static ClientAttributes getClientAttributes(HttpServletRequest request) { + String version = request.getHeader(HttpHeaderConsts.CLIENT_VERSION_HEADER); + String app = request.getHeader(HttpHeaderConsts.APP_FILED); + String clientIp = request.getRemoteAddr(); + ClientAttributes clientAttributes = new ClientAttributes(); + if (version != null) { + clientAttributes.addClientAttribute(HttpHeaderConsts.CLIENT_VERSION_HEADER, version); + } + if (app != null) { + clientAttributes.addClientAttribute(HttpHeaderConsts.APP_FILED, app); + } + if (clientIp != null) { + clientAttributes.addClientAttribute(HttpHeaderConsts.CLIENT_IP, clientIp); + } + return clientAttributes; + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/web/DistroServiceNameTagGenerator.java b/naming/src/main/java/com/alibaba/nacos/naming/web/DistroServiceNameTagGenerator.java deleted file mode 100644 index 782fa1433..000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/web/DistroServiceNameTagGenerator.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.nacos.naming.web; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.naming.CommonParams; -import com.alibaba.nacos.core.utils.ReuseHttpServletRequest; -import com.alibaba.nacos.common.utils.StringUtils; - -/** - * Distro service name tag generator for v1.x. - * - * @author xiweng.yy - */ -public class DistroServiceNameTagGenerator implements DistroTagGenerator { - - @Override - public String getResponsibleTag(ReuseHttpServletRequest request) { - String serviceName = request.getParameter(CommonParams.SERVICE_NAME); - - if (StringUtils.isNotBlank(serviceName)) { - serviceName = serviceName.trim(); - } - String groupName = request.getParameter(CommonParams.GROUP_NAME); - if (StringUtils.isBlank(groupName)) { - groupName = Constants.DEFAULT_GROUP; - } - - // use groupName@@serviceName as new service name: - String groupedServiceName = serviceName; - if (StringUtils.isNotBlank(serviceName) && !serviceName.contains(Constants.SERVICE_INFO_SPLITER)) { - groupedServiceName = groupName + Constants.SERVICE_INFO_SPLITER + serviceName; - } - return groupedServiceName; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/web/DistroTagGeneratorImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/web/DistroTagGeneratorImpl.java index cd2ba4339..2d022a982 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/web/DistroTagGeneratorImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/web/DistroTagGeneratorImpl.java @@ -17,7 +17,6 @@ package com.alibaba.nacos.naming.web; import com.alibaba.nacos.core.utils.ReuseHttpServletRequest; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; import org.springframework.stereotype.Component; /** @@ -28,14 +27,9 @@ import org.springframework.stereotype.Component; @Component public class DistroTagGeneratorImpl implements DistroTagGenerator { - private final DistroTagGenerator serviceNameTag = new DistroServiceNameTagGenerator(); - private final DistroTagGenerator ipPortTag = new DistroIpPortTagGenerator(); - private final UpgradeJudgement upgradeJudgement; - - public DistroTagGeneratorImpl(UpgradeJudgement upgradeJudgement) { - this.upgradeJudgement = upgradeJudgement; + public DistroTagGeneratorImpl() { } @Override @@ -47,13 +41,12 @@ public class DistroTagGeneratorImpl implements DistroTagGenerator { * Get tag generator according to cluster member ability. * *

- * If all member is upper than 2.x. Using {@link DistroIpPortTagGenerator}. Otherwise use 1.x {@link - * DistroServiceNameTagGenerator}. + * If all member is upper than 2.x. Using {@link DistroIpPortTagGenerator}. *

* * @return actual tag generator */ private DistroTagGenerator getTagGenerator() { - return upgradeJudgement.isUseGrpcFeatures() ? ipPortTag : serviceNameTag; + return ipPortTag; } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/web/NamingConfig.java b/naming/src/main/java/com/alibaba/nacos/naming/web/NamingConfig.java index 430b378d5..ccb5cc7f9 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/web/NamingConfig.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/web/NamingConfig.java @@ -36,6 +36,8 @@ public class NamingConfig { private static final String TRAFFIC_REVISE_FILTER = "trafficReviseFilter"; + private static final String CLIENT_ATTRIBUTES_FILTER = "clientAttributes_filter"; + @Bean public FilterRegistrationBean distroFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean<>(); @@ -66,6 +68,16 @@ public class NamingConfig { return registration; } + @Bean + public FilterRegistrationBean clientAttributesFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean<>(); + registration.setFilter(clientAttributesFilter()); + registration.addUrlPatterns(UTL_PATTERNS); + registration.setName(CLIENT_ATTRIBUTES_FILTER); + registration.setOrder(7); + return registration; + } + @Bean public DistroFilter distroFilter() { return new DistroFilter(); @@ -81,4 +93,8 @@ public class NamingConfig { return new ServiceNameFilter(); } + @Bean + public ClientAttributesFilter clientAttributesFilter() { + return new ClientAttributesFilter(); + } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/web/ServiceNameFilter.java b/naming/src/main/java/com/alibaba/nacos/naming/web/ServiceNameFilter.java index 7152bc208..fc81425ad 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/web/ServiceNameFilter.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/web/ServiceNameFilter.java @@ -18,9 +18,10 @@ package com.alibaba.nacos.naming.web; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.CommonParams; +import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.utils.ExceptionUtil; -import com.alibaba.nacos.core.utils.OverrideParameterRequestWrapper; import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.core.utils.OverrideParameterRequestWrapper; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -63,6 +64,14 @@ public class ServiceNameFilter implements Filter { if (StringUtils.isNotBlank(serviceName) && !serviceName.contains(Constants.SERVICE_INFO_SPLITER)) { groupedServiceName = groupName + Constants.SERVICE_INFO_SPLITER + serviceName; } + if (StringUtils.isNotBlank(groupedServiceName)) { + try { + NamingUtils.checkServiceNameFormat(groupedServiceName); + } catch (IllegalArgumentException e) { + resp.sendError(HttpServletResponse.SC_BAD_REQUEST, + "Service name filter error," + ExceptionUtil.getAllExceptionMsg(e)); + } + } OverrideParameterRequestWrapper requestWrapper = OverrideParameterRequestWrapper.buildRequest(request); requestWrapper.addParameter(CommonParams.SERVICE_NAME, groupedServiceName); filterChain.doFilter(requestWrapper, servletResponse); diff --git a/naming/src/test/java/com/alibaba/nacos/naming/BaseTest.java b/naming/src/test/java/com/alibaba/nacos/naming/BaseTest.java index 3d1ca30a8..97755e27c 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/BaseTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/BaseTest.java @@ -16,14 +16,7 @@ package com.alibaba.nacos.naming; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftCore; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeer; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeerSet; import com.alibaba.nacos.naming.core.DistroMapper; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import com.alibaba.nacos.naming.healthcheck.HealthCheckProcessorDelegate; -import com.alibaba.nacos.naming.misc.NetUtils; import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.naming.push.UdpPushService; import com.alibaba.nacos.sys.env.EnvUtil; @@ -33,7 +26,6 @@ import org.junit.Rule; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.context.ConfigurableApplicationContext; @@ -60,15 +52,6 @@ public abstract class BaseTest { + "\"port\":9870,\"weight\":2.0,\"healthy\":true,\"enabled\":true,\"ephemeral\":true" + ",\"clusterName\":\"clusterName\",\"serviceName\":\"serviceName\",\"metadata\":{}}]"; - @Mock - public ServiceManager serviceManager; - - @Mock - public RaftPeerSet peerSet; - - @Mock - public RaftCore raftCore; - @Rule public ExpectedException expectedException = ExpectedException.none(); @@ -81,15 +64,9 @@ public abstract class BaseTest { @Spy protected SwitchDomain switchDomain; - @Mock - protected HealthCheckProcessorDelegate delegate; - @Mock protected UdpPushService pushService; - @Mock - protected UpgradeJudgement upgradeJudgement; - @Spy protected MockEnvironment environment; @@ -99,23 +76,10 @@ public abstract class BaseTest { ApplicationUtils.injectContext(context); } - protected void mockRaft() { - RaftPeer peer = new RaftPeer(); - peer.ip = NetUtils.localServer(); - raftCore.setPeerSet(peerSet); - Mockito.when(peerSet.local()).thenReturn(peer); - Mockito.when(peerSet.getLeader()).thenReturn(peer); - Mockito.when(peerSet.isLeader(NetUtils.localServer())).thenReturn(true); - } - protected void mockInjectPushServer() { doReturn(pushService).when(context).getBean(UdpPushService.class); } - protected void mockInjectHealthCheckProcessor() { - doReturn(delegate).when(context).getBean(HealthCheckProcessorDelegate.class); - } - protected void mockInjectSwitchDomain() { doReturn(switchDomain).when(context).getBean(SwitchDomain.class); } @@ -123,8 +87,4 @@ public abstract class BaseTest { protected void mockInjectDistroMapper() { doReturn(distroMapper).when(context).getBean(DistroMapper.class); } - - protected void mockInjectUpgradeJudgement() { - doReturn(upgradeJudgement).when(context).getBean(UpgradeJudgement.class); - } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/cluster/transport/JacksonSerializerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/cluster/transport/JacksonSerializerTest.java index 803162034..49c3eddf0 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/cluster/transport/JacksonSerializerTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/cluster/transport/JacksonSerializerTest.java @@ -17,14 +17,10 @@ package com.alibaba.nacos.naming.cluster.transport; import com.alibaba.nacos.common.utils.ByteUtils; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.core.Instances; +import com.alibaba.nacos.naming.misc.SwitchDomain; import org.junit.Before; import org.junit.Test; -import java.util.Map; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -32,50 +28,32 @@ public class JacksonSerializerTest { private Serializer serializer; - private Instances instances; + private SwitchDomain switchDomain; @Before public void setUp() throws Exception { serializer = new JacksonSerializer(); - instances = new Instances(); - instances.getInstanceList().add(new Instance("1.1.1.1", 1234, "cluster")); + switchDomain = new SwitchDomain(); } @Test public void testSerialize() { - String actual = new String(serializer.serialize(instances)); - assertTrue(actual.contains("\"instanceList\":[")); - assertTrue(actual.contains("\"clusterName\":\"cluster\"")); - assertTrue(actual.contains("\"ip\":\"1.1.1.1\"")); - assertTrue(actual.contains("\"port\":1234")); + String actual = new String(serializer.serialize(switchDomain)); + System.out.println(actual); + assertTrue(actual.contains("\"defaultPushCacheMillis\":10000")); + assertTrue(actual.contains("\"clientBeatInterval\":5000")); + assertTrue(actual.contains("\"defaultCacheMillis\":3000")); + assertTrue(actual.contains("\"distroEnabled\":true")); } @Test @SuppressWarnings("checkstyle:linelength") public void testDeserialize() { - String example = "{\"instanceList\":[{\"ip\":\"1.1.1.1\",\"port\":1234,\"weight\":1.0,\"healthy\":true,\"enabled\":true,\"ephemeral\":true,\"clusterName\":\"cluster\",\"metadata\":{},\"lastBeat\":1590563397264,\"marked\":false,\"instanceIdGenerator\":\"simple\",\"instanceHeartBeatInterval\":5000,\"instanceHeartBeatTimeOut\":15000,\"ipDeleteTimeout\":30000}]}"; - Instances actual = serializer.deserialize(ByteUtils.toBytes(example), Instances.class); - assertEquals(1, actual.getInstanceList().size()); - Instance actualInstance = actual.getInstanceList().get(0); - assertEquals("1.1.1.1", actualInstance.getIp()); - assertEquals("cluster", actualInstance.getClusterName()); - assertEquals(1234, actualInstance.getPort()); - } - - @Test - @SuppressWarnings("checkstyle:linelength") - public void testDeserializeMap() { - String example = "{\"datum\":{\"key\":\"instances\",\"value\":{\"instanceList\":[{\"ip\":\"1.1.1.1\",\"port\":1234,\"weight\":1.0,\"healthy\":true,\"enabled\":true,\"ephemeral\":true,\"clusterName\":\"cluster\",\"metadata\":{},\"lastBeat\":1590563397533,\"marked\":false,\"instanceIdGenerator\":\"simple\",\"instanceHeartBeatInterval\":5000,\"instanceHeartBeatTimeOut\":15000,\"ipDeleteTimeout\":30000}]},\"timestamp\":100000}}"; - Map> actual = serializer.deserializeMap(ByteUtils.toBytes(example), Instances.class); - assertEquals(actual.size(), 1); - assertTrue(actual.containsKey("datum")); - Datum actualDatum = actual.get("datum"); - assertEquals("instances", actualDatum.key); - assertEquals(100000L, actualDatum.timestamp.get()); - assertEquals(1, actualDatum.value.getInstanceList().size()); - Instance actualInstance = actualDatum.value.getInstanceList().get(0); - assertEquals("1.1.1.1", actualInstance.getIp()); - assertEquals("cluster", actualInstance.getClusterName()); - assertEquals(1234, actualInstance.getPort()); + String example = "{\"adWeightMap\":{},\"defaultPushCacheMillis\":10000,\"clientBeatInterval\":5000,\"defaultCacheMillis\":3000,\"distroThreshold\":0.7,\"healthCheckEnabled\":true,\"autoChangeHealthCheckEnabled\":true,\"distroEnabled\":true,\"enableStandalone\":true,\"pushEnabled\":true,\"checkTimes\":3,\"httpHealthParams\":{\"max\":5000,\"min\":500,\"factor\":0.85},\"tcpHealthParams\":{\"max\":5000,\"min\":1000,\"factor\":0.75},\"mysqlHealthParams\":{\"max\":3000,\"min\":2000,\"factor\":0.65},\"incrementalList\":[],\"serverStatusSynchronizationPeriodMillis\":2000,\"serviceStatusSynchronizationPeriodMillis\":5000,\"disableAddIP\":false,\"sendBeatOnly\":false,\"lightBeatEnabled\":true,\"doubleWriteEnabled\":true,\"limitedUrlMap\":{},\"distroServerExpiredMillis\":10000,\"pushGoVersion\":\"0.1.0\",\"pushJavaVersion\":\"0.1.0\",\"pushPythonVersion\":\"0.4.3\",\"pushCVersion\":\"1.0.12\",\"pushCSharpVersion\":\"0.9.0\",\"enableAuthentication\":false,\"defaultInstanceEphemeral\":true,\"healthCheckWhiteList\":[],\"name\":\"00-00---000-NACOS_SWITCH_DOMAIN-000---00-00\"}"; + SwitchDomain actual = serializer.deserialize(ByteUtils.toBytes(example), SwitchDomain.class); + assertEquals(10000, actual.getDefaultPushCacheMillis()); + assertEquals(5000, actual.getClientBeatInterval()); + assertEquals(3000, actual.getDefaultCacheMillis()); + assertTrue(actual.isDistroEnabled()); } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/consistency/DelegateConsistencyServiceImplTest.java b/naming/src/test/java/com/alibaba/nacos/naming/consistency/DelegateConsistencyServiceImplTest.java deleted file mode 100644 index 5d9095819..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/consistency/DelegateConsistencyServiceImplTest.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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.nacos.naming.consistency; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.naming.consistency.ephemeral.EphemeralConsistencyService; -import com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyServiceDelegateImpl; -import com.alibaba.nacos.naming.pojo.Record; -import junit.framework.TestCase; -import org.junit.Before; - -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class DelegateConsistencyServiceImplTest extends TestCase { - - private DelegateConsistencyServiceImpl delegateConsistencyService; - - @Mock - private PersistentConsistencyServiceDelegateImpl persistentConsistencyService; - - @Mock - private EphemeralConsistencyService ephemeralConsistencyService; - - private static final String EPHEMERAL_KEY_PREFIX = "ephemeral."; - - public static final String INSTANCE_LIST_KEY_PREFIX = "com.alibaba.nacos.naming.iplist."; - - private final String ephemeralPrefix = INSTANCE_LIST_KEY_PREFIX + EPHEMERAL_KEY_PREFIX; - - @Mock - private Record record; - - private String ephemeralKey; - - private String peristentKey; - - public DelegateConsistencyServiceImplTest() { - } - - @Before - public void setUp() { - delegateConsistencyService - = new DelegateConsistencyServiceImpl(persistentConsistencyService, ephemeralConsistencyService); - ephemeralKey = ephemeralPrefix + "test-key"; - peristentKey = "persistent-test-key"; - } - - @Test - public void testPut() throws NacosException { - delegateConsistencyService.put(ephemeralKey, record); - verify(ephemeralConsistencyService).put(ephemeralKey, record); - verify(persistentConsistencyService, never()).put(ephemeralKey, record); - - delegateConsistencyService.put(peristentKey, record); - verify(persistentConsistencyService).put(peristentKey, record); - } - - @Test - public void testRemove() throws NacosException { - delegateConsistencyService.remove(ephemeralKey); - verify(ephemeralConsistencyService).remove(ephemeralKey); - verify(persistentConsistencyService, never()).remove(ephemeralKey); - - delegateConsistencyService.remove(peristentKey); - verify(persistentConsistencyService).remove(peristentKey); - } - - @Test - public void testGet() throws NacosException { - delegateConsistencyService.get(ephemeralKey); - verify(ephemeralConsistencyService).get(ephemeralKey); - verify(persistentConsistencyService, never()).get(ephemeralKey); - - delegateConsistencyService.get(peristentKey); - verify(persistentConsistencyService).get(peristentKey); - } - - @Test - public void testListen() throws NacosException { - delegateConsistencyService.listen(ephemeralKey, null); - verify(ephemeralConsistencyService).listen(ephemeralKey, null); - verify(persistentConsistencyService, never()).listen(ephemeralKey, null); - - delegateConsistencyService.listen(peristentKey, null); - verify(persistentConsistencyService).listen(peristentKey, null); - } - - @Test - public void testUnListen() throws NacosException { - delegateConsistencyService.unListen(ephemeralKey, null); - verify(ephemeralConsistencyService).unListen(ephemeralKey, null); - verify(persistentConsistencyService, never()).unListen(ephemeralKey, null); - - delegateConsistencyService.unListen(peristentKey, null); - verify(persistentConsistencyService).unListen(peristentKey, null); - } - - @Test - public void testIsAvailable() { - delegateConsistencyService.isAvailable(); - verify(ephemeralConsistencyService).isAvailable(); - verify(persistentConsistencyService, never()).isAvailable(); - } - - @Test - public void testGetErrorMsg() { - int ephemeralCalledTimes = 3; - delegateConsistencyService.getErrorMsg(); - verify(ephemeralConsistencyService, times(ephemeralCalledTimes)).getErrorMsg(); - verify(persistentConsistencyService).getErrorMsg(); - } - -} \ No newline at end of file diff --git a/naming/src/test/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DataStoreTest.java b/naming/src/test/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DataStoreTest.java deleted file mode 100644 index df191896a..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DataStoreTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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.nacos.naming.consistency.ephemeral.distro; - -import com.alibaba.nacos.naming.consistency.Datum; -import junit.framework.TestCase; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.test.util.ReflectionTestUtils; - -import java.util.Map; - -import static org.mockito.Mockito.verify; - -@RunWith(MockitoJUnitRunner.class) -public class DataStoreTest extends TestCase { - - private DataStore dataStore; - - private String key; - - @Mock - private Datum datum; - - @Mock - private Map dataMap; - - @Before - public void setUp() { - dataStore = new DataStore(); - key = "tmp_key"; - ReflectionTestUtils.setField(dataStore, "dataMap", dataMap); - } - - @Test - public void testPut() { - dataStore.put(key, datum); - verify(dataMap).put(key, datum); - } - - @Test - public void testRemove() { - dataStore.remove(key); - verify(dataMap).remove(key); - } - - @Test - public void testKeys() { - dataStore.keys(); - verify(dataMap).keySet(); - } - - @Test - public void testGet() { - dataStore.get(key); - verify(dataMap).get(key); - } - - @Test - public void testContains() { - dataStore.contains(key); - verify(dataMap).containsKey(key); - } -} \ No newline at end of file diff --git a/naming/src/test/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DistroConsistencyServiceImplTest.java b/naming/src/test/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DistroConsistencyServiceImplTest.java deleted file mode 100644 index f26ff0415..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DistroConsistencyServiceImplTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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.nacos.naming.consistency.ephemeral.distro; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.consistency.DataOperation; -import com.alibaba.nacos.core.distributed.distro.DistroProtocol; -import com.alibaba.nacos.core.distributed.distro.entity.DistroKey; -import com.alibaba.nacos.naming.BaseTest; -import com.alibaba.nacos.naming.cluster.transport.Serializer; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.consistency.RecordListener; -import com.alibaba.nacos.naming.core.Instances; -import com.alibaba.nacos.naming.misc.GlobalConfig; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.springframework.test.util.ReflectionTestUtils; - -import java.util.Map; -import java.util.concurrent.ConcurrentLinkedQueue; - -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; - -public class DistroConsistencyServiceImplTest extends BaseTest { - - private DistroConsistencyServiceImpl distroConsistencyService; - - @Mock - private DataStore dataStore; - - @Mock - private Serializer serializer; - - @Mock - private DistroProtocol distroProtocol; - - @Mock - private GlobalConfig globalConfig; - - @Mock - private DistroConsistencyServiceImpl.Notifier notifier; - - @Mock - private RecordListener recordListener; - - private Map> listeners; - - private Instances instances; - - @Before - public void setUp() throws Exception { - distroConsistencyService = new DistroConsistencyServiceImpl(distroMapper, dataStore, serializer, switchDomain, - globalConfig, distroProtocol); - ReflectionTestUtils.setField(distroConsistencyService, "notifier", notifier); - ReflectionTestUtils.setField(distroConsistencyService, "distroProtocol", distroProtocol); - listeners = (Map>) ReflectionTestUtils - .getField(distroConsistencyService, "listeners"); - instances = new Instances(); - mockInjectUpgradeJudgement(); - } - - @After - public void tearDown() throws Exception { - } - - @Test - public void testPutWithListener() throws NacosException { - String key = KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - distroConsistencyService.listen(key, recordListener); - distroConsistencyService.put(key, instances); - verify(distroProtocol) - .sync(new DistroKey(key, KeyBuilder.INSTANCE_LIST_KEY_PREFIX), DataOperation.CHANGE, 1000L); - verify(notifier).addTask(key, DataOperation.CHANGE); - verify(dataStore).put(eq(key), any(Datum.class)); - } - - @Test - public void testPutWithoutListener() throws NacosException { - String key = KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - distroConsistencyService.put(key, instances); - verify(distroProtocol) - .sync(new DistroKey(key, KeyBuilder.INSTANCE_LIST_KEY_PREFIX), DataOperation.CHANGE, 1000L); - verify(notifier, never()).addTask(key, DataOperation.CHANGE); - verify(dataStore).put(eq(key), any(Datum.class)); - } - - @Test - public void testRemoveWithListener() throws NacosException { - String key = KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - distroConsistencyService.listen(key, recordListener); - distroConsistencyService.remove(key); - verify(dataStore).remove(key); - verify(notifier).addTask(key, DataOperation.DELETE); - assertTrue(listeners.isEmpty()); - } - - @Test - public void testRemoveWithoutListener() throws NacosException { - String key = KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - distroConsistencyService.remove(key); - verify(dataStore).remove(key); - verify(notifier, never()).addTask(key, DataOperation.DELETE); - assertTrue(listeners.isEmpty()); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/v2/DistroClientComponentRegistryTest.java b/naming/src/test/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/v2/DistroClientComponentRegistryTest.java index 285ccd1e6..6249d3840 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/v2/DistroClientComponentRegistryTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/v2/DistroClientComponentRegistryTest.java @@ -26,7 +26,6 @@ import com.alibaba.nacos.core.distributed.distro.component.DistroFailedTaskHandl import com.alibaba.nacos.core.distributed.distro.component.DistroTransportAgent; import com.alibaba.nacos.core.distributed.distro.task.DistroTaskEngineHolder; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; import junit.framework.TestCase; import org.junit.Assert; import org.junit.Before; @@ -37,55 +36,51 @@ import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class DistroClientComponentRegistryTest extends TestCase { - + private DistroClientComponentRegistry distroClientComponentRegistry; - + @Mock private ServerMemberManager serverMemberManager; - + @Mock private DistroProtocol distroProtocol; - + @Mock private DistroTaskEngineHolder taskEngineHolder; - + @Mock private ClientManagerDelegate clientManager; - + @Mock private ClusterRpcClientProxy clusterRpcClientProxy; - - @Mock - private UpgradeJudgement upgradeJudgement; - + private DistroComponentHolder componentHolder; - + @Before public void setUp() throws Exception { componentHolder = new DistroComponentHolder(); - + distroClientComponentRegistry = new DistroClientComponentRegistry(serverMemberManager, distroProtocol, - componentHolder, taskEngineHolder, - clientManager, clusterRpcClientProxy, - upgradeJudgement); + componentHolder, taskEngineHolder, clientManager, clusterRpcClientProxy); } - + @Test public void testDoRegister() { distroClientComponentRegistry.doRegister(); - + DistroDataStorage dataStorage = componentHolder.findDataStorage(DistroClientDataProcessor.TYPE); Assert.assertNotNull(dataStorage); - + DistroDataProcessor dataProcessor = componentHolder.findDataProcessor(DistroClientDataProcessor.TYPE); Assert.assertNotNull(dataProcessor); - - DistroFailedTaskHandler failedTaskHandler = componentHolder.findFailedTaskHandler(DistroClientDataProcessor.TYPE); + + DistroFailedTaskHandler failedTaskHandler = componentHolder + .findFailedTaskHandler(DistroClientDataProcessor.TYPE); Assert.assertNotNull(failedTaskHandler); - + DistroTransportAgent transportAgent = componentHolder.findTransportAgent(DistroClientDataProcessor.TYPE); Assert.assertNotNull(transportAgent); - + } - -} \ No newline at end of file + +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/consistency/persistent/ClusterVersionJudgementTest.java b/naming/src/test/java/com/alibaba/nacos/naming/consistency/persistent/ClusterVersionJudgementTest.java deleted file mode 100644 index 16cc9f828..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/consistency/persistent/ClusterVersionJudgementTest.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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.nacos.naming.consistency.persistent; - -import com.alibaba.nacos.core.cluster.Member; -import com.alibaba.nacos.core.cluster.MemberMetaDataConstants; -import com.alibaba.nacos.core.cluster.NodeState; -import com.alibaba.nacos.core.cluster.ServerMemberManager; -import com.alibaba.nacos.sys.env.EnvUtil; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.springframework.mock.env.MockEnvironment; -import org.springframework.mock.web.MockServletContext; - -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -public class ClusterVersionJudgementTest { - - private ServerMemberManager manager; - - private String[] ipList; - - private final int ipCount = 3; - - private final String ip1 = "1.1.1.1"; - - private final String ip2 = "2.2.2.2"; - - private final String ip3 = "3.3.3.3"; - - private final int defalutPort = 80; - - private final String newVersion = "1.4.0"; - - private final String oldVersion = "1.3.0"; - - private List members; - - private Map newVersionMeta; - - private Map oldVersionMeta; - - private ClusterVersionJudgement judgement; - - public ClusterVersionJudgementTest() { - } - - @BeforeClass - public static void beforeClass() { - EnvUtil.setEnvironment(new MockEnvironment()); - } - - @Before - public void beforeMethod() throws Exception { - manager = new ServerMemberManager(new MockServletContext()); - newVersionMeta = new HashMap<>(4); - newVersionMeta.put(MemberMetaDataConstants.VERSION, newVersion); - oldVersionMeta = new HashMap<>(4); - oldVersionMeta.put(MemberMetaDataConstants.VERSION, oldVersion); - ipList = new String[ipCount]; - ipList[0] = ip1; - ipList[1] = ip2; - ipList[2] = ip3; - members = new LinkedList<>(); - members.add(Member.builder().ip(ipList[0]).port(defalutPort).state(NodeState.UP).build()); - members.add(Member.builder().ip(ipList[1]).port(defalutPort).state(NodeState.UP).build()); - members.add(Member.builder().ip(ipList[2]).port(defalutPort).state(NodeState.UP).build()); - manager.memberJoin(members); - } - - @After - public void afterMethod() throws Exception { - manager.shutdown(); - manager = null; - } - - /** - * The member node has version information greater than 1.4.0 - */ - @Test - public void testAllMemberIsNewVersion() { - Collection allMembers = manager.allMembers(); - allMembers.forEach(member -> member.setExtendInfo(newVersionMeta)); - judgement = new ClusterVersionJudgement(manager); - judgement.judge(); - Assert.assertTrue(judgement.allMemberIsNewVersion()); - } - - @Test - public void testPartMemberIsNewVersion() { - Collection allMembers = manager.allMembers(); - AtomicInteger count = new AtomicInteger(); - allMembers.forEach(member -> { - if (count.get() == 0) { - member.setExtendInfo(oldVersionMeta); - } else { - count.incrementAndGet(); - member.setExtendInfo(newVersionMeta); - } - }); - judgement = new ClusterVersionJudgement(manager); - judgement.judge(); - Assert.assertFalse(judgement.allMemberIsNewVersion()); - } - - @Test - public void testPartMemberUpdateToNewVersion() { - // Firstly, make a cluster with a part of new version servers. - Collection allMembers = manager.allMembers(); - AtomicInteger count = new AtomicInteger(); - allMembers.forEach(member -> { - if (count.get() == 0) { - member.setExtendInfo(oldVersionMeta); - } else { - count.incrementAndGet(); - member.setExtendInfo(newVersionMeta); - } - }); - judgement = new ClusterVersionJudgement(manager); - judgement.judge(); - Assert.assertFalse(judgement.allMemberIsNewVersion()); - // Secondly, make all in the cluster to be new version servers. - allMembers.forEach(member -> member.setExtendInfo(newVersionMeta)); - judgement = new ClusterVersionJudgement(manager); - judgement.judge(); - Assert.assertTrue(judgement.allMemberIsNewVersion()); - } - -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/consistency/persistent/PersistentConsistencyServiceDelegateImplTest.java b/naming/src/test/java/com/alibaba/nacos/naming/consistency/persistent/PersistentConsistencyServiceDelegateImplTest.java deleted file mode 100644 index 17d180e32..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/consistency/persistent/PersistentConsistencyServiceDelegateImplTest.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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.nacos.naming.consistency.persistent; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.core.distributed.ProtocolManager; -import com.alibaba.nacos.naming.consistency.RecordListener; -import com.alibaba.nacos.naming.consistency.persistent.impl.BasePersistentServiceProcessor; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftConsistencyServiceImpl; -import com.alibaba.nacos.naming.pojo.Record; -import com.alibaba.nacos.sys.env.Constants; -import com.alibaba.nacos.sys.env.EnvUtil; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.mock.env.MockEnvironment; - -import java.lang.reflect.Field; - -@RunWith(MockitoJUnitRunner.class) -public class PersistentConsistencyServiceDelegateImplTest { - - @Mock - private ClusterVersionJudgement clusterVersionJudgement; - - @Mock - private RaftConsistencyServiceImpl raftConsistencyService; - - @Mock - private ProtocolManager protocolManager; - - @Mock - private Record record; - - @Mock - private RecordListener recordListener; - - @Mock - private BasePersistentServiceProcessor basePersistentServiceProcessor; - - private PersistentConsistencyServiceDelegateImpl oldPersistentConsistencyServiceDelegate; - - private PersistentConsistencyServiceDelegateImpl newPersistentConsistencyServiceDelegate; - - @Before - public void setUp() throws Exception { - MockEnvironment environment = new MockEnvironment(); - environment.setProperty(Constants.SUPPORT_UPGRADE_FROM_1X, "true"); - EnvUtil.setEnvironment(environment); - EnvUtil.setIsStandalone(true); - oldPersistentConsistencyServiceDelegate = new PersistentConsistencyServiceDelegateImpl(clusterVersionJudgement, - raftConsistencyService, protocolManager); - - newPersistentConsistencyServiceDelegate = new PersistentConsistencyServiceDelegateImpl(clusterVersionJudgement, - raftConsistencyService, protocolManager); - Class persistentConsistencyServiceDelegateClass = PersistentConsistencyServiceDelegateImpl.class; - Field switchNewPersistentService = persistentConsistencyServiceDelegateClass - .getDeclaredField("switchNewPersistentService"); - switchNewPersistentService.setAccessible(true); - switchNewPersistentService.set(newPersistentConsistencyServiceDelegate, true); - - Field newPersistentConsistencyService = persistentConsistencyServiceDelegateClass - .getDeclaredField("newPersistentConsistencyService"); - newPersistentConsistencyService.setAccessible(true); - newPersistentConsistencyService.set(newPersistentConsistencyServiceDelegate, basePersistentServiceProcessor); - } - - @Test() - public void testPut() throws Exception { - String key = "record_key"; - oldPersistentConsistencyServiceDelegate.put(key, record); - Mockito.verify(raftConsistencyService).put(key, record); - - newPersistentConsistencyServiceDelegate.put(key, record); - Mockito.verify(basePersistentServiceProcessor).put(key, record); - } - - @Test - public void testRemove() throws NacosException { - String key = "record_key"; - oldPersistentConsistencyServiceDelegate.remove(key); - Mockito.verify(raftConsistencyService).remove(key); - - newPersistentConsistencyServiceDelegate.remove(key); - Mockito.verify(basePersistentServiceProcessor).remove(key); - } - - @Test() - public void testGet() throws NacosException { - String key = "record_key"; - oldPersistentConsistencyServiceDelegate.get(key); - Mockito.verify(raftConsistencyService).get(key); - - newPersistentConsistencyServiceDelegate.get(key); - Mockito.verify(basePersistentServiceProcessor).get(key); - } - - @Test - public void testListen() throws NacosException { - String key = "listen_key"; - oldPersistentConsistencyServiceDelegate.listen(key, recordListener); - Mockito.verify(raftConsistencyService).listen(key, recordListener); - - newPersistentConsistencyServiceDelegate.listen(key, recordListener); - Mockito.verify(basePersistentServiceProcessor).listen(key, recordListener); - } - - @Test - public void testUnListen() throws NacosException { - String key = "listen_key"; - oldPersistentConsistencyServiceDelegate.unListen(key, recordListener); - Mockito.verify(raftConsistencyService).unListen(key, recordListener); - - newPersistentConsistencyServiceDelegate.unListen(key, recordListener); - Mockito.verify(basePersistentServiceProcessor).unListen(key, recordListener); - } - - @Test - public void testIsAvailable() { - oldPersistentConsistencyServiceDelegate.isAvailable(); - Mockito.verify(raftConsistencyService).isAvailable(); - - newPersistentConsistencyServiceDelegate.isAvailable(); - Mockito.verify(basePersistentServiceProcessor).isAvailable(); - } - - @Test - public void testGetErrorMsg() { - oldPersistentConsistencyServiceDelegate.getErrorMsg(); - Mockito.verify(raftConsistencyService).getErrorMsg(); - - newPersistentConsistencyServiceDelegate.getErrorMsg(); - Mockito.verify(basePersistentServiceProcessor).getErrorMsg(); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftPeerSetTest.java b/naming/src/test/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftPeerSetTest.java deleted file mode 100644 index d60c448c5..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftPeerSetTest.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.nacos.naming.consistency.persistent.raft; - -import com.alibaba.nacos.core.cluster.Member; -import com.alibaba.nacos.core.cluster.ServerMemberManager; -import com.alibaba.nacos.sys.env.EnvUtil; -import com.alibaba.nacos.sys.utils.ApplicationUtils; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.springframework.context.support.StaticApplicationContext; -import org.springframework.mock.env.MockEnvironment; -import org.springframework.mock.web.MockServletContext; - -import java.util.Arrays; -import java.util.Collection; -import java.util.concurrent.atomic.AtomicBoolean; - -public class RaftPeerSetTest { - - @BeforeClass - public static void beforeClass() { - ApplicationUtils.injectContext(new StaticApplicationContext()); - EnvUtil.setEnvironment(new MockEnvironment()); - } - - private ServerMemberManager memberManager; - - @Before - public void before() throws Exception { - memberManager = new ServerMemberManager(new MockServletContext()); - } - - @Test - public void testRaftPeerChange() { - final AtomicBoolean notifyReceived = new AtomicBoolean(false); - RaftPeerSetCopy peerSetCopy = new RaftPeerSetCopy(memberManager, () -> { - notifyReceived.set(true); - }); - - Collection firstEvent = Arrays.asList( - Member.builder().ip("127.0.0.1").port(80).build(), - Member.builder().ip("127.0.0.2").port(81).build(), - Member.builder().ip("127.0.0.3").port(82).build()); - - peerSetCopy.changePeers(firstEvent); - Assert.assertTrue(notifyReceived.get()); - notifyReceived.set(false); - - Collection secondEvent = Arrays.asList( - Member.builder().ip("127.0.0.1").port(80).build(), - Member.builder().ip("127.0.0.2").port(81).build(), - Member.builder().ip("127.0.0.3").port(82).build(), - Member.builder().ip("127.0.0.4").port(83).build()); - - peerSetCopy.changePeers(secondEvent); - Assert.assertTrue(notifyReceived.get()); - notifyReceived.set(false); - - Collection thirdEvent = Arrays.asList( - Member.builder().ip("127.0.0.1").port(80).build(), - Member.builder().ip("127.0.0.2").port(81).build(), - Member.builder().ip("127.0.0.5").port(82).build()); - - peerSetCopy.changePeers(thirdEvent); - Assert.assertTrue(notifyReceived.get()); - notifyReceived.set(false); - - Collection fourEvent = Arrays.asList( - Member.builder().ip("127.0.0.1").port(80).build(), - Member.builder().ip("127.0.0.2").port(81).build()); - - peerSetCopy.changePeers(fourEvent); - Assert.assertTrue(notifyReceived.get()); - notifyReceived.set(false); - - Collection fiveEvent = Arrays.asList( - Member.builder().ip("127.0.0.1").port(80).build(), - Member.builder().ip("127.0.0.3").port(81).build()); - - peerSetCopy.changePeers(fiveEvent); - Assert.assertTrue(notifyReceived.get()); - notifyReceived.set(false); - } - - private static class RaftPeerSetCopy extends RaftPeerSet { - - private final Runnable runnable; - - public RaftPeerSetCopy(ServerMemberManager memberManager, Runnable callback) { - super(memberManager); - this.runnable = callback; - } - - @Override - protected void changePeers(Collection members) { - this.runnable.run(); - } - } - -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/CatalogControllerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/CatalogControllerTest.java index 9ff9ddbff..9ced04c69 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/controllers/CatalogControllerTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/CatalogControllerTest.java @@ -18,112 +18,65 @@ package com.alibaba.nacos.naming.controllers; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.CommonParams; -import com.alibaba.nacos.naming.core.CatalogServiceV1Impl; -import com.alibaba.nacos.naming.core.Cluster; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import com.alibaba.nacos.naming.healthcheck.HealthCheckTask; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.naming.core.CatalogServiceV2Impl; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.util.ReflectionTestUtils; +import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class CatalogControllerTest { - @Mock - private ServiceManager serviceManager; - - @Mock - protected UpgradeJudgement upgradeJudgement; - - private CatalogServiceV1Impl catalogServiceV1; - - private CatalogController catalogController; - - private Service service; - - private Cluster cluster; - - @Mock - private HealthCheckTask healthCheckTask; - - @Before - public void setUp() throws NoSuchFieldException, IllegalAccessException, NacosException { - catalogController = new CatalogController(); - catalogServiceV1 = new CatalogServiceV1Impl(serviceManager); - ReflectionTestUtils.setField(catalogController, "serviceManager", serviceManager); - ReflectionTestUtils.setField(catalogController, "upgradeJudgement", upgradeJudgement); - ReflectionTestUtils.setField(catalogController, "catalogServiceV1", catalogServiceV1); - service = new Service(TEST_SERVICE_NAME); - service.setNamespaceId(Constants.DEFAULT_NAMESPACE_ID); - service.setProtectThreshold(12.34f); - service.setGroupName(TEST_GROUP_NAME); - cluster = new Cluster(TEST_CLUSTER_NAME, service); - cluster.setDefaultPort(1); - Mockito.when(healthCheckTask.getCheckRtBest()).thenReturn(1L); - Mockito.when(healthCheckTask.getCheckRtWorst()).thenReturn(1L); - ReflectionTestUtils.setField(cluster, "checkTask", healthCheckTask); - service.addCluster(cluster); - when(serviceManager.getService(Constants.DEFAULT_NAMESPACE_ID, - TEST_GROUP_NAME + Constants.SERVICE_INFO_SPLITER + TEST_SERVICE_NAME)).thenReturn(service); - doCallRealMethod().when(serviceManager).checkServiceIsNull(eq(null), anyString(), anyString()); - } - - @Test - public void testServiceDetail() throws Exception { - Object result = catalogController.serviceDetail(Constants.DEFAULT_NAMESPACE_ID, - TEST_GROUP_NAME + Constants.SERVICE_INFO_SPLITER + TEST_SERVICE_NAME); - String actual = result.toString(); - assertTrue(actual.contains("\"service\":{")); - assertTrue(actual.contains("\"groupName\":\"test-group-name\"")); - assertTrue(actual.contains("\"metadata\":{}")); - assertTrue(actual.contains("\"name\":\"test-service\"")); - assertTrue(actual.contains("\"selector\":{\"type\":\"none\",\"contextType\":\"NONE\"}")); - assertTrue(actual.contains("\"protectThreshold\":12.34")); - assertTrue(actual.contains("\"clusters\":[{")); - assertTrue(actual.contains("\"defaultCheckPort\":80")); - assertTrue(actual.contains("\"defaultPort\":1")); - assertTrue(actual.contains("\"healthChecker\":{\"type\":\"TCP\"}")); - assertTrue(actual.contains("\"metadata\":{}")); - assertTrue(actual.contains("\"name\":\"test-cluster\"")); - assertTrue(actual.contains("\"serviceName\":\"test-service\"")); - assertTrue(actual.contains("\"useIPPort4Check\":true")); - } - - @Test(expected = NacosException.class) - public void testServiceDetailNotFound() throws Exception { - catalogController.serviceDetail(Constants.DEFAULT_NAMESPACE_ID, TEST_SERVICE_NAME); - } - private static final String TEST_CLUSTER_NAME = "test-cluster"; private static final String TEST_SERVICE_NAME = "test-service"; private static final String TEST_GROUP_NAME = "test-group-name"; + @Mock + private CatalogServiceV2Impl catalogServiceV2; + + @InjectMocks + private CatalogController catalogController; + + @Before + public void setUp() throws NoSuchFieldException, IllegalAccessException, NacosException { + } + + @Test + public void testServiceDetail() throws Exception { + Object expected = new Object(); + when(catalogServiceV2.getServiceDetail(Constants.DEFAULT_NAMESPACE_ID, TEST_GROUP_NAME, TEST_SERVICE_NAME)) + .thenReturn(expected); + Object actual = catalogController.serviceDetail(Constants.DEFAULT_NAMESPACE_ID, + TEST_GROUP_NAME + Constants.SERVICE_INFO_SPLITER + TEST_SERVICE_NAME); + assertEquals(expected, actual); + } + @Test public void testInstanceList() throws NacosException { - Instance instance = new Instance("1.1.1.1", 1234, TEST_CLUSTER_NAME); - cluster.updateIps(Collections.singletonList(instance), false); + Instance instance = new Instance(); + instance.setIp("1.1.1.1"); + instance.setPort(1234); + instance.setClusterName(TEST_CLUSTER_NAME); + List list = new ArrayList<>(1); + list.add(instance); + when(catalogServiceV2 + .listInstances(Constants.DEFAULT_NAMESPACE_ID, TEST_GROUP_NAME, TEST_SERVICE_NAME, TEST_CLUSTER_NAME)) + .thenReturn(list); ObjectNode result = catalogController.instanceList(Constants.DEFAULT_NAMESPACE_ID, TEST_GROUP_NAME + Constants.SERVICE_INFO_SPLITER + TEST_SERVICE_NAME, TEST_CLUSTER_NAME, 1, 10); String actual = result.toString(); @@ -137,8 +90,12 @@ public class CatalogControllerTest { @Test public void testListDetail() { try { - Object res = catalogController.listDetail(true, Constants.DEFAULT_NAMESPACE_ID, 1, 10, - TEST_GROUP_NAME + Constants.SERVICE_INFO_SPLITER + TEST_SERVICE_NAME, TEST_GROUP_NAME, null, true); + when(catalogServiceV2 + .pageListServiceDetail(Constants.DEFAULT_NAMESPACE_ID, TEST_GROUP_NAME, TEST_SERVICE_NAME, 1, 10)) + .thenReturn(Collections.emptyList()); + Object res = catalogController + .listDetail(true, Constants.DEFAULT_NAMESPACE_ID, 1, 10, TEST_SERVICE_NAME, TEST_GROUP_NAME, null, + true); Assert.assertTrue(res instanceof List); Assert.assertEquals(0, ((List) res).size()); } catch (NacosException e) { @@ -146,18 +103,4 @@ public class CatalogControllerTest { Assert.fail(e.getMessage()); } } - - @Test - public void testRt4Service() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addParameter(CommonParams.SERVICE_NAME, TEST_GROUP_NAME + Constants.SERVICE_INFO_SPLITER + TEST_SERVICE_NAME); - try { - ObjectNode objectNode = catalogController.rt4Service(request); - String result = objectNode.toString(); - assertTrue(result.contains("\"checkRTWorst\":1")); - } catch (NacosException e) { - e.printStackTrace(); - Assert.fail(e.getMessage()); - } - } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/ClusterControllerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/ClusterControllerTest.java index 452320f23..bc012caf6 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/controllers/ClusterControllerTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/ClusterControllerTest.java @@ -16,132 +16,56 @@ package com.alibaba.nacos.naming.controllers; -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.CommonParams; import com.alibaba.nacos.naming.BaseTest; -import com.alibaba.nacos.naming.core.Cluster; -import com.alibaba.nacos.naming.core.ClusterOperatorV1Impl; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import org.junit.Assert; +import com.alibaba.nacos.naming.core.ClusterOperatorV2Impl; +import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.springframework.mock.web.MockServletContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.web.WebAppConfiguration; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; -import static org.hamcrest.CoreMatchers.isA; -import static org.mockito.ArgumentMatchers.anyString; +import javax.servlet.http.HttpServletRequest; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = MockServletContext.class) -@WebAppConfiguration +@RunWith(MockitoJUnitRunner.class) public class ClusterControllerTest extends BaseTest { - @InjectMocks + @Mock + private ClusterOperatorV2Impl clusterOperatorV2; + + @Mock + private HttpServletRequest request; + private ClusterController clusterController; - @InjectMocks - private ClusterOperatorV1Impl clusterOperatorV1; - - private MockMvc mockmvc; - @Before public void before() { super.before(); - mockInjectSwitchDomain(); - mockInjectDistroMapper(); - mockmvc = MockMvcBuilders.standaloneSetup(clusterController).build(); - ReflectionTestUtils.setField(clusterController, "upgradeJudgement", upgradeJudgement); - ReflectionTestUtils.setField(clusterController, "clusterOperatorV1", clusterOperatorV1); - try { - doCallRealMethod().when(serviceManager).checkServiceIsNull(eq(null), anyString(), anyString()); - } catch (NacosException e) { - e.printStackTrace(); - } + clusterController = new ClusterController(clusterOperatorV2); } @Test public void testUpdate() throws Exception { - Service service = new Service(TEST_SERVICE_NAME); - service.setNamespaceId("test-namespace"); - when(serviceManager.getService(Constants.DEFAULT_NAMESPACE_ID, TEST_SERVICE_NAME)).thenReturn(service); - - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders - .put(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/cluster").param("clusterName", TEST_CLUSTER_NAME) - .param("serviceName", TEST_SERVICE_NAME).param("healthChecker", "{\"type\":\"HTTP\"}") - .param("checkPort", "1").param("useInstancePort4Check", "true"); - Assert.assertEquals("ok", mockmvc.perform(builder).andReturn().getResponse().getContentAsString()); - - Cluster expectedCluster = new Cluster(TEST_CLUSTER_NAME, service); - Cluster actualCluster = service.getClusterMap().get(TEST_CLUSTER_NAME); - - Assert.assertEquals(expectedCluster, actualCluster); - Assert.assertEquals(1, actualCluster.getDefCkport()); - Assert.assertTrue(actualCluster.isUseIPPort4Check()); + mockRequestParameter(CommonParams.NAMESPACE_ID, "test-namespace"); + mockRequestParameter(CommonParams.CLUSTER_NAME, TEST_CLUSTER_NAME); + mockRequestParameter(CommonParams.SERVICE_NAME, TEST_SERVICE_NAME); + mockRequestParameter("checkPort", "1"); + mockRequestParameter("useInstancePort4Check", "true"); + mockRequestParameter("healthChecker", "{\"type\":\"HTTP\"}"); + assertEquals("ok", clusterController.update(request)); + verify(clusterOperatorV2) + .updateClusterMetadata(eq("test-namespace"), eq(TEST_SERVICE_NAME), eq(TEST_CLUSTER_NAME), + any(ClusterMetadata.class)); } - @Test - public void testUpdateNoService() throws Exception { - expectedException.expectCause(isA(NacosException.class)); - expectedException.expectMessage("service not found, namespace: public, serviceName: test-service-not-found"); - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders - .put(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/cluster").param("clusterName", TEST_CLUSTER_NAME) - .param("serviceName", "test-service-not-found").param("healthChecker", "{\"type\":\"HTTP\"}") - .param("checkPort", "1").param("useInstancePort4Check", "true"); - mockmvc.perform(builder); + private void mockRequestParameter(String paramKey, String value) { + when(request.getParameter(paramKey)).thenReturn(value); } - - @Test - public void testUpdateHealthCheckerType() throws Exception { - - Service service = new Service(TEST_SERVICE_NAME); - service.setNamespaceId(Constants.DEFAULT_NAMESPACE_ID); - when(serviceManager.getService(Constants.DEFAULT_NAMESPACE_ID, TEST_SERVICE_NAME)).thenReturn(service); - - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders - .put(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/cluster").param("clusterName", TEST_CLUSTER_NAME) - .param("serviceName", TEST_SERVICE_NAME).param("healthChecker", "{\"type\":\"123\"}") - .param("checkPort", "1").param("useInstancePort4Check", "true"); - mockmvc.perform(builder); - - Assert.assertEquals("NONE", service.getClusterMap().get(TEST_CLUSTER_NAME).getHealthChecker().getType()); - - MockHttpServletRequestBuilder builder2 = MockMvcRequestBuilders - .put(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/cluster").param("clusterName", TEST_CLUSTER_NAME) - .param("serviceName", TEST_SERVICE_NAME).param("healthChecker", "{\"type\":\"TCP\"}") - .param("checkPort", "1").param("useInstancePort4Check", "true"); - mockmvc.perform(builder2); - - Assert.assertEquals("TCP", service.getClusterMap().get(TEST_CLUSTER_NAME).getHealthChecker().getType()); - - MockHttpServletRequestBuilder builder3 = MockMvcRequestBuilders - .put(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/cluster").param("clusterName", TEST_CLUSTER_NAME) - .param("serviceName", TEST_SERVICE_NAME).param("healthChecker", "{\"type\":\"HTTP\"}") - .param("checkPort", "1").param("useInstancePort4Check", "true"); - mockmvc.perform(builder3); - - Assert.assertEquals("HTTP", service.getClusterMap().get(TEST_CLUSTER_NAME).getHealthChecker().getType()); - - MockHttpServletRequestBuilder builder4 = MockMvcRequestBuilders - .put(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/cluster").param("clusterName", TEST_CLUSTER_NAME) - .param("serviceName", TEST_SERVICE_NAME).param("healthChecker", "{\"type\":\"MYSQL\"}") - .param("checkPort", "1").param("useInstancePort4Check", "true"); - mockmvc.perform(builder4); - - Assert.assertEquals("MYSQL", service.getClusterMap().get(TEST_CLUSTER_NAME).getHealthChecker().getType()); - - } - } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/DistroControllerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/DistroControllerTest.java deleted file mode 100644 index fe518dbcb..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/controllers/DistroControllerTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 1999-2021 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.nacos.naming.controllers; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.core.distributed.distro.DistroProtocol; -import com.alibaba.nacos.core.distributed.distro.entity.DistroData; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.core.Instances; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.http.ResponseEntity; - -import java.util.HashMap; -import java.util.Map; - -/** - * {@link DistroController} unit test. - * - * @author chenglu - * @date 2021-07-21 19:06 - */ -@RunWith(MockitoJUnitRunner.class) -public class DistroControllerTest { - - @InjectMocks - private DistroController distroController; - - @Mock - private DistroProtocol distroProtocol; - - @Mock - private ServiceManager serviceManager; - - @Mock - private SwitchDomain switchDomain; - - @Test - public void testOnSyncDatum() { - Map> dataMap = new HashMap<>(); - try { - distroController.onSyncDatum(dataMap); - } catch (Exception e) { - Assert.assertTrue(e instanceof NacosException); - NacosException exception = (NacosException) e; - Assert.assertEquals(NacosException.INVALID_PARAM, exception.getErrCode()); - } - - dataMap.put("test", new Datum<>()); - try { - ResponseEntity responseEntity = distroController.onSyncDatum(dataMap); - Assert.assertEquals("ok", responseEntity.getBody()); - } catch (Exception e) { - e.printStackTrace(); - Assert.fail(e.getMessage()); - } - } - - @Test - public void testSyncChecksum() { - Mockito.when(distroProtocol.onVerify(Mockito.any(), Mockito.anyString())).thenReturn(true); - - ResponseEntity responseEntity = distroController.syncChecksum("test", new HashMap<>()); - Assert.assertEquals("ok", responseEntity.getBody()); - } - - @Test - public void testGet() { - try { - Mockito.when(distroProtocol.onQuery(Mockito.any())).thenReturn(new DistroData(null, "content".getBytes())); - - ResponseEntity responseEntity = distroController.get("{\"keys\":[\"12\", \"33\"]}"); - Assert.assertArrayEquals("content".getBytes(), responseEntity.getBody()); - } catch (Exception e) { - e.printStackTrace(); - Assert.fail(e.getMessage()); - } - } - - @Test - public void testGetAllDatums() { - Mockito.when(distroProtocol.onSnapshot(Mockito.any())).thenReturn(new DistroData(null, "content".getBytes())); - - ResponseEntity responseEntity = distroController.getAllDatums(); - Assert.assertArrayEquals("content".getBytes(), responseEntity.getBody()); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/HealthControllerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/HealthControllerTest.java index 0c801dbb8..d069e14f7 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/controllers/HealthControllerTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/HealthControllerTest.java @@ -20,9 +20,7 @@ package com.alibaba.nacos.naming.controllers; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.CommonParams; import com.alibaba.nacos.naming.constants.RequestConstant; -import com.alibaba.nacos.naming.core.HealthOperatorV1Impl; import com.alibaba.nacos.naming.core.HealthOperatorV2Impl; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; import com.alibaba.nacos.sys.env.EnvUtil; import org.junit.Assert; import org.junit.Before; @@ -30,7 +28,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.http.ResponseEntity; import org.springframework.mock.env.MockEnvironment; @@ -48,15 +45,9 @@ public class HealthControllerTest { @InjectMocks private HealthController healthController; - @Mock - private HealthOperatorV1Impl healthOperatorV1; - @Mock private HealthOperatorV2Impl healthOperatorV2; - @Mock - private UpgradeJudgement upgradeJudgement; - @Before public void setUp() { EnvUtil.setEnvironment(new MockEnvironment()); @@ -75,8 +66,6 @@ public class HealthControllerTest { servletRequest.addParameter(RequestConstant.IP_KEY, "1.1.1.1"); servletRequest.addParameter(RequestConstant.PORT_KEY, "8848"); servletRequest.addParameter(RequestConstant.HEALTHY_KEY, "true"); - - Mockito.when(upgradeJudgement.isUseGrpcFeatures()).thenReturn(true); try { ResponseEntity responseEntity = healthController.update(servletRequest); diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/InstanceControllerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/InstanceControllerTest.java index f8be12ccd..99e1a463a 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/controllers/InstanceControllerTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/InstanceControllerTest.java @@ -17,261 +17,206 @@ package com.alibaba.nacos.naming.controllers; import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.common.constant.HttpHeaderConsts; +import com.alibaba.nacos.api.naming.CommonParams; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.notify.listener.SmartSubscriber; +import com.alibaba.nacos.common.trace.event.naming.DeregisterInstanceTraceEvent; +import com.alibaba.nacos.common.trace.event.naming.RegisterInstanceTraceEvent; import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.naming.BaseTest; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeerSet; -import com.alibaba.nacos.naming.core.Cluster; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.core.InstanceOperatorServiceImpl; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteEventListener; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.execute.InstanceUpgradeHelper; +import com.alibaba.nacos.naming.core.InstanceOperatorClientImpl; +import com.alibaba.nacos.naming.core.InstancePatchObject; import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.naming.pojo.InstanceOperationInfo; -import com.fasterxml.jackson.databind.JsonNode; -import org.junit.Assert; +import com.alibaba.nacos.naming.pojo.Subscriber; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.mock.web.MockServletContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.web.WebAppConfiguration; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.mockito.junit.MockitoJUnitRunner; -import java.util.ArrayList; -import java.util.HashMap; +import javax.servlet.http.HttpServletRequest; +import java.util.Collections; import java.util.LinkedList; import java.util.List; -import java.util.Map; -import java.util.function.Function; +import java.util.concurrent.TimeUnit; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = MockServletContext.class) -@WebAppConfiguration +@RunWith(MockitoJUnitRunner.class) public class InstanceControllerTest extends BaseTest { + @Mock + private InstanceOperatorClientImpl instanceServiceV2; + + @Mock + private HttpServletRequest request; + + private SmartSubscriber subscriber; + + private volatile boolean eventReceived = false; + @InjectMocks private InstanceController instanceController; - @InjectMocks - private InstanceOperatorServiceImpl instanceOperatorService; - - @Mock - private DoubleWriteEventListener doubleWriteEventListener; - - @Mock - private InstanceUpgradeHelper instanceUpgradeHelper; - - @Mock - private RaftPeerSet peerSet; - - private MockMvc mockmvc; - @Before public void before() { super.before(); - mockInjectPushServer(); - ReflectionTestUtils.setField(instanceController, "upgradeJudgement", upgradeJudgement); - ReflectionTestUtils.setField(instanceController, "instanceServiceV1", instanceOperatorService); - when(context.getBean(DoubleWriteEventListener.class)).thenReturn(doubleWriteEventListener); - when(instanceUpgradeHelper.toV1(any())).thenReturn(new Instance("1.1.1.1", 9999)); - mockmvc = MockMvcBuilders.standaloneSetup(instanceController).build(); + when(switchDomain.isDefaultInstanceEphemeral()).thenReturn(true); + subscriber = new SmartSubscriber() { + @Override + public List> subscribeTypes() { + List> result = new LinkedList<>(); + result.add(RegisterInstanceTraceEvent.class); + result.add(DeregisterInstanceTraceEvent.class); + return result; + } + + @Override + public void onEvent(Event event) { + eventReceived = true; + } + }; + NotifyCenter.registerSubscriber(subscriber); + mockRequestParameter(CommonParams.SERVICE_NAME, TEST_GROUP_NAME + "@@" + TEST_SERVICE_NAME); + mockRequestParameter("ip", "1.1.1.1"); + mockRequestParameter("port", "3306"); + } + + @After + public void tearDown() throws Exception { + NotifyCenter.deregisterSubscriber(subscriber); + NotifyCenter.deregisterPublisher(RegisterInstanceTraceEvent.class); + NotifyCenter.deregisterPublisher(DeregisterInstanceTraceEvent.class); + eventReceived = false; + } + + private void mockRequestParameter(String key, String value) { + when(request.getParameter(key)).thenReturn(value); } @Test - public void registerInstance() throws Exception { - - Service service = new Service(); - service.setName(TEST_SERVICE_NAME); - - Cluster cluster = new Cluster(UtilsAndCommons.DEFAULT_CLUSTER_NAME, service); - service.addCluster(cluster); - + public void testRegister() throws Exception { + assertEquals("ok", instanceController.register(request)); + verify(instanceServiceV2) + .registerInstance(eq(Constants.DEFAULT_NAMESPACE_ID), eq(TEST_GROUP_NAME + "@@" + TEST_SERVICE_NAME), + any(Instance.class)); + TimeUnit.SECONDS.sleep(1); + assertTrue(eventReceived); + } + + @Test + public void testDeregister() throws Exception { + assertEquals("ok", instanceController.deregister(request)); + verify(instanceServiceV2) + .removeInstance(eq(Constants.DEFAULT_NAMESPACE_ID), eq(TEST_GROUP_NAME + "@@" + TEST_SERVICE_NAME), + any(Instance.class)); + TimeUnit.SECONDS.sleep(1); + assertTrue(eventReceived); + } + + @Test + public void testUpdate() throws Exception { + assertEquals("ok", instanceController.update(request)); + verify(instanceServiceV2) + .updateInstance(eq(Constants.DEFAULT_NAMESPACE_ID), eq(TEST_GROUP_NAME + "@@" + TEST_SERVICE_NAME), + any(Instance.class)); + } + + @Test + public void testBatchUpdateInstanceMetadata() throws Exception { Instance instance = new Instance(); instance.setIp("1.1.1.1"); - instance.setPort(9999); - List ipList = new ArrayList<>(); - ipList.add(instance); - service.updateIPs(ipList, false); - - when(serviceManager.getService(Constants.DEFAULT_NAMESPACE_ID, TEST_SERVICE_NAME)).thenReturn(service); - - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders - .post(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/instance").param("serviceName", TEST_SERVICE_NAME) - .param("ip", "1.1.1.1").param("port", "9999"); - String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); - - Assert.assertEquals("ok", actualValue); + instance.setPort(3306); + List mockInstance = Collections.singletonList(instance); + String instanceJson = JacksonUtils.toJson(mockInstance); + mockRequestParameter("instances", instanceJson); + mockRequestParameter("metadata", "{}"); + when(instanceServiceV2.batchUpdateMetadata(eq(Constants.DEFAULT_NAMESPACE_ID), any(), anyMap())) + .thenReturn(Collections.singletonList("1.1.1.1:3306:unknown:DEFAULT:ephemeral")); + ObjectNode actual = instanceController.batchUpdateInstanceMetadata(request); + assertEquals("1.1.1.1:3306:unknown:DEFAULT:ephemeral", actual.get("updated").get(0).textValue()); } @Test - public void deregisterInstance() throws Exception { - - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders - .delete(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/instance").param("serviceName", TEST_SERVICE_NAME) - .param("ip", "1.1.1.1").param("port", "9999") - .param("clusterName", UtilsAndCommons.DEFAULT_CLUSTER_NAME); - String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); - - Assert.assertEquals("ok", actualValue); + public void testBatchDeleteInstanceMetadata() throws Exception { + mockRequestParameter("metadata", "{}"); + when(instanceServiceV2.batchDeleteMetadata(eq(Constants.DEFAULT_NAMESPACE_ID), any(), anyMap())) + .thenReturn(Collections.singletonList("1.1.1.1:3306:unknown:DEFAULT:ephemeral")); + ObjectNode actual = instanceController.batchDeleteInstanceMetadata(request); + assertEquals("1.1.1.1:3306:unknown:DEFAULT:ephemeral", actual.get("updated").get(0).textValue()); } @Test - public void getInstances() throws Exception { - - Service service = new Service(); - service.setName(TEST_SERVICE_NAME); - - Cluster cluster = new Cluster(UtilsAndCommons.DEFAULT_CLUSTER_NAME, service); - service.addCluster(cluster); - + public void testPatch() throws Exception { + mockRequestParameter("metadata", "{}"); + mockRequestParameter("app", "test"); + mockRequestParameter("weight", "10"); + mockRequestParameter("healthy", "false"); + mockRequestParameter("enabled", "false"); + assertEquals("ok", instanceController.patch(request)); + verify(instanceServiceV2) + .patchInstance(eq(Constants.DEFAULT_NAMESPACE_ID), eq(TEST_GROUP_NAME + "@@" + TEST_SERVICE_NAME), + any(InstancePatchObject.class)); + } + + @Test + public void testList() throws Exception { Instance instance = new Instance(); - instance.setIp("10.10.10.10"); - instance.setPort(8888); - instance.setWeight(2.0); - instance.setServiceName(TEST_SERVICE_NAME); - List ipList = new ArrayList<>(); - ipList.add(instance); - service.updateIPs(ipList, false); - - when(serviceManager.getService(Constants.DEFAULT_NAMESPACE_ID, TEST_SERVICE_NAME)).thenReturn(service); - - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders - .get(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/instance/list").param("serviceName", TEST_SERVICE_NAME) - .header(HttpHeaderConsts.USER_AGENT_HEADER, "Nacos-Server:v1"); - - MockHttpServletResponse response = mockmvc.perform(builder).andReturn().getResponse(); - String actualValue = response.getContentAsString(); - JsonNode result = JacksonUtils.toObj(actualValue); - - Assert.assertEquals(TEST_SERVICE_NAME, result.get("name").asText()); - JsonNode hosts = result.get("hosts"); - Assert.assertNotNull(hosts); - Assert.assertEquals(hosts.size(), 1); - - JsonNode host = hosts.get(0); - Assert.assertNotNull(host); - Assert.assertEquals("10.10.10.10", host.get("ip").asText()); - Assert.assertEquals(8888, host.get("port").asInt()); - Assert.assertEquals(2.0, host.get("weight").asDouble(), 0.001); + instance.setIp("1.1.1.1"); + instance.setPort(3306); + ServiceInfo expected = new ServiceInfo(); + expected.setHosts(Collections.singletonList(instance)); + when(instanceServiceV2 + .listInstance(eq(Constants.DEFAULT_NAMESPACE_ID), eq(TEST_GROUP_NAME + "@@" + TEST_SERVICE_NAME), + any(Subscriber.class), eq(StringUtils.EMPTY), eq(false))).thenReturn(expected); + assertEquals(expected, instanceController.list(request)); } @Test - public void getNullServiceInstances() throws Exception { - when(serviceManager.getService(Constants.DEFAULT_NAMESPACE_ID, TEST_SERVICE_NAME)).thenReturn(null); - - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders - .get(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/instance/list").param("serviceName", TEST_SERVICE_NAME) - .header(HttpHeaderConsts.USER_AGENT_HEADER, "Nacos-Server:v1"); - - MockHttpServletResponse response = mockmvc.perform(builder).andReturn().getResponse(); - String actualValue = response.getContentAsString(); - JsonNode result = JacksonUtils.toObj(actualValue); - - JsonNode hosts = result.get("hosts"); - Assert.assertEquals(hosts.size(), 0); + public void testDetail() throws Exception { + Instance instance = new Instance(); + instance.setIp("1.1.1.1"); + instance.setPort(3306); + instance.setInstanceId("testId"); + instance.setClusterName(UtilsAndCommons.DEFAULT_CLUSTER_NAME); + when(instanceServiceV2.getInstance(Constants.DEFAULT_NAMESPACE_ID, TEST_GROUP_NAME + "@@" + TEST_SERVICE_NAME, + UtilsAndCommons.DEFAULT_CLUSTER_NAME, "1.1.1.1", 3306)).thenReturn(instance); + ObjectNode actual = instanceController.detail(request); + assertEquals(TEST_GROUP_NAME + "@@" + TEST_SERVICE_NAME, actual.get("service").textValue()); + assertEquals("1.1.1.1", actual.get("ip").textValue()); + assertEquals(3306, actual.get("port").intValue()); + assertEquals(UtilsAndCommons.DEFAULT_CLUSTER_NAME, actual.get("clusterName").textValue()); + assertEquals(1.0D, actual.get("weight").doubleValue(), 0.1); + assertEquals(true, actual.get("healthy").booleanValue()); + assertEquals("testId", actual.get("instanceId").textValue()); + assertEquals("{}", actual.get("metadata").toString()); } @Test - public void batchUpdateMetadata() throws Exception { - Instance instance = new Instance("1.1.1.1", 8080, TEST_CLUSTER_NAME); - instance.setServiceName(TEST_SERVICE_NAME); - Map metadata = new HashMap<>(); - metadata.put("key1", "value1"); - instance.setMetadata(metadata); - - Instance instance2 = new Instance("2.2.2.2", 8080, TEST_CLUSTER_NAME); - instance2.setServiceName(TEST_SERVICE_NAME); - - List instanceList = new LinkedList<>(); - instanceList.add(instance); - instanceList.add(instance2); - - when(serviceManager - .batchOperate(ArgumentMatchers.anyString(), ArgumentMatchers.any(InstanceOperationInfo.class), - ArgumentMatchers.any(Function.class))).thenReturn(instanceList); - - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders - .put(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/instance/metadata/batch").param("namespace", "public") - .param("serviceName", TEST_SERVICE_NAME).param("instances", - "[{\"ip\":\"1.1.1.1\",\"port\": \"8080\",\"ephemeral\":\"true\",\"clusterName\":\"test-cluster\"}," - + "{\"ip\":\"2.2.2.2\",\"port\":\"8080\",\"ephemeral\":\"true\",\"clusterName\":\"test-cluster\"}]") - .param("metadata", "{\"age\":\"20\",\"name\":\"horizon\"}"); - - String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); - - JsonNode result = JacksonUtils.toObj(actualValue); - - JsonNode updated = result.get("updated"); - - Assert.assertEquals(updated.size(), 2); - - Assert.assertTrue(updated.get(0).asText().contains("1.1.1.1")); - Assert.assertTrue(updated.get(0).asText().contains("8080")); - Assert.assertTrue(updated.get(0).asText().contains(TEST_CLUSTER_NAME)); - Assert.assertTrue(updated.get(0).asText().contains("ephemeral")); - - Assert.assertTrue(updated.get(1).asText().contains("2.2.2.2")); - Assert.assertTrue(updated.get(1).asText().contains("8080")); - Assert.assertTrue(updated.get(1).asText().contains(TEST_CLUSTER_NAME)); - Assert.assertTrue(updated.get(1).asText().contains("ephemeral")); - } - - @Test - public void batchDeleteMetadata() throws Exception { - Instance instance = new Instance("1.1.1.1", 8080, TEST_CLUSTER_NAME); - instance.setServiceName(TEST_SERVICE_NAME); - Map metadata = new HashMap<>(); - metadata.put("key1", "value1"); - instance.setMetadata(metadata); - - Instance instance2 = new Instance("2.2.2.2", 8080, TEST_CLUSTER_NAME); - instance2.setServiceName(TEST_SERVICE_NAME); - - List instanceList = new LinkedList<>(); - instanceList.add(instance); - instanceList.add(instance2); - - when(serviceManager - .batchOperate(ArgumentMatchers.anyString(), ArgumentMatchers.any(InstanceOperationInfo.class), - ArgumentMatchers.any(Function.class))).thenReturn(instanceList); - - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders - .delete(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/instance/metadata/batch").param("namespace", "public") - .param("serviceName", TEST_SERVICE_NAME).param("instances", - "[{\"ip\":\"1.1.1.1\",\"port\": \"8080\",\"ephemeral\":\"true\",\"clusterName\":\"test-cluster\"}," - + "{\"ip\":\"2.2.2.2\",\"port\":\"8080\",\"ephemeral\":\"true\",\"clusterName\":\"test-cluster\"}]") - .param("metadata", "{\"age\":\"20\",\"name\":\"horizon\"}"); - - String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); - - JsonNode result = JacksonUtils.toObj(actualValue); - - JsonNode updated = result.get("updated"); - - Assert.assertEquals(updated.size(), 2); - - Assert.assertTrue(updated.get(0).asText().contains("1.1.1.1")); - Assert.assertTrue(updated.get(0).asText().contains("8080")); - Assert.assertTrue(updated.get(0).asText().contains(TEST_CLUSTER_NAME)); - Assert.assertTrue(updated.get(0).asText().contains("ephemeral")); - - Assert.assertTrue(updated.get(1).asText().contains("2.2.2.2")); - Assert.assertTrue(updated.get(1).asText().contains("8080")); - Assert.assertTrue(updated.get(1).asText().contains(TEST_CLUSTER_NAME)); - Assert.assertTrue(updated.get(1).asText().contains("ephemeral")); + public void testBeat() throws Exception { + when(instanceServiceV2 + .handleBeat(eq(Constants.DEFAULT_NAMESPACE_ID), eq(TEST_GROUP_NAME + "@@" + TEST_SERVICE_NAME), + eq("1.1.1.1"), eq(3306), eq(UtilsAndCommons.DEFAULT_CLUSTER_NAME), any(), any())) + .thenReturn(200); + when(instanceServiceV2.getHeartBeatInterval(eq(Constants.DEFAULT_NAMESPACE_ID), + eq(TEST_GROUP_NAME + "@@" + TEST_SERVICE_NAME), eq("1.1.1.1"), eq(3306), + eq(UtilsAndCommons.DEFAULT_CLUSTER_NAME))).thenReturn(10000L); + ObjectNode actual = instanceController.beat(request); + assertEquals(200, actual.get("code").intValue()); + assertEquals(10000L, actual.get("clientBeatInterval").longValue()); + assertTrue(actual.get("lightBeatEnabled").booleanValue()); } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/InstanceControllerV2Test.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/InstanceControllerV2Test.java deleted file mode 100644 index 217c6c1b4..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/controllers/InstanceControllerV2Test.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 1999-2021 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.nacos.naming.controllers; - -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.naming.BaseTest; -import com.alibaba.nacos.naming.core.InstanceOperatorClientImpl; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.fasterxml.jackson.databind.JsonNode; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; - -@RunWith(MockitoJUnitRunner.class) -public class InstanceControllerV2Test extends BaseTest { - - @InjectMocks - private InstanceControllerV2 instanceControllerV2; - - @Mock - private InstanceOperatorClientImpl instanceServiceV2; - - private MockMvc mockmvc; - - @Before - public void before() { - super.before(); - ReflectionTestUtils.setField(instanceControllerV2, "instanceServiceV2", instanceServiceV2); - mockmvc = MockMvcBuilders.standaloneSetup(instanceControllerV2).build(); - } - - @Test - public void registerInstance() throws Exception { - - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post( - UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT) - .param("namespaceId", TEST_NAMESPACE).param("serviceName", TEST_SERVICE_NAME).param("ip", TEST_IP) - .param("cluster", TEST_CLUSTER_NAME).param("port", "9999").param("healthy", "true").param("weight", "1") - .param("enabled", "true").param("metadata", TEST_METADATA).param("ephemeral", "true"); - String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); - - Assert.assertEquals("ok", actualValue); - } - - @Test - public void deregisterInstance() throws Exception { - - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.delete( - UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT) - .param("namespaceId", TEST_NAMESPACE).param("serviceName", TEST_SERVICE_NAME).param("ip", TEST_IP) - .param("cluster", TEST_CLUSTER_NAME).param("port", "9999").param("healthy", "true").param("weight", "1") - .param("enabled", "true").param("metadata", TEST_METADATA).param("ephemeral", "true"); - String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); - - Assert.assertEquals("ok", actualValue); - } - - @Test - public void updateInstance() throws Exception { - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put( - UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT) - .param("namespaceId", TEST_NAMESPACE).param("serviceName", TEST_SERVICE_NAME).param("ip", TEST_IP) - .param("cluster", TEST_CLUSTER_NAME).param("port", "9999").param("healthy", "true") - .param("weight", "2.0").param("enabled", "true").param("metadata", TEST_METADATA) - .param("ephemeral", "false"); - String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); - - Assert.assertEquals("ok", actualValue); - } - - @Test - public void batchUpdateInstanceMetadata() throws Exception { - - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put( - UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT - + "/metadata/batch").param("namespaceId", TEST_NAMESPACE) - .param("serviceName", TEST_SERVICE_NAME).param("consistencyType", "ephemeral") - .param("instances", TEST_INSTANCE_INFO_LIST).param("metadata", TEST_METADATA); - MockHttpServletResponse response = mockmvc.perform(builder).andReturn().getResponse(); - String actualValue = response.getContentAsString(); - - JsonNode result = JacksonUtils.toObj(actualValue); - Assert.assertNotNull(result); - Assert.assertEquals(result.size(), 1); - } - - @Test - public void patch() throws Exception { - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.patch( - UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT) - .param("namespaceId", TEST_NAMESPACE).param("serviceName", TEST_SERVICE_NAME).param("ip", TEST_IP) - .param("cluster", TEST_CLUSTER_NAME).param("port", "9999").param("healthy", "true") - .param("weight", "2.0").param("enabled", "true").param("metadata", TEST_METADATA) - .param("ephemeral", "false"); - String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); - Assert.assertEquals("ok", actualValue); - } - - @Test - public void listInstance() throws Exception { - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get( - UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT - + "/list").param("namespaceId", TEST_NAMESPACE).param("serviceName", TEST_SERVICE_NAME) - .param("clientIP", TEST_IP).param("udpPort", "9870").param("healthyOnly", "true") - .param("app", "appName"); - String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); - Assert.assertNotNull(actualValue); - } - - @Test - public void detail() throws Exception { - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get( - UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT) - .param("namespaceId", TEST_NAMESPACE).param("serviceName", TEST_SERVICE_NAME).param("ip", TEST_IP) - .param("clusterName", "clusterName"); - String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); - Assert.assertNotNull(actualValue); - } - - @Test - public void beat() throws Exception { - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put( - UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT - + "/beat").param("namespaceId", TEST_NAMESPACE).param("serviceName", TEST_SERVICE_NAME) - .param("ip", TEST_IP).param("clusterName", "clusterName").param("port", "0").param("beat", ""); - String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); - Assert.assertNotNull(actualValue); - } - - @Test - public void listWithHealthStatus() throws Exception { - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get( - UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT - + "/statuses").param("key", ""); - String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); - Assert.assertNotNull(actualValue); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/OperatorControllerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/OperatorControllerTest.java index fe00746af..884cfcc95 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/controllers/OperatorControllerTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/OperatorControllerTest.java @@ -17,14 +17,14 @@ package com.alibaba.nacos.naming.controllers; -import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.naming.cluster.ServerStatus; import com.alibaba.nacos.naming.cluster.ServerStatusManager; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftCore; import com.alibaba.nacos.naming.core.DistroMapper; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.ServiceManager; +import com.alibaba.nacos.naming.core.v2.client.Client; +import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; +import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.naming.misc.SwitchManager; import com.alibaba.nacos.naming.monitor.MetricsMonitor; @@ -66,15 +66,9 @@ public class OperatorControllerTest { @Mock private ServerStatusManager serverStatusManager; - @Mock - private ServiceManager serviceManager; - @Mock private ClientManager clientManager; - @Mock - private RaftCore raftCore; - @Mock private DistroMapper distroMapper; @@ -98,17 +92,6 @@ public class OperatorControllerTest { Assert.assertEquals(this.switchDomain, switchDomain); } - @Test - public void testSwitchDomainForNotSupportUpgrade() { - MockEnvironment environment = new MockEnvironment(); - EnvUtil.setEnvironment(environment); - SwitchDomain switchDomain = operatorController.switches(new MockHttpServletRequest()); - SwitchDomain expected = new SwitchDomain(); - expected.update(switchDomain); - expected.setDoubleWriteEnabled(false); - Assert.assertEquals(expected.toString(), switchDomain.toString()); - } - @Test public void testUpdateSwitch() { try { @@ -123,43 +106,27 @@ public class OperatorControllerTest { @Test public void testMetrics() { Mockito.when(serverStatusManager.getServerStatus()).thenReturn(ServerStatus.UP); - Mockito.when(serviceManager.getResponsibleServiceCount()).thenReturn(1); - Mockito.when(serviceManager.getResponsibleInstanceCount()).thenReturn(1); - Mockito.when(raftCore.getNotifyTaskCount()).thenReturn(1); Collection clients = new HashSet<>(); clients.add("1628132208793_127.0.0.1_8080"); clients.add("127.0.0.1:8081#true"); clients.add("127.0.0.1:8082#false"); Mockito.when(clientManager.allClientId()).thenReturn(clients); - Mockito.when(clientManager.isResponsibleClient(null)).thenReturn(Boolean.TRUE); + Client client = new IpPortBasedClient("127.0.0.1:8081#true", true); + client.addServiceInstance(Service.newService("", "", ""), new InstancePublishInfo()); + Mockito.when(clientManager.getClient("127.0.0.1:8081#true")).thenReturn(client); + Mockito.when(clientManager.isResponsibleClient(client)).thenReturn(Boolean.TRUE); MockHttpServletRequest servletRequest = new MockHttpServletRequest(); servletRequest.addParameter("onlyStatus", "false"); ObjectNode objectNode = operatorController.metrics(servletRequest); - Assert.assertEquals(1, objectNode.get("responsibleServiceCount").asInt()); Assert.assertEquals(1, objectNode.get("responsibleInstanceCount").asInt()); Assert.assertEquals(ServerStatus.UP.toString(), objectNode.get("status").asText()); Assert.assertEquals(3, objectNode.get("clientCount").asInt()); Assert.assertEquals(1, objectNode.get("connectionBasedClientCount").asInt()); Assert.assertEquals(1, objectNode.get("ephemeralIpPortClientCount").asInt()); Assert.assertEquals(1, objectNode.get("persistentIpPortClientCount").asInt()); - Assert.assertEquals(3, objectNode.get("responsibleClientCount").asInt()); - } - - @Test - public void testGetResponsibleServer4Service() { - try { - Mockito.when(serviceManager.getService(Mockito.anyString(), Mockito.anyString())).thenReturn(new Service()); - Mockito.when(distroMapper.mapSrv(Mockito.anyString())).thenReturn("test"); - - ObjectNode objectNode = operatorController.getResponsibleServer4Service("test", "test"); - - Assert.assertEquals("test", objectNode.get("responsibleServer").asText()); - } catch (NacosException e) { - e.printStackTrace(); - Assert.fail(e.getMessage()); - } + Assert.assertEquals(1, objectNode.get("responsibleClientCount").asInt()); } @Test diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/ServiceControllerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/ServiceControllerTest.java index 414c0bd12..4403d5c66 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/controllers/ServiceControllerTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/ServiceControllerTest.java @@ -21,7 +21,6 @@ import com.alibaba.nacos.api.naming.CommonParams; import com.alibaba.nacos.naming.BaseTest; import com.alibaba.nacos.naming.core.ServiceOperatorV2Impl; import com.alibaba.nacos.naming.core.SubscribeManager; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; import com.alibaba.nacos.naming.pojo.Subscriber; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Assert; @@ -33,7 +32,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.util.ReflectionTestUtils; import java.util.Arrays; import java.util.Collections; @@ -47,17 +45,12 @@ public class ServiceControllerTest extends BaseTest { @Mock private ServiceOperatorV2Impl serviceOperatorV2; - @Mock - private UpgradeJudgement upgradeJudgement; - @Mock private SubscribeManager subscribeManager; @Before public void before() { super.before(); - ReflectionTestUtils.setField(serviceController, "upgradeJudgement", upgradeJudgement); - Mockito.when(upgradeJudgement.isUseGrpcFeatures()).thenReturn(true); } @Test @@ -65,7 +58,7 @@ public class ServiceControllerTest extends BaseTest { Mockito.when(serviceOperatorV2.listService(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Collections.singletonList("DEFAULT_GROUP@@providers:com.alibaba.nacos.controller.test:1")); - + MockHttpServletRequest servletRequest = new MockHttpServletRequest(); servletRequest.addParameter("pageNo", "1"); servletRequest.addParameter("pageSize", "10"); @@ -77,7 +70,6 @@ public class ServiceControllerTest extends BaseTest { @Test public void testCreate() { try { - Mockito.when(upgradeJudgement.isUseGrpcFeatures()).thenReturn(true); String res = serviceController.create(TEST_NAMESPACE, TEST_SERVICE_NAME, 0, "", ""); Assert.assertEquals("ok", res); } catch (Exception e) { @@ -128,10 +120,11 @@ public class ServiceControllerTest extends BaseTest { @Test public void testSearchService() { try { - Mockito.when(serviceOperatorV2.searchServiceName(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean())) + Mockito.when( + serviceOperatorV2.searchServiceName(Mockito.anyString(), Mockito.anyString())) .thenReturn(Collections.singletonList("result")); - ObjectNode objectNode = serviceController.searchService(TEST_NAMESPACE, "", true); + ObjectNode objectNode = serviceController.searchService(TEST_NAMESPACE, ""); Assert.assertEquals(1, objectNode.get("count").asInt()); } catch (NacosException e) { e.printStackTrace(); @@ -139,11 +132,12 @@ public class ServiceControllerTest extends BaseTest { } try { - Mockito.when(serviceOperatorV2.searchServiceName(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean())) + Mockito.when( + serviceOperatorV2.searchServiceName(Mockito.anyString(), Mockito.anyString())) .thenReturn(Arrays.asList("re1", "re2")); Mockito.when(serviceOperatorV2.listAllNamespace()).thenReturn(Arrays.asList("re1", "re2")); - ObjectNode objectNode = serviceController.searchService(null, "", true); + ObjectNode objectNode = serviceController.searchService(null, ""); Assert.assertEquals(4, objectNode.get("count").asInt()); } catch (NacosException e) { e.printStackTrace(); diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/InstanceControllerV2Test.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/InstanceControllerV2Test.java new file mode 100644 index 000000000..1af5f7021 --- /dev/null +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/InstanceControllerV2Test.java @@ -0,0 +1,231 @@ +/* + * Copyright 1999-2022 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.nacos.naming.controllers.v2; + +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.naming.BaseTest; +import com.alibaba.nacos.naming.core.InstanceOperatorClientImpl; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.model.form.InstanceForm; +import com.alibaba.nacos.naming.model.form.InstanceMetadataBatchOperationForm; +import com.alibaba.nacos.naming.model.vo.InstanceDetailInfoVo; +import com.alibaba.nacos.naming.model.vo.InstanceMetadataBatchOperationVo; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import java.util.ArrayList; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class InstanceControllerV2Test extends BaseTest { + + @InjectMocks + private InstanceControllerV2 instanceControllerV2; + + @Mock + private InstanceOperatorClientImpl instanceServiceV2; + + private MockMvc mockmvc; + + @Before + public void before() { + super.before(); + ReflectionTestUtils.setField(instanceControllerV2, "instanceServiceV2", instanceServiceV2); + mockmvc = MockMvcBuilders.standaloneSetup(instanceControllerV2).build(); + } + + @Test + public void registerInstance() throws Exception { + + InstanceForm instanceForm = new InstanceForm(); + instanceForm.setNamespaceId(TEST_NAMESPACE); + instanceForm.setGroupName("DEFAULT_GROUP"); + instanceForm.setServiceName("test-service"); + instanceForm.setIp(TEST_IP); + instanceForm.setClusterName(TEST_CLUSTER_NAME); + instanceForm.setPort(9999); + instanceForm.setHealthy(true); + instanceForm.setWeight(1.0); + instanceForm.setEnabled(true); + instanceForm.setMetadata(TEST_METADATA); + instanceForm.setEphemeral(true); + + Result result = instanceControllerV2.register(instanceForm); + + verify(instanceServiceV2).registerInstance(eq(TEST_NAMESPACE), eq(TEST_SERVICE_NAME), any()); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals("ok", result.getData()); + } + + @Test + public void deregisterInstance() throws Exception { + + InstanceForm instanceForm = new InstanceForm(); + instanceForm.setNamespaceId(TEST_NAMESPACE); + instanceForm.setGroupName("DEFAULT_GROUP"); + instanceForm.setServiceName("test-service"); + instanceForm.setIp(TEST_IP); + instanceForm.setClusterName(TEST_CLUSTER_NAME); + instanceForm.setPort(9999); + instanceForm.setHealthy(true); + instanceForm.setWeight(1.0); + instanceForm.setEnabled(true); + instanceForm.setMetadata(TEST_METADATA); + instanceForm.setEphemeral(true); + + Result result = instanceControllerV2.deregister(instanceForm); + + verify(instanceServiceV2).removeInstance(eq(TEST_NAMESPACE), eq(TEST_SERVICE_NAME), any()); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals("ok", result.getData()); + + } + + @Test + public void updateInstance() throws Exception { + InstanceForm instanceForm = new InstanceForm(); + instanceForm.setNamespaceId(TEST_NAMESPACE); + instanceForm.setGroupName("DEFAULT_GROUP"); + instanceForm.setServiceName("test-service"); + instanceForm.setIp(TEST_IP); + instanceForm.setClusterName(TEST_CLUSTER_NAME); + instanceForm.setPort(9999); + instanceForm.setHealthy(true); + instanceForm.setWeight(1.0); + instanceForm.setEnabled(true); + instanceForm.setMetadata(TEST_METADATA); + instanceForm.setEphemeral(true); + + Result result = instanceControllerV2.update(instanceForm); + + verify(instanceServiceV2).updateInstance(eq(TEST_NAMESPACE), eq(TEST_SERVICE_NAME), any()); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals("ok", result.getData()); + } + + @Test + public void batchUpdateInstanceMetadata() throws Exception { + + InstanceMetadataBatchOperationForm form = new InstanceMetadataBatchOperationForm(); + form.setNamespaceId(TEST_NAMESPACE); + form.setGroupName("DEFAULT"); + form.setServiceName("test-service"); + form.setConsistencyType("ephemeral"); + form.setInstances(TEST_INSTANCE_INFO_LIST); + form.setMetadata(TEST_METADATA); + + ArrayList ipList = new ArrayList<>(); + ipList.add(TEST_IP); + when(instanceServiceV2.batchUpdateMetadata(eq(TEST_NAMESPACE), any(), any())).thenReturn(ipList); + + InstanceMetadataBatchOperationVo expectUpdate = new InstanceMetadataBatchOperationVo(ipList); + + Result result = instanceControllerV2.batchUpdateInstanceMetadata(form); + verify(instanceServiceV2).batchUpdateMetadata(eq(TEST_NAMESPACE), any(), any()); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(expectUpdate.getUpdated().size(), result.getData().getUpdated().size()); + assertEquals(expectUpdate.getUpdated().get(0), result.getData().getUpdated().get(0)); + } + + @Test + public void patch() throws Exception { + MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.patch( + UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT) + .param("namespaceId", TEST_NAMESPACE).param("serviceName", TEST_SERVICE_NAME).param("ip", TEST_IP) + .param("cluster", TEST_CLUSTER_NAME).param("port", "9999").param("healthy", "true") + .param("weight", "2.0").param("enabled", "true").param("metadata", TEST_METADATA) + .param("ephemeral", "false"); + String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); + Assert.assertEquals("ok", actualValue); + } + + @Test + public void listInstance() throws Exception { + + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.setName("serviceInfo"); + + when(instanceServiceV2.listInstance(eq(TEST_NAMESPACE), eq(TEST_SERVICE_NAME), any(), eq(TEST_CLUSTER_NAME), eq(false))) + .thenReturn(serviceInfo); + + Result result = instanceControllerV2 + .list(TEST_NAMESPACE, "DEFAULT_GROUP", "test-service", TEST_CLUSTER_NAME, TEST_IP, 9999, false, "", "", ""); + + verify(instanceServiceV2).listInstance(eq(TEST_NAMESPACE), eq(TEST_SERVICE_NAME), any(), eq(TEST_CLUSTER_NAME), eq(false)); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(serviceInfo.getName(), result.getData().getName()); + } + + @Test + public void detail() throws Exception { + + Instance instance = new Instance(); + instance.setInstanceId("test-id"); + + when(instanceServiceV2.getInstance(TEST_NAMESPACE, TEST_SERVICE_NAME, TEST_CLUSTER_NAME, TEST_IP, 9999)).thenReturn(instance); + + Result result = instanceControllerV2 + .detail(TEST_NAMESPACE, "DEFAULT_GROUP", "test-service", TEST_CLUSTER_NAME, TEST_IP, 9999); + + verify(instanceServiceV2).getInstance(TEST_NAMESPACE, TEST_SERVICE_NAME, TEST_CLUSTER_NAME, TEST_IP, 9999); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(instance.getInstanceId(), result.getData().getInstanceId()); + } + + @Test + public void beat() throws Exception { + MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put( + UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT + + "/beat").param("namespaceId", TEST_NAMESPACE).param("serviceName", TEST_SERVICE_NAME) + .param("ip", TEST_IP).param("clusterName", "clusterName").param("port", "0").param("beat", ""); + String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); + Assert.assertNotNull(actualValue); + } + + @Test + public void listWithHealthStatus() throws Exception { + MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get( + UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT + + "/statuses").param("key", ""); + String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); + Assert.assertNotNull(actualValue); + } +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/OperatorControllerV2Test.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/OperatorControllerV2Test.java new file mode 100644 index 000000000..25e414a10 --- /dev/null +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/OperatorControllerV2Test.java @@ -0,0 +1,123 @@ +/* + * Copyright 1999-2022 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.nacos.naming.controllers.v2; + +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.naming.cluster.ServerStatus; +import com.alibaba.nacos.naming.cluster.ServerStatusManager; +import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.misc.SwitchDomain; +import com.alibaba.nacos.naming.misc.SwitchManager; +import com.alibaba.nacos.naming.model.form.UpdateSwitchForm; +import com.alibaba.nacos.naming.model.vo.MetricsInfoVo; +import com.alibaba.nacos.sys.env.Constants; +import com.alibaba.nacos.sys.env.EnvUtil; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.mock.env.MockEnvironment; + +import java.util.Collection; +import java.util.HashSet; + +import static org.junit.Assert.assertEquals; + +/** + * OperatorControllerV2Test. + * + * @author dongyafei + * @date 2022/9/15 + */ + +@RunWith(MockitoJUnitRunner.class) +public class OperatorControllerV2Test { + + private OperatorControllerV2 operatorControllerV2; + + @Mock + private SwitchDomain switchDomain; + + @Mock + private SwitchManager switchManager; + + @Mock + private ServerStatusManager serverStatusManager; + + @Mock + private ClientManager clientManager; + + @Before + public void setUp() { + this.operatorControllerV2 = new OperatorControllerV2(switchManager, serverStatusManager, switchDomain, + clientManager); + MockEnvironment environment = new MockEnvironment(); + environment.setProperty(Constants.SUPPORT_UPGRADE_FROM_1X, "true"); + EnvUtil.setEnvironment(environment); + } + + @Test + public void testSwitches() { + Result result = operatorControllerV2.switches(); + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(this.switchDomain, result.getData()); + } + + @Test + public void testUpdateSwitches() { + UpdateSwitchForm updateSwitchForm = new UpdateSwitchForm(); + updateSwitchForm.setDebug(true); + updateSwitchForm.setEntry("test"); + updateSwitchForm.setValue("test"); + + try { + Result result = operatorControllerV2.updateSwitch(updateSwitchForm); + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals("ok", result.getData()); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(e.getMessage()); + } + } + + @Test + public void testMetrics() { + Mockito.when(serverStatusManager.getServerStatus()).thenReturn(ServerStatus.UP); + Collection clients = new HashSet<>(); + clients.add("1628132208793_127.0.0.1_8080"); + clients.add("127.0.0.1:8081#true"); + clients.add("127.0.0.1:8082#false"); + Mockito.when(clientManager.allClientId()).thenReturn(clients); + Mockito.when(clientManager.isResponsibleClient(null)).thenReturn(Boolean.TRUE); + + Result result = operatorControllerV2.metrics(false); + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + + MetricsInfoVo metricsInfoVo = result.getData(); + + Assert.assertEquals(ServerStatus.UP.toString(), metricsInfoVo.getStatus()); + Assert.assertEquals(3, metricsInfoVo.getClientCount().intValue()); + Assert.assertEquals(1, metricsInfoVo.getConnectionBasedClientCount().intValue()); + Assert.assertEquals(1, metricsInfoVo.getEphemeralIpPortClientCount().intValue()); + Assert.assertEquals(1, metricsInfoVo.getPersistentIpPortClientCount().intValue()); + Assert.assertEquals(3, metricsInfoVo.getResponsibleClientCount().intValue()); + } +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/ServiceControllerV2Test.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/ServiceControllerV2Test.java similarity index 67% rename from naming/src/test/java/com/alibaba/nacos/naming/controllers/ServiceControllerV2Test.java rename to naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/ServiceControllerV2Test.java index 28845fe78..1b9014b43 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/controllers/ServiceControllerV2Test.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/ServiceControllerV2Test.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2021 Alibaba Group Holding Ltd. + * Copyright 1999-2022 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. @@ -14,13 +14,15 @@ * limitations under the License. */ -package com.alibaba.nacos.naming.controllers; +package com.alibaba.nacos.naming.controllers.v2; import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.common.model.RestResult; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.naming.core.ServiceOperatorV2Impl; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.model.form.ServiceForm; import com.alibaba.nacos.naming.pojo.ServiceDetailInfo; import com.alibaba.nacos.naming.pojo.ServiceNameView; import com.alibaba.nacos.naming.selector.SelectorManager; @@ -28,11 +30,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; -import javax.servlet.http.HttpServletRequest; - import java.util.Collections; import static org.junit.Assert.assertEquals; @@ -59,23 +58,32 @@ public class ServiceControllerV2Test { @Test public void testCreate() throws Exception { - RestResult actual = serviceController - .create(Constants.DEFAULT_NAMESPACE_ID, "service", Constants.DEFAULT_GROUP, true, 0.0f, "", ""); + + ServiceForm serviceForm = new ServiceForm(); + serviceForm.setNamespaceId(Constants.DEFAULT_NAMESPACE_ID); + serviceForm.setServiceName("service"); + serviceForm.setGroupName(Constants.DEFAULT_GROUP); + serviceForm.setEphemeral(true); + serviceForm.setProtectThreshold(0.0F); + serviceForm.setMetadata(""); + serviceForm.setSelector(""); + + Result actual = serviceController.create(serviceForm); verify(serviceOperatorV2) .create(eq(Service.newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "service")), any(ServiceMetadata.class)); + assertEquals(ErrorCode.SUCCESS.getCode(), actual.getCode()); assertEquals("ok", actual.getData()); - assertEquals(200, actual.getCode()); } @Test public void testRemove() throws Exception { - RestResult actual = serviceController + Result actual = serviceController .remove(Constants.DEFAULT_NAMESPACE_ID, "service", Constants.DEFAULT_GROUP); verify(serviceOperatorV2) .delete(Service.newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "service")); assertEquals("ok", actual.getData()); - assertEquals(200, actual.getCode()); + assertEquals(ErrorCode.SUCCESS.getCode(), actual.getCode()); } @Test @@ -84,21 +92,19 @@ public class ServiceControllerV2Test { when(serviceOperatorV2 .queryService(Service.newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "service"))) .thenReturn(expected); - RestResult actual = serviceController + Result actual = serviceController .detail(Constants.DEFAULT_NAMESPACE_ID, "service", Constants.DEFAULT_GROUP); - assertEquals(200, actual.getCode()); + assertEquals(ErrorCode.SUCCESS.getCode(), actual.getCode()); assertEquals(expected, actual.getData()); } @Test public void testList() throws Exception { - HttpServletRequest request = Mockito.mock(HttpServletRequest.class); - when(request.getParameter("pageNo")).thenReturn("1"); - when(request.getParameter("pageSize")).thenReturn("10"); + when(serviceOperatorV2.listService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "")).thenReturn( Collections.singletonList("serviceName")); - RestResult actual = serviceController.list(request); - assertEquals(200, actual.getCode()); + Result actual = serviceController.list(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "", 1, 10); + assertEquals(ErrorCode.SUCCESS.getCode(), actual.getCode()); assertEquals(1, actual.getData().getCount()); assertEquals(1, actual.getData().getServices().size()); assertEquals("serviceName", actual.getData().getServices().iterator().next()); @@ -106,12 +112,19 @@ public class ServiceControllerV2Test { @Test public void testUpdate() throws Exception { - RestResult actual = serviceController - .update(Constants.DEFAULT_NAMESPACE_ID, "service", Constants.DEFAULT_GROUP, 0.0f, "", ""); + ServiceForm serviceForm = new ServiceForm(); + serviceForm.setNamespaceId(Constants.DEFAULT_NAMESPACE_ID); + serviceForm.setGroupName(Constants.DEFAULT_GROUP); + serviceForm.setServiceName("service"); + serviceForm.setProtectThreshold(0.0f); + serviceForm.setMetadata(""); + serviceForm.setSelector(""); + Result actual = serviceController + .update(serviceForm); verify(serviceOperatorV2) .update(eq(Service.newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "service")), any(ServiceMetadata.class)); + assertEquals(ErrorCode.SUCCESS.getCode(), actual.getCode()); assertEquals("ok", actual.getData()); - assertEquals(200, actual.getCode()); } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/ClusterTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/ClusterTest.java deleted file mode 100644 index c769c49fd..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/ClusterTest.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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.nacos.naming.core; - -import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Http; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.sys.env.EnvUtil; -import com.alibaba.nacos.sys.utils.ApplicationUtils; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.misc.SwitchDomain.TcpHealthParams; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.mock.env.MockEnvironment; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class ClusterTest { - - private Cluster cluster; - - @Mock - private ConfigurableApplicationContext context; - - @Mock - private SwitchDomain switchDomain; - - @Before - public void before() { - EnvUtil.setEnvironment(new MockEnvironment()); - ApplicationUtils.injectContext(context); - when(context.getBean(SwitchDomain.class)).thenReturn(switchDomain); - when(switchDomain.getTcpHealthParams()).thenReturn(new TcpHealthParams()); - Service service = new Service(); - service.setName("nacos.service.1"); - - cluster = new Cluster("nacos-cluster-1", service); - cluster.setDefCkport(80); - cluster.setDefIPPort(8080); - cluster.init(); - } - - @After - public void tearDown() { - cluster.destroy(); - } - - @Test - public void updateCluster() { - Service service = new Service(); - service.setName("nacos.service.2"); - - Cluster newCluster = new Cluster("nacos-cluster-1", service); - newCluster.setDefCkport(8888); - newCluster.setDefIPPort(9999); - Http healthCheckConfig = new Http(); - healthCheckConfig.setPath("/nacos-path-1"); - healthCheckConfig.setExpectedResponseCode(500); - healthCheckConfig.setHeaders("Client-Version:nacos-test-1"); - newCluster.setHealthChecker(healthCheckConfig); - - cluster.update(newCluster); - - assertEquals(8888, cluster.getDefCkport()); - assertEquals(9999, cluster.getDefIPPort()); - assertTrue(cluster.getHealthChecker() instanceof Http); - Http httpHealthCheck = (Http) (cluster.getHealthChecker()); - assertEquals("/nacos-path-1", httpHealthCheck.getPath()); - assertEquals(500, httpHealthCheck.getExpectedResponseCode()); - assertEquals("Client-Version:nacos-test-1", httpHealthCheck.getHeaders()); - } - - @Test - public void updateIps() { - - Instance instance1 = new Instance(); - instance1.setIp("1.1.1.1"); - instance1.setPort(1234); - - Instance instance2 = new Instance(); - instance2.setIp("1.1.1.1"); - instance2.setPort(2345); - - List list = new ArrayList<>(); - list.add(instance1); - list.add(instance2); - - cluster.updateIps(list, false); - - List ips = cluster.allIPs(); - assertNotNull(ips); - assertEquals(2, ips.size()); - assertEquals("1.1.1.1", ips.get(0).getIp()); - assertEquals(1234, ips.get(0).getPort()); - assertEquals("1.1.1.1", ips.get(1).getIp()); - assertEquals(2345, ips.get(1).getPort()); - } - - @Test - public void testValidate() { - Service service = new Service("nacos.service.2"); - cluster = new Cluster("nacos-cluster-1", service); - cluster.validate(); - } - - @Test(expected = IllegalArgumentException.class) - public void testValidateClusterNameNull() { - Service service = new Service("nacos.service.2"); - cluster = new Cluster(null, service); - cluster.validate(); - } - - @Test(expected = IllegalArgumentException.class) - public void testValidateServiceNull() { - cluster = new Cluster("nacos-cluster-1", null); - cluster.validate(); - } - - @Test - public void testSerialize() throws Exception { - String actual = JacksonUtils.toJson(cluster); - System.out.println(actual); - assertTrue(actual.contains("\"defaultPort\":80")); - assertTrue(actual.contains("\"defIPPort\":8080")); - assertTrue(actual.contains("\"healthChecker\":{\"type\":\"TCP\"}")); - assertTrue(actual.contains("\"metadata\":{}")); - assertTrue(actual.contains("\"defCkport\":80")); - assertTrue(actual.contains("\"name\":\"nacos-cluster-1\"")); - assertTrue(actual.contains("\"defaultCheckPort\":80")); - assertTrue(actual.contains("\"serviceName\":\"nacos.service.1\"")); - assertTrue(actual.contains("\"useIPPort4Check\":true")); - assertTrue(actual.contains("\"sitegroup\":\"\"")); - assertTrue(actual.contains("\"empty\":true")); - assertFalse(actual.contains("\"service\"")); - - } - - @Test - @SuppressWarnings("checkstyle:linelength") - public void testDeserialize() throws Exception { - String example = "{\"defCkport\":80,\"defIPPort\":8080,\"defaultCheckPort\":80,\"defaultPort\":80,\"empty\":true,\"healthChecker\":{\"type\":\"TCP\"},\"metadata\":{},\"name\":\"nacos-cluster-1\",\"serviceName\":\"nacos.service.1\",\"sitegroup\":\"\",\"useIPPort4Check\":true}"; - Cluster actual = JacksonUtils.toObj(example, Cluster.class); - assertEquals(80, actual.getDefCkport()); - assertEquals(8080, actual.getDefIPPort()); - assertEquals(80, actual.getDefaultCheckPort()); - assertEquals(80, actual.getDefaultPort()); - assertTrue(actual.isEmpty()); - assertTrue(actual.getMetadata().isEmpty()); - assertTrue(actual.isUseIPPort4Check()); - assertEquals("nacos-cluster-1", actual.getName()); - assertEquals("nacos.service.1", actual.getServiceName()); - assertEquals("", actual.getSitegroup()); - assertNull(actual.getHealthCheckTask()); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/DomainTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/DomainTest.java deleted file mode 100644 index 31aa3c237..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/DomainTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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.nacos.naming.core; - -import com.alibaba.nacos.naming.BaseTest; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteEventListener; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import static org.mockito.Mockito.when; - -public class DomainTest extends BaseTest { - - private Service service; - - @Mock - private DoubleWriteEventListener doubleWriteEventListener; - - @Before - public void before() { - super.before(); - when(context.getBean(DoubleWriteEventListener.class)).thenReturn(doubleWriteEventListener); - service = new Service(); - service.setName("nacos.service.1"); - Cluster cluster = new Cluster(UtilsAndCommons.DEFAULT_CLUSTER_NAME, service); - service.addCluster(cluster); - mockInjectPushServer(); - } - - @After - public void tearDown() throws Exception { - service.destroy(); - } - - @Test - public void updateDomain() { - - Service newDomain = new Service(); - newDomain.setName("nacos.service.1"); - newDomain.setProtectThreshold(0.7f); - Cluster cluster = new Cluster(UtilsAndCommons.DEFAULT_CLUSTER_NAME, newDomain); - newDomain.addCluster(cluster); - - service.update(newDomain); - - Assert.assertEquals(0.7f, service.getProtectThreshold(), 0.0001f); - } - - @Test - public void addCluster() { - Cluster cluster = new Cluster("nacos-cluster-1", service); - - service.addCluster(cluster); - - Map clusterMap = service.getClusterMap(); - Assert.assertNotNull(clusterMap); - Assert.assertEquals(2, clusterMap.size()); - Assert.assertTrue(clusterMap.containsKey("nacos-cluster-1")); - } - - @Test - public void updateIps() throws Exception { - - Instance instance = new Instance(); - instance.setIp("1.1.1.1"); - instance.setPort(1234); - List list = new ArrayList(); - list.add(instance); - - Instances instances = new Instances(); - - instances.setInstanceList(list); - - service.onChange("iplist", instances); - - List ips = service.allIPs(); - - Assert.assertNotNull(ips); - Assert.assertEquals(1, ips.size()); - Assert.assertEquals("1.1.1.1", ips.get(0).getIp()); - Assert.assertEquals(1234, ips.get(0).getPort()); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/DomainsManagerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/DomainsManagerTest.java deleted file mode 100644 index 6d7e0dfe4..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/DomainsManagerTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.nacos.naming.core; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.naming.BaseTest; -import com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl; -import org.junit.Assert; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Spy; - -import java.util.List; - -public class DomainsManagerTest extends BaseTest { - - @Spy - @InjectMocks - private ServiceManager manager; - - @Mock - private DistroConsistencyServiceImpl consistencyService; - - @Test - public void easyRemoveDom() throws Exception { - Service service = new Service(TEST_SERVICE_NAME); - service.setNamespaceId(TEST_NAMESPACE); - manager.putService(service); - manager.easyRemoveService(TEST_NAMESPACE, TEST_SERVICE_NAME); - } - - @Test - public void easyRemoveDomNotExist() throws Exception { - expectedException.expect(NacosException.class); - expectedException.expectMessage("specified service not exist, serviceName : " + TEST_SERVICE_NAME); - manager.easyRemoveService(Constants.DEFAULT_NAMESPACE_ID, TEST_SERVICE_NAME); - } - - @Test - public void searchDom() { - Service service = new Service(TEST_SERVICE_NAME); - service.setNamespaceId(TEST_NAMESPACE); - manager.putService(service); - - List list = manager.searchServices(TEST_NAMESPACE, "(.*)test(.*)"); - Assert.assertNotNull(list); - Assert.assertEquals(1, list.size()); - Assert.assertEquals(TEST_SERVICE_NAME, list.get(0).getName()); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/HealthOperatorV1ImplTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/HealthOperatorV1ImplTest.java deleted file mode 100644 index 2e97686c6..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/HealthOperatorV1ImplTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 1999-2021 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.nacos.naming.core; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; -import com.alibaba.nacos.naming.push.UdpPushService; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * {@link HealthOperatorV1Impl} unit tests. - * - * @author chenglu - * @date 2021-08-03 22:19 - */ -@RunWith(MockitoJUnitRunner.class) -public class HealthOperatorV1ImplTest { - - private HealthOperatorV1Impl healthOperatorV1; - - @Mock - private ServiceManager serviceManager; - - @Mock - private UdpPushService pushService; - - @Before - public void setUp() { - healthOperatorV1 = new HealthOperatorV1Impl(serviceManager, pushService); - } - - @Test - public void testUpdateHealthStatusForPersistentInstance() { - try { - Service service = new Service(); - Map clusterMap = new HashMap<>(2); - Cluster cluster = Mockito.mock(Cluster.class); - clusterMap.put("C", cluster); - service.setClusterMap(clusterMap); - Instance instance = new Instance(); - instance.setIp("1.1.1.1"); - instance.setPort(8080); - Mockito.when(cluster.allIPs()).thenReturn(Collections.singletonList(instance)); - Mockito.when(cluster.getHealthChecker()).thenReturn(new AbstractHealthChecker.None()); - - Mockito.when(serviceManager.getService(Mockito.anyString(), Mockito.anyString())).thenReturn(service); - - healthOperatorV1.updateHealthStatusForPersistentInstance("A", "B", "C", "1.1.1.1", 8080, true); - } catch (NacosException e) { - e.printStackTrace(); - Assert.fail(e.getMessage()); - } - } - -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/HealthOperatorV2ImplTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/HealthOperatorV2ImplTest.java index 2e793e158..825fd6c03 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/HealthOperatorV2ImplTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/HealthOperatorV2ImplTest.java @@ -18,6 +18,7 @@ package com.alibaba.nacos.naming.core; import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.healthcheck.HealthCheckType; import com.alibaba.nacos.naming.core.v2.client.impl.ConnectionBasedClient; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate; @@ -72,7 +73,7 @@ public class HealthOperatorV2ImplTest { instance.setPort(8080); Mockito.when(cluster.getHealthyCheckType()).thenReturn(HealthCheckType.NONE.name()); Mockito.when(metadataManager.getServiceMetadata(Mockito.any())).thenReturn(Optional.of(metadata)); - + ConnectionBasedClient client = Mockito.mock(ConnectionBasedClient.class); Mockito.when(clientManager.getClient(Mockito.anyString())).thenReturn(client); diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/InstanceTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/InstanceTest.java deleted file mode 100644 index a2e04d67d..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/InstanceTest.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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.nacos.naming.core; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.naming.healthcheck.RsInfo; -import org.junit.Before; -import org.junit.Test; - -import java.util.Map; -import java.util.HashMap; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -public class InstanceTest { - - private Instance instance; - - @Before - public void before() { - instance = new Instance(); - } - - @Test - public void updateIp() { - instance.setIp("1.1.1.1"); - instance.setPort(1234); - instance.setWeight(5); - - assertEquals("1.1.1.1", instance.getIp()); - assertEquals(1234, instance.getPort()); - assertEquals(5, instance.getWeight(), 0.001); - } - - @Test - public void testToJsonWithAllParam() { - instance = new Instance("1.1.1.1", 1234, "TEST", "TENANT", "APP"); - String actual = instance.toJson(); - assertTrue(actual.contains("\"app\":\"APP\"")); - assertTrue(actual.contains("\"clusterName\":\"TEST\"")); - assertTrue(actual.contains("\"enabled\":true")); - assertTrue(actual.contains("\"ephemeral\":true")); - assertTrue(actual.contains("\"healthy\":true")); - assertTrue(actual.contains("\"instanceHeartBeatInterval\":5000")); - assertTrue(actual.contains("\"instanceHeartBeatTimeOut\":15000")); - assertTrue(actual.contains("\"instanceIdGenerator\":\"simple\"")); - assertTrue(actual.contains("\"ip\":\"1.1.1.1\"")); - assertTrue(actual.contains("\"ipDeleteTimeout\":30000")); - assertTrue(actual.contains("\"lastBeat\":" + instance.getLastBeat())); - assertTrue(actual.contains("\"marked\":false")); - assertTrue(actual.contains("\"metadata\":{}")); - assertTrue(actual.contains("\"port\":1234")); - assertTrue(actual.contains("\"tenant\":\"TENANT\"")); - assertTrue(actual.contains("\"weight\":1.0")); - assertFalse(actual.contains("\"mockValid\"")); - assertFalse(actual.contains("\"failCount\"")); - } - - @Test - public void testToJsonWithoutTenantAndApp() { - instance = new Instance("1.1.1.1", 1234, "TEST"); - String actual = instance.toJson(); - System.out.println(actual); - assertTrue(actual.contains("\"clusterName\":\"TEST\"")); - assertTrue(actual.contains("\"enabled\":true")); - assertTrue(actual.contains("\"ephemeral\":true")); - assertTrue(actual.contains("\"healthy\":true")); - assertTrue(actual.contains("\"instanceHeartBeatInterval\":5000")); - assertTrue(actual.contains("\"instanceHeartBeatTimeOut\":15000")); - assertTrue(actual.contains("\"instanceIdGenerator\":\"simple\"")); - assertTrue(actual.contains("\"ip\":\"1.1.1.1\"")); - assertTrue(actual.contains("\"ipDeleteTimeout\":30000")); - assertTrue(actual.contains("\"lastBeat\":" + instance.getLastBeat())); - assertTrue(actual.contains("\"marked\":false")); - assertTrue(actual.contains("\"metadata\":{}")); - assertTrue(actual.contains("\"port\":1234")); - assertTrue(actual.contains("\"weight\":1.0")); - assertFalse(actual.contains("\"app\"")); - assertFalse(actual.contains("\"tenant\":")); - assertFalse(actual.contains("\"mockValid\"")); - assertFalse(actual.contains("\"failCount\"")); - } - - @Test - @SuppressWarnings("checkstyle:linelength") - public void testFromJsonByJson() { - instance = Instance.fromJson( - "{\"clusterName\":\"TEST\",\"enabled\":true,\"ephemeral\":true,\"healthy\":true,\"instanceHeartBeatInterval\":5000,\"instanceHeartBeatTimeOut\":15000,\"instanceIdGenerator\":\"simple\",\"ip\":\"1.1.1.1\",\"ipDeleteTimeout\":30000,\"lastBeat\":1590043805463,\"marked\":false,\"metadata\":{},\"port\":1234,\"weight\":1.0}\n"); - assertEquals("1.1.1.1", instance.getIp()); - assertEquals(1234, instance.getPort()); - assertEquals("TEST", instance.getClusterName()); - assertNull(instance.getApp()); - assertNull(instance.getTenant()); - } - - @Test - public void testFromJsonByNoJson() { - instance = Instance.fromJson("2.2.2.2:8888_2_TEST1"); - assertEquals("2.2.2.2", instance.getIp()); - assertEquals(8888, instance.getPort()); - assertEquals(2, instance.getWeight(), 0.001); - assertEquals("TEST1", instance.getClusterName()); - } - - @Test - public void rsInfo() throws Exception { - RsInfo info = new RsInfo(); - Map metadata = new HashMap<>(); - metadata.put("version", "2222"); - info.setMetadata(metadata); - System.out.println(JacksonUtils.toJson(info)); - - String json = JacksonUtils.toJson(info); - RsInfo info1 = JacksonUtils.toObj(json, RsInfo.class); - System.out.println(info1); - } - - @Test(expected = NacosException.class) - public void testIpValidate() throws NacosException { - Instance instance1 = new Instance("192.168.1.3d", 8080); - instance1.validate(); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/InstancesTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/InstancesTest.java deleted file mode 100644 index 4791919d1..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/InstancesTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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.nacos.naming.core; - -import org.junit.Ignore; -import org.junit.Test; - -import java.math.BigInteger; -import java.nio.charset.Charset; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; - -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.common.utils.StringUtils; - -import static org.junit.Assert.assertEquals; - -public class InstancesTest { - - private static MessageDigest messageDigest; - - static { - try { - messageDigest = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - messageDigest = null; - } - } - - private static final ThreadLocal MESSAGE_DIGEST_LOCAL = new ThreadLocal() { - @Override - protected MessageDigest initialValue() { - try { - return MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - return null; - } - } - }; - - @Test - public void checkSumNotThreadSafe() throws Exception { - - final AtomicBoolean catchException = new AtomicBoolean(false); - CountDownLatch countDownLatch = new CountDownLatch(4); - - for (int i = 0; i < 4; i++) { - Thread thread = new Thread(new Runnable() { - @Override - public void run() { - while (true) { - if (catchException.get()) { - break; - } - try { - checkSumThreadNotSafeVersion("test"); - } catch (ArrayIndexOutOfBoundsException e) { - catchException.set(true); - } - } - countDownLatch.countDown(); - } - }); - thread.start(); - } - - countDownLatch.await(); - - assert catchException.get(); - } - - @Test - public void testToString() { - Instances actual = new Instances(); - Collection instancesCase = createInstancesCase(); - actual.getInstanceList().addAll(instancesCase); - String expected = "{\"instanceList\":[" + StringUtils - .join(instancesCase.stream().map(Instance::toJson).collect(Collectors.toList()), ",") + "]}"; - assertEquals(expected, actual.toString()); - } - - @Test - public void testDeserializeFromJson() throws Exception { - Collection expected = createInstancesCase(); - String instancesJson = "{\"instanceList\":[" + StringUtils - .join(expected.stream().map(Instance::toJson).collect(Collectors.toList()), ",") + "]}"; - Instances actual = JacksonUtils.toObj(instancesJson, Instances.class); - assertEquals(expected, actual.getInstanceList()); - } - - private Collection createInstancesCase() { - Collection result = new ArrayList<>(); - Instance instanceWithBasicParam = new Instance("1.1.1.1", 1111); - Instance instanceWithCluster = new Instance("1.1.1.1", 1112, "TEST"); - Instance instanceWithAllParam = new Instance("1.1.1.1", 1112, "TEST", "TENANT", "APP"); - result.add(instanceWithBasicParam); - result.add(instanceWithCluster); - result.add(instanceWithAllParam); - return result; - } - - @Test - @Ignore("č·‘čµ·ę„ę…¢ļ¼Œę‰€ä»„先åæ½ē•„") - public void checkSumThreadSafe() throws Exception { - - CountDownLatch countDownLatch = new CountDownLatch(4); - - for (int i = 0; i < 4; i++) { - Thread thread = new Thread(new Runnable() { - @Override - public void run() { - for (int j = 0; j < Integer.MAX_VALUE; j++) { - checkSumThreadSafeVersion("test"); - } - countDownLatch.countDown(); - } - }); - thread.start(); - } - countDownLatch.await(); - } - - private String checkSumThreadNotSafeVersion(String checkString) { - return new BigInteger(1, messageDigest.digest((checkString).getBytes(Charset.forName("UTF-8")))).toString(16); - } - - private String checkSumThreadSafeVersion(String checkString) { - return new BigInteger(1, MESSAGE_DIGEST_LOCAL.get().digest((checkString).getBytes(Charset.forName("UTF-8")))) - .toString(16); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/ServiceManagerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/ServiceManagerTest.java deleted file mode 100644 index 925aec34d..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/ServiceManagerTest.java +++ /dev/null @@ -1,478 +0,0 @@ -/* - * 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.nacos.naming.core; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.PreservedMetadataKeys; -import com.alibaba.nacos.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.core.cluster.ServerMemberManager; -import com.alibaba.nacos.naming.BaseTest; -import com.alibaba.nacos.naming.consistency.ConsistencyService; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.core.ServiceManager.ServiceChecksum; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteEventListener; -import com.alibaba.nacos.naming.misc.Message; -import com.alibaba.nacos.naming.misc.Synchronizer; -import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.springframework.test.util.ReflectionTestUtils; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static com.alibaba.nacos.naming.misc.UtilsAndCommons.UPDATE_INSTANCE_METADATA_ACTION_REMOVE; -import static com.alibaba.nacos.naming.misc.UtilsAndCommons.UPDATE_INSTANCE_METADATA_ACTION_UPDATE; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class ServiceManagerTest extends BaseTest { - - private ServiceManager serviceManager; - - @Mock - private ConsistencyService consistencyService; - - @Mock - private Synchronizer synchronizer; - - @Mock - private ServerMemberManager serverMemberManager; - - @Mock - private DoubleWriteEventListener doubleWriteEventListener; - - private Service service; - - private Cluster cluster; - - private Instance instance; - - private Instance instance2; - - private List serviceNames; - - @Before - public void before() { - super.before(); - serviceManager = new ServiceManager(switchDomain, distroMapper, serverMemberManager, pushService, peerSet); - ReflectionTestUtils.setField(serviceManager, "consistencyService", consistencyService); - ReflectionTestUtils.setField(serviceManager, "synchronizer", synchronizer); - mockInjectSwitchDomain(); - mockInjectDistroMapper(); - mockService(); - mockCluster(); - mockInstance(); - mockServiceName(); - when(context.getBean(DoubleWriteEventListener.class)).thenReturn(doubleWriteEventListener); - } - - @After - public void tearDown() throws Exception { - service.destroy(); - } - - private void mockService() { - service = new Service(TEST_SERVICE_NAME); - service.setNamespaceId(TEST_NAMESPACE); - } - - private void mockCluster() { - cluster = new Cluster(TEST_CLUSTER_NAME, service); - } - - private void mockInstance() { - instance = new Instance("1.1.1.1", 1, TEST_CLUSTER_NAME); - Map metadata = new HashMap<>(); - metadata.put("key1", "value1"); - instance.setMetadata(metadata); - instance2 = new Instance("2.2.2.2", 2); - } - - private void mockServiceName() { - serviceNames = new ArrayList<>(5); - for (int i = 0; i < 32; i++) { - serviceNames.add(String.valueOf(i)); - } - } - - @Test - public void testGetAllNamespaces() throws NacosException { - assertTrue(serviceManager.getAllNamespaces().isEmpty()); - serviceManager.createEmptyService(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - assertFalse(serviceManager.getAllNamespaces().isEmpty()); - assertEquals(1, serviceManager.getAllNamespaces().size()); - assertEquals(TEST_NAMESPACE, serviceManager.getAllNamespaces().iterator().next()); - } - - @Test - public void testGetAllServiceNames() throws NacosException { - assertTrue(serviceManager.getAllServiceNames().isEmpty()); - serviceManager.createEmptyService(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - assertFalse(serviceManager.getAllServiceNames().isEmpty()); - assertEquals(1, serviceManager.getAllServiceNames().size()); - assertEquals(1, serviceManager.getAllServiceNames(TEST_NAMESPACE).size()); - assertEquals(TEST_SERVICE_NAME, serviceManager.getAllServiceNames(TEST_NAMESPACE).iterator().next()); - } - - @Test - public void testGetAllServiceNamesOrder() throws NacosException { - assertTrue(serviceManager.getAllServiceNames().isEmpty()); - for (String serviceName : serviceNames) { - serviceManager.createEmptyService(TEST_NAMESPACE, serviceName, true); - } - assertFalse(serviceManager.getAllServiceNames().isEmpty()); - assertEquals(1, serviceManager.getAllServiceNames().size()); - assertEquals(serviceNames.size(), serviceManager.getAllServiceNames(TEST_NAMESPACE).size()); - Collections.sort(serviceNames); - Iterator iterator = serviceManager.getAllServiceNames(TEST_NAMESPACE).iterator(); - int index = 0; - while (iterator.hasNext()) { - String next = iterator.next(); - assertEquals(next, serviceNames.get(index)); - index++; - } - } - - @Test - public void testGetAllServiceNameList() throws NacosException { - assertTrue(serviceManager.getAllServiceNameList(TEST_NAMESPACE).isEmpty()); - serviceManager.createEmptyService(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - assertFalse(serviceManager.getAllServiceNameList(TEST_NAMESPACE).isEmpty()); - assertEquals(1, serviceManager.getAllServiceNameList(TEST_NAMESPACE).size()); - assertEquals(TEST_SERVICE_NAME, serviceManager.getAllServiceNameList(TEST_NAMESPACE).get(0)); - } - - @Test - public void testGetAllServiceNameListOrder() throws NacosException { - assertTrue(serviceManager.getAllServiceNameList(TEST_NAMESPACE).isEmpty()); - for (String serviceName : serviceNames) { - serviceManager.createEmptyService(TEST_NAMESPACE, serviceName, true); - } - assertFalse(serviceManager.getAllServiceNameList(TEST_NAMESPACE).isEmpty()); - assertEquals(serviceNames.size(), serviceManager.getAllServiceNameList(TEST_NAMESPACE).size()); - List allServiceNameList = serviceManager.getAllServiceNameList(TEST_NAMESPACE); - Collections.sort(serviceNames); - for (int i = 0; i < allServiceNameList.size(); i++) { - assertEquals(allServiceNameList.get(i), serviceNames.get(i)); - } - } - - @Test - public void testGetResponsibleServices() throws NacosException { - when(distroMapper.responsible(TEST_SERVICE_NAME)).thenReturn(true); - assertEquals(0, serviceManager.getResponsibleServiceCount()); - serviceManager.createEmptyService(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - assertEquals(1, serviceManager.getResponsibleServiceCount()); - assertEquals(TEST_SERVICE_NAME, - serviceManager.getResponsibleServices().get(TEST_NAMESPACE).iterator().next().getName()); - } - - @Test - public void getResponsibleInstanceCount() throws NacosException { - when(distroMapper.responsible(TEST_SERVICE_NAME)).thenReturn(true); - assertEquals(0, serviceManager.getResponsibleInstanceCount()); - serviceManager.createEmptyService(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - Service service = serviceManager.getService(TEST_NAMESPACE, TEST_SERVICE_NAME); - service.addCluster(cluster); - ((Set) ReflectionTestUtils.getField(cluster, "ephemeralInstances")).add(instance); - assertEquals(1, serviceManager.getResponsibleInstanceCount()); - } - - @Test - public void testCreateEmptyServiceForEphemeral() throws NacosException { - assertFalse(serviceManager.containService(TEST_NAMESPACE, TEST_SERVICE_NAME)); - assertEquals(0, serviceManager.getServiceCount()); - serviceManager.createServiceIfAbsent(TEST_NAMESPACE, TEST_SERVICE_NAME, true, - new Cluster(TEST_CLUSTER_NAME, service)); - assertTrue(serviceManager.containService(TEST_NAMESPACE, TEST_SERVICE_NAME)); - assertEquals(1, serviceManager.getServiceCount()); - verify(consistencyService).listen(eq(KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, true)), - any(Service.class)); - verify(consistencyService).listen(eq(KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, false)), - any(Service.class)); - verify(consistencyService, never()) - .put(eq(KeyBuilder.buildServiceMetaKey(TEST_NAMESPACE, TEST_SERVICE_NAME)), any(Service.class)); - } - - @Test - public void testCreateEmptyServiceForPersistent() throws NacosException { - assertFalse(serviceManager.containService(TEST_NAMESPACE, TEST_SERVICE_NAME)); - assertEquals(0, serviceManager.getServiceCount()); - serviceManager.createServiceIfAbsent(TEST_NAMESPACE, TEST_SERVICE_NAME, false, - new Cluster(TEST_CLUSTER_NAME, service)); - assertTrue(serviceManager.containService(TEST_NAMESPACE, TEST_SERVICE_NAME)); - assertEquals(1, serviceManager.getServiceCount()); - verify(consistencyService).listen(eq(KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, true)), - any(Service.class)); - verify(consistencyService).listen(eq(KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, false)), - any(Service.class)); - verify(consistencyService) - .put(eq(KeyBuilder.buildServiceMetaKey(TEST_NAMESPACE, TEST_SERVICE_NAME)), any(Service.class)); - } - - @Test - public void testEasyRemoveServiceSuccessfully() throws Exception { - serviceManager.createEmptyService(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - serviceManager.easyRemoveService(TEST_NAMESPACE, TEST_SERVICE_NAME); - verify(consistencyService).remove(KeyBuilder.buildServiceMetaKey(TEST_NAMESPACE, TEST_SERVICE_NAME)); - } - - @Test - public void testEasyRemoveServiceFailed() throws Exception { - expectedException.expect(NacosException.class); - expectedException.expectMessage("specified service not exist, serviceName : " + TEST_SERVICE_NAME); - serviceManager.easyRemoveService(TEST_NAMESPACE, TEST_SERVICE_NAME); - } - - @Test - public void testRegisterInstance() throws NacosException { - assertEquals(0, serviceManager.getInstanceCount()); - serviceManager.registerInstance(TEST_NAMESPACE, TEST_SERVICE_NAME, instance); - String instanceListKey = KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - verify(consistencyService).put(eq(instanceListKey), any(Instances.class)); - } - - @Test - public void testUpdateInstance() throws NacosException { - serviceManager.createEmptyService(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - Service service = serviceManager.getService(TEST_NAMESPACE, TEST_SERVICE_NAME); - service.addCluster(cluster); - ((Set) ReflectionTestUtils.getField(cluster, "ephemeralInstances")).add(instance); - serviceManager.updateInstance(TEST_NAMESPACE, TEST_SERVICE_NAME, instance); - String instanceListKey = KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - verify(consistencyService).put(eq(instanceListKey), any(Instances.class)); - } - - @Test - public void testUpdateMetadata() throws NacosException { - - serviceManager.createEmptyService(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - - List instanceList = new LinkedList<>(); - Datum datam = new Datum(); - datam.key = KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - Instances instances = new Instances(); - instanceList.add(instance); - instanceList.add(instance2); - instances.setInstanceList(instanceList); - datam.value = instances; - when(consistencyService.get(KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, true))) - .thenReturn(datam); - - Instance updateMetadataInstance = new Instance(); - updateMetadataInstance.setIp(instance.getIp()); - updateMetadataInstance.setPort(instance.getPort()); - updateMetadataInstance.setClusterName(cluster.getName()); - updateMetadataInstance.setEphemeral(instance.isEphemeral()); - - Map updateMetadata = new HashMap<>(16); - updateMetadata.put("key1", "new-value1"); - updateMetadata.put("key2", "value2"); - updateMetadataInstance.setMetadata(updateMetadata); - - //all=false, update input instances - serviceManager - .updateMetadata(TEST_NAMESPACE, TEST_SERVICE_NAME, true, UPDATE_INSTANCE_METADATA_ACTION_UPDATE, false, - CollectionUtils.list(updateMetadataInstance), updateMetadata); - - assertEquals(instance.getMetadata().get("key1"), "new-value1"); - assertEquals(instance.getMetadata().get("key2"), "value2"); - - //all=true, update all instances - serviceManager - .updateMetadata(TEST_NAMESPACE, TEST_SERVICE_NAME, true, UPDATE_INSTANCE_METADATA_ACTION_UPDATE, true, - null, updateMetadata); - - assertEquals(instance2.getMetadata().get("key1"), "new-value1"); - assertEquals(instance2.getMetadata().get("key2"), "value2"); - - Instance deleteMetadataInstance = new Instance(); - deleteMetadataInstance.setIp(instance.getIp()); - deleteMetadataInstance.setPort(instance.getPort()); - deleteMetadataInstance.setClusterName(cluster.getName()); - deleteMetadataInstance.setEphemeral(instance.isEphemeral()); - Map deleteMetadata = new HashMap<>(16); - deleteMetadata.put("key2", null); - deleteMetadata.put("key3", null); - updateMetadataInstance.setMetadata(deleteMetadata); - - serviceManager - .updateMetadata(TEST_NAMESPACE, TEST_SERVICE_NAME, true, UPDATE_INSTANCE_METADATA_ACTION_REMOVE, false, - CollectionUtils.list(deleteMetadataInstance), deleteMetadata); - - assertEquals(instance.getMetadata().get("key1"), "new-value1"); - assertNull(instance.getMetadata().get("key2")); - assertNull(instance.getMetadata().get("key3")); - - serviceManager - .updateMetadata(TEST_NAMESPACE, TEST_SERVICE_NAME, true, UPDATE_INSTANCE_METADATA_ACTION_REMOVE, true, - null, deleteMetadata); - - assertEquals(instance2.getMetadata().get("key1"), "new-value1"); - assertNull(instance2.getMetadata().get("key2")); - assertNull(instance2.getMetadata().get("key3")); - } - - @Test - public void testRemoveInstance() throws NacosException { - serviceManager.createEmptyService(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - serviceManager.removeInstance(TEST_NAMESPACE, TEST_SERVICE_NAME, true, instance); - String instanceListKey = KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - verify(consistencyService).put(eq(instanceListKey), any(Instances.class)); - } - - @Test - public void testGetInstance() throws NacosException { - assertNull(serviceManager.getInstance(TEST_NAMESPACE, TEST_SERVICE_NAME, TEST_CLUSTER_NAME, "1.1.1.1", 1)); - serviceManager.createEmptyService(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - assertNull(serviceManager.getInstance(TEST_NAMESPACE, TEST_SERVICE_NAME, TEST_CLUSTER_NAME, "1.1.1.1", 1)); - Service service = serviceManager.getService(TEST_NAMESPACE, TEST_SERVICE_NAME); - service.addCluster(cluster); - ((Set) ReflectionTestUtils.getField(cluster, "ephemeralInstances")).add(instance); - assertEquals(instance, - serviceManager.getInstance(TEST_NAMESPACE, TEST_SERVICE_NAME, TEST_CLUSTER_NAME, "1.1.1.1", 1)); - assertNull(serviceManager.getInstance(TEST_NAMESPACE, TEST_SERVICE_NAME, TEST_CLUSTER_NAME, "2.2.2.2", 2)); - } - - @Test - public void testUpdateIpAddresses() throws Exception { - List instanceList = serviceManager - .updateIpAddresses(service, UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD, true, instance); - Assert.assertEquals(1, instanceList.size()); - Assert.assertEquals(instance, instanceList.get(0)); - Assert.assertEquals(1, service.getClusterMap().size()); - Assert.assertEquals(new Cluster(instance.getClusterName(), service), - service.getClusterMap().get(TEST_CLUSTER_NAME)); - - Datum datam = new Datum(); - datam.key = KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - Instances instances = new Instances(); - instanceList.add(instance2); - instances.setInstanceList(instanceList); - datam.value = instances; - when(consistencyService.get(KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, true))) - .thenReturn(datam); - service.getClusterMap().get(TEST_CLUSTER_NAME).updateIps(instanceList, true); - instanceList = serviceManager - .updateIpAddresses(service, UtilsAndCommons.UPDATE_INSTANCE_ACTION_REMOVE, true, instance); - Assert.assertEquals(1, instanceList.size()); - Assert.assertEquals(instance2, instanceList.get(0)); - Assert.assertEquals(1, service.getClusterMap().size()); - Assert.assertEquals(new Cluster(instance.getClusterName(), service), - service.getClusterMap().get(TEST_CLUSTER_NAME)); - } - - @Test - public void testUpdateIpAddressesNoInstance() throws Exception { - expectedException.expect(IllegalArgumentException.class); - expectedException - .expectMessage(String.format("ip list can not be empty, service: %s, ip list: []", TEST_SERVICE_NAME)); - serviceManager.updateIpAddresses(service, UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD, true); - } - - @Test - public void testSearchServices() throws NacosException { - serviceManager.createEmptyService(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - List actual = serviceManager - .searchServices(TEST_NAMESPACE, Constants.ANY_PATTERN + TEST_SERVICE_NAME + Constants.ANY_PATTERN); - assertEquals(1, actual.size()); - assertEquals(TEST_SERVICE_NAME, actual.get(0).getName()); - } - - @Test - public void testGetPagedService() throws NacosException { - serviceManager.createEmptyService(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - Service service = serviceManager.getService(TEST_NAMESPACE, TEST_SERVICE_NAME); - service.addCluster(cluster); - ((Set) ReflectionTestUtils.getField(cluster, "ephemeralInstances")).add(instance); - List actualServices = new ArrayList<>(8); - int actualSize = serviceManager - .getPagedService(TEST_NAMESPACE, 0, 10, StringUtils.EMPTY, "1.1.1.1:1", actualServices, true); - assertEquals(1, actualSize); - assertEquals(TEST_SERVICE_NAME, actualServices.get(0).getName()); - } - - @Test - public void testSnowflakeInstanceId() throws Exception { - Map metaData = new HashMap<>(); - metaData.put(PreservedMetadataKeys.INSTANCE_ID_GENERATOR, Constants.SNOWFLAKE_INSTANCE_ID_GENERATOR); - - instance.setMetadata(metaData); - - instance2.setClusterName(TEST_CLUSTER_NAME); - instance2.setMetadata(metaData); - - List instanceList = serviceManager - .updateIpAddresses(service, UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD, true, instance, instance2); - Assert.assertNotNull(instanceList); - Assert.assertEquals(2, instanceList.size()); - int instanceId1 = Integer.parseInt(instance.getInstanceId()); - int instanceId2 = Integer.parseInt(instance2.getInstanceId()); - Assert.assertNotEquals(instanceId1, instanceId2); - } - - @Test - public void testUpdatedHealthStatus() { - String namespaceId = "namespaceId"; - String serviceName = "testService"; - String serverIp = "127.0.0.1"; - String example = "{\"ips\":[\"127.0.0.1:8848_true\"]}"; - Message message = new Message(); - message.setData(example); - when(synchronizer.get(serverIp, UtilsAndCommons.assembleFullServiceName(namespaceId, serviceName))) - .thenReturn(message); - serviceManager.updatedHealthStatus(namespaceId, serviceName, serverIp); - } - - @Test - public void testSerializeServiceChecksum() { - ServiceChecksum checksum = new ServiceChecksum(); - checksum.addItem("test", "1234567890"); - String actual = JacksonUtils.toJson(checksum); - assertTrue(actual.contains("\"namespaceId\":\"public\"")); - assertTrue(actual.contains("\"serviceName2Checksum\":{\"test\":\"1234567890\"}")); - } - - @Test(expected = NacosException.class) - public void testCheckServiceIsNull() throws NacosException { - serviceManager.createEmptyService(TEST_NAMESPACE, TEST_SERVICE_NAME, true); - String serviceName = "order-service"; - Service service = serviceManager.getService(TEST_NAMESPACE, serviceName); - serviceManager.checkServiceIsNull(service, TEST_NAMESPACE, serviceName); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/ServiceOperatorV1ImplTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/ServiceOperatorV1ImplTest.java deleted file mode 100644 index 3a675460b..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/ServiceOperatorV1ImplTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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.nacos.naming.core; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.utils.NamingUtils; -import com.alibaba.nacos.naming.BaseTest; -import com.alibaba.nacos.naming.consistency.ConsistencyService; -import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.springframework.test.util.ReflectionTestUtils; - -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; - -public class ServiceOperatorV1ImplTest extends BaseTest { - - private ServiceOperatorV1Impl serviceOperatorV1Impl; - - @InjectMocks - private ServiceManager serviceManager; - - @InjectMocks - private DistroMapper distroMapper; - - @Mock - private ConsistencyService consistencyService; - - @Before - public void setUp() { - ReflectionTestUtils.setField(serviceManager, "consistencyService", consistencyService); - serviceOperatorV1Impl = new ServiceOperatorV1Impl(serviceManager, distroMapper); - } - - @Test - public void testUpdate() throws NacosException { - String serviceName = "DEFAULT_GROUP@@order-service"; - serviceManager.createEmptyService(TEST_NAMESPACE, serviceName, true); - com.alibaba.nacos.naming.core.v2.pojo.Service service = Service - .newService(TEST_NAMESPACE, NamingUtils.getGroupName(serviceName), - NamingUtils.getServiceName(serviceName)); - ServiceMetadata metadata = new ServiceMetadata(); - metadata.setProtectThreshold(0.1F); - serviceOperatorV1Impl.update(service, metadata); - assertThat(serviceManager.getService(TEST_NAMESPACE, serviceName).getProtectThreshold(), is(0.1F)); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/ServiceOperatorV2ImplTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/ServiceOperatorV2ImplTest.java index 550b578eb..cb4e49d7d 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/ServiceOperatorV2ImplTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/ServiceOperatorV2ImplTest.java @@ -144,7 +144,7 @@ public class ServiceOperatorV2ImplTest { @Test public void testSearchServiceName() throws NacosException { - Collection res = serviceOperatorV2.searchServiceName("A", "", true); + Collection res = serviceOperatorV2.searchServiceName("A", ""); Assert.assertEquals(1, res.size()); } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/ServiceTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/ServiceTest.java deleted file mode 100644 index 0ce7efe3e..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/ServiceTest.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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.nacos.naming.core; - -import com.alibaba.nacos.api.selector.SelectorType; -import com.alibaba.nacos.common.utils.JacksonUtils; -import com.alibaba.nacos.naming.BaseTest; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteEventListener; -import com.alibaba.nacos.naming.selector.NoneSelector; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; - -import java.util.ArrayList; -import java.util.List; - -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; - -public class ServiceTest extends BaseTest { - - private Service service; - - @Mock - private DoubleWriteEventListener doubleWriteEventListener; - - @Before - public void before() { - super.before(); - when(context.getBean(DoubleWriteEventListener.class)).thenReturn(doubleWriteEventListener); - service = new Service("test-service"); - mockInjectPushServer(); - mockInjectHealthCheckProcessor(); - mockInjectDistroMapper(); - mockInjectSwitchDomain(); - } - - @After - public void tearDown() throws Exception { - service.destroy(); - } - - @Test - public void testUpdateIPs() { - List instances = new ArrayList<>(); - Instance instance = new Instance("1.1.1.1", 1, "test-instance1"); - instances.add(instance); - service.updateIPs(instances, true); - Assert.assertEquals(instances, service.allIPs(true)); - - instances = new ArrayList<>(); - instance = new Instance(); - instance.setIp("2.2.2.2"); - instance.setPort(2); - instances.add(instance); - instances.add(null); - service.updateIPs(instances, true); - instances.remove(null); - Assert.assertEquals(instances, service.allIPs(true)); - } - - @Test - public void testSerialize() throws Exception { - String actual = new Service("test-service").toJson(); - System.out.println(actual); - assertTrue(actual.contains("\"checksum\":\"394c845e1160bb880e7f26fb2149ed6d\"")); - assertTrue(actual.contains("\"clusterMap\":{}")); - assertTrue(actual.contains("\"empty\":true")); - assertTrue(actual.contains("\"enabled\":true")); - assertTrue(actual.contains("\"finalizeCount\":0")); - assertTrue(actual.contains("\"ipDeleteTimeout\":30000")); - assertTrue(actual.contains("\"lastModifiedMillis\":0")); - assertTrue(actual.contains("\"metadata\":{}")); - assertTrue(actual.contains("\"name\":\"test-service\"")); - assertTrue(actual.contains("\"owners\":[]")); - assertTrue(actual.contains("\"protectThreshold\":0.0")); - assertTrue(actual.contains("\"resetWeight\":false")); - assertFalse(actual.contains("clientBeatCheckTask")); - assertFalse(actual.contains("serviceString")); - assertFalse(actual.contains("pushService")); - } - - @Test - @SuppressWarnings("checkstyle:linelength") - public void testDeserialize() throws Exception { - JacksonUtils.registerSubtype(NoneSelector.class, SelectorType.none.name()); - String example = "{\"checksum\":\"394c845e1160bb880e7f26fb2149ed6d\",\"clusterMap\":{},\"empty\":true,\"enabled\":true,\"finalizeCount\":0,\"ipDeleteTimeout\":30000,\"lastModifiedMillis\":0,\"metadata\":{},\"name\":\"test-service\",\"owners\":[],\"protectThreshold\":0.0,\"resetWeight\":false,\"selector\":{\"type\":\"none\"}}"; - Service actual = JacksonUtils.toObj(example, Service.class); - assertEquals("394c845e1160bb880e7f26fb2149ed6d", actual.getChecksum()); - assertEquals("test-service", actual.getName()); - assertTrue(actual.getClusterMap().isEmpty()); - assertTrue(actual.isEmpty()); - assertTrue(actual.getEnabled()); - assertTrue(actual.getMetadata().isEmpty()); - assertTrue(actual.getOwners().isEmpty()); - assertEquals(0, actual.getFinalizeCount()); - assertEquals(30000, actual.getIpDeleteTimeout()); - assertEquals(0, actual.getLastModifiedMillis()); - assertEquals(0, actual.getLastModifiedMillis()); - assertEquals(0.0, actual.getProtectThreshold(), 0); - assertFalse(actual.getResetWeight()); - assertThat(actual.getSelector(), instanceOf(NoneSelector.class)); - } - - @Test - public void testGetServiceString() { - String actual = service.getServiceString(); - assertTrue(actual.contains("\"invalidIPCount\":0")); - assertTrue(actual.contains("\"name\":\"test-service\"")); - assertTrue(actual.contains("\"ipCount\":0")); - assertTrue(actual.contains("\"owners\":[]")); - assertTrue(actual.contains("\"protectThreshold\":0.0")); - assertTrue(actual.contains("\"clusters\":[]")); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/cleaner/EmptyServiceAutoCleanerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/cleaner/EmptyServiceAutoCleanerTest.java deleted file mode 100644 index 7630c5e5a..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/cleaner/EmptyServiceAutoCleanerTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 1999-2021 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.nacos.naming.core.v2.cleaner; - -import com.alibaba.nacos.naming.core.DistroMapper; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.ServiceManager; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * {@link EmptyServiceAutoCleaner} unit test. - * - * @author chenglu - * @date 2021-07-21 12:31 - */ -@RunWith(MockitoJUnitRunner.class) -public class EmptyServiceAutoCleanerTest { - - @Mock - private ServiceManager serviceManager; - - @Mock - private DistroMapper distroMapper; - - private EmptyServiceAutoCleaner emptyServiceAutoCleaner; - - @Before - public void setUp() { - emptyServiceAutoCleaner = new EmptyServiceAutoCleaner(serviceManager, distroMapper); - } - - @Test - public void testRun() { - try { - Mockito.when(serviceManager.getAllNamespaces()).thenReturn(Collections.singleton("test")); - - Map serviceMap = new HashMap<>(2); - Service service = new Service(); - serviceMap.put("test", service); - Mockito.when(serviceManager.chooseServiceMap(Mockito.anyString())).thenReturn(serviceMap); - - Mockito.when(distroMapper.responsible(Mockito.anyString())).thenReturn(true); - - emptyServiceAutoCleaner.run(); - } catch (Exception e) { - e.printStackTrace(); - Assert.fail(e.getMessage()); - } - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/AbstractClientTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/AbstractClientTest.java index a66188264..c26e2894f 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/AbstractClientTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/AbstractClientTest.java @@ -18,6 +18,7 @@ package com.alibaba.nacos.naming.core.v2.client; import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.monitor.MetricsMonitor; import com.alibaba.nacos.naming.pojo.Subscriber; import org.junit.Assert; import org.junit.Before; @@ -27,13 +28,6 @@ import org.mockito.junit.MockitoJUnitRunner; import java.util.Collection; -/** - * AbstractClient test. - * @ClassName: AbstractClientTest - * @Author: ChenHao26 - * @Date: 2022/8/3 16:22 - * @Description: TODO - */ @RunWith(MockitoJUnitRunner.class) public class AbstractClientTest { @@ -47,21 +41,23 @@ public class AbstractClientTest { @Before public void setUp() { - abstractClient = new MockAbstractClient(); + abstractClient = new MockAbstractClient(0L); service = Service.newService("ns1", "group1", "serviceName001"); instancePublishInfo = new InstancePublishInfo("127.0.0.1", 8890); subscriber = new Subscriber("127.0.0.1:8848", "agent1", "appName", "127.0.0.1", "ns1", "serviceName001", 9090); - addServiceInstance(); - addServiceSubscriber(); + MetricsMonitor.getIpCountMonitor().set(0); + MetricsMonitor.getSubscriberCount().set(0); } - private void addServiceInstance() { + @Test + public void addServiceInstance() { boolean result = abstractClient.addServiceInstance(service, instancePublishInfo); Assert.assertTrue(result); } - private void addServiceSubscriber() { + @Test + public void addServiceSubscriber() { Assert.assertTrue(abstractClient.addServiceSubscriber(service, subscriber)); } @@ -72,12 +68,14 @@ public class AbstractClientTest { @Test public void removeServiceInstanceSuccess() { + addServiceInstance(); InstancePublishInfo publishInfo = abstractClient.removeServiceInstance(service); Assert.assertNotNull(publishInfo); } @Test public void getInstancePublishInfo() { + addServiceInstance(); InstancePublishInfo publishInfo = abstractClient.getInstancePublishInfo(service); Assert.assertNotNull(publishInfo); } @@ -96,6 +94,7 @@ public class AbstractClientTest { @Test public void getSubscriber() { + addServiceSubscriber(); Subscriber subscriber1 = abstractClient.getSubscriber(service); Assert.assertNotNull(subscriber1); } @@ -114,11 +113,15 @@ public class AbstractClientTest { @Test public void release() { - try { - abstractClient.release(); - } catch (Exception e) { - e.printStackTrace(); - Assert.assertNotNull(e); - } + + abstractClient.addServiceInstance(service, instancePublishInfo); + Assert.assertEquals(1, MetricsMonitor.getIpCountMonitor().get()); + abstractClient.addServiceSubscriber(service, subscriber); + Assert.assertEquals(1, MetricsMonitor.getSubscriberCount().get()); + + abstractClient.release(); + + Assert.assertEquals(0, MetricsMonitor.getSubscriberCount().get()); + Assert.assertEquals(0, MetricsMonitor.getIpCountMonitor().get()); } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/MockAbstractClient.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/MockAbstractClient.java index 7bfd557ed..f9d2a261b 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/MockAbstractClient.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/MockAbstractClient.java @@ -16,15 +16,12 @@ package com.alibaba.nacos.naming.core.v2.client; -/** - * Mock AbstractClient. - * @ClassName: MockAbstractClient - * @Author: ChenHao26 - * @Date: 2022/8/3 16:25 - * @Description: TODO - */ public class MockAbstractClient extends AbstractClient { + public MockAbstractClient(Long revision) { + super(revision); + } + @Override public String getClientId() { return "-1"; diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/impl/ConnectionBasedClientTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/impl/ConnectionBasedClientTest.java index d5a61f7b7..0b9fc0acc 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/impl/ConnectionBasedClientTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/impl/ConnectionBasedClientTest.java @@ -20,6 +20,9 @@ import com.alibaba.nacos.naming.misc.ClientConfig; import org.junit.Before; import org.junit.Test; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class ConnectionBasedClientTest { @@ -32,7 +35,7 @@ public class ConnectionBasedClientTest { @Before public void setUp() throws Exception { - connectionBasedClient = new ConnectionBasedClient(connectionId, isNative); + connectionBasedClient = new ConnectionBasedClient(connectionId, isNative, null); } @Test @@ -47,4 +50,26 @@ public class ConnectionBasedClientTest { connectionBasedClient.getLastRenewTime() + 2 * ClientConfig.getInstance().getClientExpiredTime(); assertTrue(connectionBasedClient.isExpire(mustExpireTime)); } + + @Test + public void testRecalculateRevision() { + assertEquals(0, connectionBasedClient.getRevision()); + connectionBasedClient.recalculateRevision(); + assertEquals(1, connectionBasedClient.getRevision()); + } + + @Test + public void testRecalculateRevisionAsync() throws InterruptedException { + assertEquals(0, connectionBasedClient.getRevision()); + for (int i = 0; i < 10; i++) { + Thread thread = new Thread(() -> { + for (int j = 0; j < 10; j++) { + connectionBasedClient.recalculateRevision(); + } + }); + thread.start(); + } + TimeUnit.SECONDS.sleep(1); + assertEquals(100, connectionBasedClient.getRevision()); + } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/impl/IpPortBasedClientTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/impl/IpPortBasedClientTest.java index 28b1d9f94..15ad549b9 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/impl/IpPortBasedClientTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/impl/IpPortBasedClientTest.java @@ -53,7 +53,7 @@ public class IpPortBasedClientTest { @Before public void setUp() throws Exception { - ipPortBasedClient = new IpPortBasedClient(clientId, true); + ipPortBasedClient = new IpPortBasedClient(clientId, true, 123L); ipPortBasedClient.init(); instancePublishInfo = new InstancePublishInfo(); } @@ -84,6 +84,18 @@ public class IpPortBasedClientTest { assertEquals(allInstancePublishInfo.iterator().next(), instancePublishInfo); } + @Test + public void testRecalculateRevision() { + assertEquals(123L, ipPortBasedClient.getRevision()); + assertEquals(-1531701243L, ipPortBasedClient.recalculateRevision()); + } + + @Test + public void testConstructor0() { + IpPortBasedClient client = new IpPortBasedClient(clientId, true); + assertEquals(0, client.getRevision()); + } + @After public void tearDown() { ipPortBasedClient.release(); diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/ClientManagerDelegateTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/ClientManagerDelegateTest.java index f2e13e819..f06993f79 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/ClientManagerDelegateTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/ClientManagerDelegateTest.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.naming.core.v2.client.manager; +import com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo; import com.alibaba.nacos.naming.core.v2.client.manager.impl.ConnectionBasedClientManager; import com.alibaba.nacos.naming.core.v2.client.manager.impl.EphemeralIpPortClientManager; import com.alibaba.nacos.naming.core.v2.client.manager.impl.PersistentIpPortClientManager; @@ -86,18 +87,20 @@ public class ClientManagerDelegateTest { @Test public void testChooseEphemeralIpPortClient() { - delegate.verifyClient(ephemeralIpPortId); - verify(connectionBasedClientManager, never()).verifyClient(ephemeralIpPortId); - verify(ephemeralIpPortClientManager).verifyClient(ephemeralIpPortId); - verify(persistentIpPortClientManager, never()).verifyClient(ephemeralIpPortId); + DistroClientVerifyInfo verify = new DistroClientVerifyInfo(ephemeralIpPortId, 0); + delegate.verifyClient(verify); + verify(connectionBasedClientManager, never()).verifyClient(verify); + verify(ephemeralIpPortClientManager).verifyClient(verify); + verify(persistentIpPortClientManager, never()).verifyClient(verify); } @Test public void testChoosePersistentIpPortClient() { - delegate.verifyClient(persistentIpPortId); - verify(connectionBasedClientManager, never()).verifyClient(persistentIpPortId); - verify(ephemeralIpPortClientManager, never()).verifyClient(persistentIpPortId); - verify(persistentIpPortClientManager).verifyClient(persistentIpPortId); + DistroClientVerifyInfo verify = new DistroClientVerifyInfo(persistentIpPortId, 0); + delegate.verifyClient(verify); + verify(connectionBasedClientManager, never()).verifyClient(verify); + verify(ephemeralIpPortClientManager, never()).verifyClient(verify); + verify(persistentIpPortClientManager).verifyClient(verify); } @Test diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/ConnectionBasedClientManagerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/ConnectionBasedClientManagerTest.java index fa03fa937..c1d198595 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/ConnectionBasedClientManagerTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/ConnectionBasedClientManagerTest.java @@ -19,14 +19,19 @@ package com.alibaba.nacos.naming.core.v2.client.manager.impl; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.core.remote.Connection; import com.alibaba.nacos.core.remote.ConnectionMeta; +import com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo; +import com.alibaba.nacos.naming.constants.ClientConstants; import com.alibaba.nacos.naming.core.v2.client.ClientAttributes; import com.alibaba.nacos.naming.core.v2.client.impl.ConnectionBasedClient; +import com.alibaba.nacos.sys.env.EnvUtil; import org.junit.After; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.mock.env.MockEnvironment; import java.util.Collection; @@ -54,6 +59,11 @@ public class ConnectionBasedClientManagerTest { @Mock private ClientAttributes clientAttributes; + @BeforeClass + public static void setUpBeforeClass() { + EnvUtil.setEnvironment(new MockEnvironment()); + } + @Before public void setUp() throws Exception { connectionBasedClientManager = new ConnectionBasedClientManager(); @@ -62,8 +72,9 @@ public class ConnectionBasedClientManagerTest { when(connection.getMetaInfo()).thenReturn(connectionMeta); when(connectionMeta.getLabel(RemoteConstants.LABEL_MODULE)).thenReturn(RemoteConstants.LABEL_MODULE_NAMING); + when(clientAttributes.getClientAttribute(ClientConstants.REVISION, 0)).thenReturn(0); assertTrue(connectionBasedClientManager.syncClientConnected(connectionId, clientAttributes)); - assertTrue(connectionBasedClientManager.verifyClient(connectionId)); + assertTrue(connectionBasedClientManager.verifyClient(new DistroClientVerifyInfo(connectionId, 0))); connectionBasedClientManager.clientConnected(connection); } @@ -72,16 +83,16 @@ public class ConnectionBasedClientManagerTest { public void testAllClientId() { Collection allClientIds = connectionBasedClientManager.allClientId(); assertEquals(1, allClientIds.size()); - assertTrue(connectionBasedClientManager.verifyClient(connectionId)); + assertTrue(connectionBasedClientManager.verifyClient(new DistroClientVerifyInfo(connectionId, 0))); assertTrue(allClientIds.contains(connectionId)); } @Test public void testContainsConnectionId() { - assertTrue(connectionBasedClientManager.verifyClient(connectionId)); + assertTrue(connectionBasedClientManager.verifyClient(new DistroClientVerifyInfo(connectionId, 0))); assertTrue(connectionBasedClientManager.contains(connectionId)); String unUsedClientId = "127.0.0.1:8888#true"; - assertFalse(connectionBasedClientManager.verifyClient(unUsedClientId)); + assertFalse(connectionBasedClientManager.verifyClient(new DistroClientVerifyInfo(unUsedClientId, 0))); assertFalse(connectionBasedClientManager.contains(unUsedClientId)); } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/EphemeralIpPortClientManagerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/EphemeralIpPortClientManagerTest.java index 4fa786951..62c8b7ad1 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/EphemeralIpPortClientManagerTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/EphemeralIpPortClientManagerTest.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.naming.core.v2.client.manager.impl; +import com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo; +import com.alibaba.nacos.naming.constants.ClientConstants; import com.alibaba.nacos.naming.core.DistroMapper; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.ClientAttributes; @@ -67,7 +69,9 @@ public class EphemeralIpPortClientManagerTest { public void setUp() throws Exception { ephemeralIpPortClientManager = new EphemeralIpPortClientManager(distroMapper, switchDomain); when(client.getClientId()).thenReturn(ephemeralIpPortId); + when(client.getRevision()).thenReturn(1320L); ephemeralIpPortClientManager.clientConnected(client); + when(attributes.getClientAttribute(ClientConstants.REVISION, 0)).thenReturn(5120); ephemeralIpPortClientManager.syncClientConnected(syncedClientId, attributes); } @@ -92,4 +96,18 @@ public class EphemeralIpPortClientManagerTest { String unUsedClientId = "127.0.0.1:8888#true"; assertFalse(ephemeralIpPortClientManager.contains(unUsedClientId)); } + + @Test + public void testVerifyClient0() { + assertTrue(ephemeralIpPortClientManager.verifyClient(new DistroClientVerifyInfo(ephemeralIpPortId, 0))); + assertTrue(ephemeralIpPortClientManager.verifyClient(new DistroClientVerifyInfo(syncedClientId, 0))); + } + + @Test + public void testVerifyClient() { + assertFalse(ephemeralIpPortClientManager.verifyClient(new DistroClientVerifyInfo(ephemeralIpPortId, 1))); + assertTrue(ephemeralIpPortClientManager.verifyClient(new DistroClientVerifyInfo(ephemeralIpPortId, 1320))); + assertFalse(ephemeralIpPortClientManager.verifyClient(new DistroClientVerifyInfo(syncedClientId, 1))); + assertTrue(ephemeralIpPortClientManager.verifyClient(new DistroClientVerifyInfo(syncedClientId, 5120))); + } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/PersistentIpPortClientManagerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/PersistentIpPortClientManagerTest.java index 0533276bd..30d5931dc 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/PersistentIpPortClientManagerTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/client/manager/impl/PersistentIpPortClientManagerTest.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.naming.core.v2.client.manager.impl; +import com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo; import com.alibaba.nacos.naming.core.v2.client.ClientAttributes; import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; import org.junit.After; @@ -85,7 +86,7 @@ public class PersistentIpPortClientManagerTest { @Test(expected = UnsupportedOperationException.class) public void makeSureNoVerify() { - persistentIpPortClientManager.verifyClient(clientId); + persistentIpPortClientManager.verifyClient(new DistroClientVerifyInfo(clientId, 0)); } @Test diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/event/publisher/NamingEventPublisherTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/event/publisher/NamingEventPublisherTest.java index a80dd91a1..47ce15e80 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/event/publisher/NamingEventPublisherTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/event/publisher/NamingEventPublisherTest.java @@ -29,6 +29,7 @@ import org.mockito.junit.MockitoJUnitRunner; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -92,7 +93,7 @@ public class NamingEventPublisherTest { } namingEventPublisher.addSubscriber(subscriber, TestEvent.class); namingEventPublisher.publish(testEvent); - verify(subscriber).onEvent(testEvent); + verify(subscriber, atLeastOnce()).onEvent(testEvent); } @Test diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/metadata/ServiceMetadataProcessorTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/metadata/ServiceMetadataProcessorTest.java index 497bf9a59..76ed0cdfc 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/metadata/ServiceMetadataProcessorTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/metadata/ServiceMetadataProcessorTest.java @@ -28,7 +28,6 @@ import com.alibaba.nacos.naming.constants.Constants; import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteEventListener; import com.alibaba.nacos.sys.utils.ApplicationUtils; import com.google.protobuf.ByteString; import org.junit.Assert; @@ -45,7 +44,6 @@ import java.util.List; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class ServiceMetadataProcessorTest { @@ -65,16 +63,12 @@ public class ServiceMetadataProcessorTest { @Mock private ConfigurableApplicationContext context; - @Mock - private DoubleWriteEventListener doubleWriteEventListener; - private ServiceMetadataProcessor serviceMetadataProcessor; @Before public void setUp() throws Exception { Mockito.when(protocolManager.getCpProtocol()).thenReturn(cpProtocol); ApplicationUtils.injectContext(context); - when(context.getBean(DoubleWriteEventListener.class)).thenReturn(doubleWriteEventListener); serviceMetadataProcessor = new ServiceMetadataProcessor(namingMetadataManager, protocolManager, serviceStorage); } @@ -126,7 +120,6 @@ public class ServiceMetadataProcessorTest { Assert.assertTrue(addResponse.getSuccess()); verify(namingMetadataManager).getServiceMetadata(service); verify(namingMetadataManager).updateServiceMetadata(service, serviceMetadata); - verify(context).getBean(DoubleWriteEventListener.class); // CHANGE operation.set(defaultInstance, "CHANGE"); @@ -135,7 +128,6 @@ public class ServiceMetadataProcessorTest { Assert.assertTrue(changeResponse.getSuccess()); verify(namingMetadataManager, times(2)).getServiceMetadata(service); verify(namingMetadataManager).updateServiceMetadata(service, serviceMetadata); - verify(context, times(2)).getBean(DoubleWriteEventListener.class); // DELETE operation.set(defaultInstance, "DELETE"); @@ -158,4 +150,4 @@ public class ServiceMetadataProcessorTest { Assert.assertEquals(group, Constants.SERVICE_METADATA); } -} \ No newline at end of file +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/upgrade/MockSelfUpgradeChecker.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/upgrade/MockSelfUpgradeChecker.java deleted file mode 100644 index bb4da3f3f..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/upgrade/MockSelfUpgradeChecker.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade; - -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteDelayTaskEngine; - -public class MockSelfUpgradeChecker implements SelfUpgradeChecker { - - @Override - public String checkType() { - return "mock"; - } - - @Override - public boolean isReadyToUpgrade(ServiceManager serviceManager, DoubleWriteDelayTaskEngine taskEngine) { - return true; - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/upgrade/UpgradeJudgementTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/upgrade/UpgradeJudgementTest.java deleted file mode 100644 index f92d49d83..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/upgrade/UpgradeJudgementTest.java +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.core.v2.upgrade; - -import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.core.cluster.Member; -import com.alibaba.nacos.core.cluster.MemberMetaDataConstants; -import com.alibaba.nacos.core.cluster.MembersChangeEvent; -import com.alibaba.nacos.core.cluster.ServerMemberManager; -import com.alibaba.nacos.naming.consistency.persistent.ClusterVersionJudgement; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftCore; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftPeerSet; -import com.alibaba.nacos.naming.core.ServiceManager; -import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; -import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteDelayTaskEngine; -import com.alibaba.nacos.naming.monitor.MetricsMonitor; -import com.alibaba.nacos.sys.env.Constants; -import com.alibaba.nacos.sys.env.EnvUtil; -import com.alibaba.nacos.sys.utils.ApplicationUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.mock.env.MockEnvironment; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.atMostOnce; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class UpgradeJudgementTest { - - private final long sleepForCheck = 800L; - - @Mock - private ConfigurableApplicationContext context; - - @Mock - private RaftPeerSet raftPeerSet; - - @Mock - private RaftCore raftCore; - - @Mock - private ClusterVersionJudgement versionJudgement; - - @Mock - private ServerMemberManager memberManager; - - @Mock - private ServiceManager serviceManager; - - @Mock - private DoubleWriteDelayTaskEngine doubleWriteDelayTaskEngine; - - @Mock - private UpgradeStates upgradeStates; - - @Mock - private ServiceStorage serviceStorage; - - private UpgradeJudgement upgradeJudgement; - - @Before - public void setUp() throws Exception { - MockEnvironment environment = new MockEnvironment(); - environment.setProperty(Constants.SUPPORT_UPGRADE_FROM_1X, "true"); - EnvUtil.setEnvironment(environment); - EnvUtil.setIsStandalone(false); - when(context.getBean(ServiceManager.class)).thenReturn(serviceManager); - when(context.getBean(ServiceStorage.class)).thenReturn(serviceStorage); - ApplicationUtils.injectContext(context); - upgradeJudgement = new UpgradeJudgement(raftPeerSet, raftCore, versionJudgement, memberManager, serviceManager, - upgradeStates, doubleWriteDelayTaskEngine); - NotifyCenter.deregisterSubscriber(upgradeJudgement); - MetricsMonitor.getIpCountMonitor().set(0); - MetricsMonitor.getDomCountMonitor().set(0); - } - - @After - public void tearDown() { - upgradeJudgement.shutdown(); - EnvUtil.setEnvironment(null); - } - - @Test - public void testUpgradeOneNode() throws Exception { - Collection members = mockMember("1.3.2", "1.3.2", "2.0.0"); - upgradeJudgement.onEvent(MembersChangeEvent.builder().triggers(members).build()); - verify(raftPeerSet, never()).init(); - verify(raftCore, never()).init(); - verify(versionJudgement, never()).reset(); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - assertFalse(upgradeJudgement.isUseGrpcFeatures()); - assertFalse(upgradeJudgement.isUseJraftFeatures()); - } - - @Test - public void testUpgradeOneFor14XNode() throws Exception { - Collection members = mockMember("1.4.0", "2.0.0", "2.0.0"); - upgradeJudgement.onEvent(MembersChangeEvent.builder().triggers(members).build()); - verify(raftPeerSet, never()).init(); - verify(raftCore, never()).init(); - verify(versionJudgement, never()).reset(); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - assertFalse(upgradeJudgement.isUseGrpcFeatures()); - assertTrue(upgradeJudgement.isUseJraftFeatures()); - } - - @Test - public void testUpgradeTwoNode() throws Exception { - Collection members = mockMember("", "2.0.0", "2.0.0"); - upgradeJudgement.onEvent(MembersChangeEvent.builder().triggers(members).build()); - verify(raftPeerSet, never()).init(); - verify(raftCore, never()).init(); - verify(versionJudgement, never()).reset(); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - assertFalse(upgradeJudgement.isUseGrpcFeatures()); - assertFalse(upgradeJudgement.isUseJraftFeatures()); - } - - @Test - public void testUpgradeCheckSucc() throws Exception { - Collection members = mockMember("2.0.0-snapshot", "2.0.0", "2.0.0"); - Iterator iterator = members.iterator(); - when(doubleWriteDelayTaskEngine.isEmpty()).thenReturn(true); - iterator.next(); - while (iterator.hasNext()) { - iterator.next().setExtendVal(MemberMetaDataConstants.READY_TO_UPGRADE, true); - } - upgradeJudgement.onEvent(MembersChangeEvent.builder().triggers(members).build()); - verify(raftPeerSet, never()).init(); - verify(raftCore, never()).init(); - verify(versionJudgement, never()).reset(); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - assertTrue(upgradeJudgement.isUseGrpcFeatures()); - assertTrue(upgradeJudgement.isUseJraftFeatures()); - } - - @Test - public void testUpgradeCheckSelfFail() throws Exception { - Collection members = mockMember("2.0.0", "2.0.0", "2.0.0"); - Iterator iterator = members.iterator(); - when(doubleWriteDelayTaskEngine.isEmpty()).thenReturn(false); - iterator.next(); - while (iterator.hasNext()) { - iterator.next().setExtendVal(MemberMetaDataConstants.READY_TO_UPGRADE, true); - } - upgradeJudgement.onEvent(MembersChangeEvent.builder().triggers(members).build()); - verify(raftPeerSet, never()).init(); - verify(raftCore, never()).init(); - verify(versionJudgement, never()).reset(); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - assertFalse(upgradeJudgement.isUseGrpcFeatures()); - assertFalse(upgradeJudgement.isUseJraftFeatures()); - } - - @Test - public void testAlreadyUpgradedAndCheckSelfFail() throws Exception { - Collection members = mockMember("2.0.0", "2.0.0", "2.0.0"); - Iterator iterator = members.iterator(); - iterator.next(); - while (iterator.hasNext()) { - iterator.next().setExtendVal(MemberMetaDataConstants.READY_TO_UPGRADE, true); - } - when(upgradeStates.isUpgraded()).thenReturn(true); - upgradeJudgement.shutdown(); - upgradeJudgement = new UpgradeJudgement(raftPeerSet, raftCore, versionJudgement, memberManager, serviceManager, - upgradeStates, doubleWriteDelayTaskEngine); - upgradeJudgement.onEvent(MembersChangeEvent.builder().triggers(members).build()); - verify(raftPeerSet, never()).init(); - verify(raftCore, never()).init(); - verify(versionJudgement, never()).reset(); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - assertTrue(upgradeJudgement.isUseGrpcFeatures()); - assertTrue(upgradeJudgement.isUseJraftFeatures()); - } - - @Test - public void testUpgradeCheckOthersFail() throws Exception { - Collection members = mockMember("2.0.0", "2.0.0", "2.0.0"); - when(doubleWriteDelayTaskEngine.isEmpty()).thenReturn(true); - members.iterator().next().setExtendVal(MemberMetaDataConstants.READY_TO_UPGRADE, true); - upgradeJudgement.onEvent(MembersChangeEvent.builder().triggers(members).build()); - verify(raftPeerSet, never()).init(); - verify(raftCore, never()).init(); - verify(versionJudgement, never()).reset(); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - assertFalse(upgradeJudgement.isUseGrpcFeatures()); - assertFalse(upgradeJudgement.isUseJraftFeatures()); - } - - @Test - public void testAlreadyUpgradedAndCheckOthersFail() throws Exception { - Collection members = mockMember("2.0.0", "2.0.0", "2.0.0"); - members.iterator().next().setExtendVal(MemberMetaDataConstants.READY_TO_UPGRADE, true); - upgradeJudgement.shutdown(); - when(upgradeStates.isUpgraded()).thenReturn(true); - upgradeJudgement = new UpgradeJudgement(raftPeerSet, raftCore, versionJudgement, memberManager, serviceManager, - upgradeStates, doubleWriteDelayTaskEngine); - upgradeJudgement.onEvent(MembersChangeEvent.builder().triggers(members).build()); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - verify(raftPeerSet, never()).init(); - verify(raftCore, never()).init(); - verify(versionJudgement, never()).reset(); - assertTrue(upgradeJudgement.isUseGrpcFeatures()); - assertTrue(upgradeJudgement.isUseJraftFeatures()); - } - - @Test - public void testDowngradeOneFor14XNode() throws Exception { - upgradeJudgement.setUseGrpcFeatures(true); - upgradeJudgement.setUseJraftFeatures(true); - Collection members = mockMember("1.4.0", "2.0.0", "2.0.0"); - upgradeJudgement.onEvent(MembersChangeEvent.builder().triggers(members).build()); - verify(raftPeerSet, never()).init(); - verify(raftCore, never()).init(); - verify(versionJudgement, never()).reset(); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - assertFalse(upgradeJudgement.isUseGrpcFeatures()); - assertTrue(upgradeJudgement.isUseJraftFeatures()); - } - - @Test - public void testAlreadyUpgradedAndDowngradeOneFor14XNode() throws Exception { - when(upgradeStates.isUpgraded()).thenReturn(true); - upgradeJudgement.shutdown(); - upgradeJudgement = new UpgradeJudgement(raftPeerSet, raftCore, versionJudgement, memberManager, serviceManager, - upgradeStates, doubleWriteDelayTaskEngine); - upgradeJudgement.onEvent(MembersChangeEvent.builder().triggers(mockMember("1.4.0", "2.0.0", "2.0.0")).build()); - verify(raftPeerSet, never()).init(); - verify(raftCore, never()).init(); - verify(versionJudgement, never()).reset(); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - assertFalse(upgradeJudgement.isUseGrpcFeatures()); - assertTrue(upgradeJudgement.isUseJraftFeatures()); - } - - @Test - public void testDowngradeTwoNode() throws Exception { - upgradeJudgement.setUseGrpcFeatures(true); - upgradeJudgement.setUseJraftFeatures(true); - Collection members = mockMember("", "", "2.0.0"); - upgradeJudgement.onEvent(MembersChangeEvent.builder().triggers(members).build()); - verify(raftPeerSet, atMostOnce()).init(); - verify(raftCore, atMostOnce()).init(); - verify(versionJudgement, atMostOnce()).reset(); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - assertFalse(upgradeJudgement.isUseGrpcFeatures()); - assertFalse(upgradeJudgement.isUseJraftFeatures()); - } - - @Test - public void testAlreadyUpgradedAndDowngradeTwoNode() throws Exception { - when(upgradeStates.isUpgraded()).thenReturn(true); - upgradeJudgement.shutdown(); - upgradeJudgement = new UpgradeJudgement(raftPeerSet, raftCore, versionJudgement, memberManager, serviceManager, - upgradeStates, doubleWriteDelayTaskEngine); - upgradeJudgement.onEvent(MembersChangeEvent.builder().triggers(mockMember("", "", "2.0.0")).build()); - verify(raftPeerSet, atMostOnce()).init(); - verify(raftCore, atMostOnce()).init(); - verify(versionJudgement, atMostOnce()).reset(); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - assertFalse(upgradeJudgement.isUseGrpcFeatures()); - assertFalse(upgradeJudgement.isUseJraftFeatures()); - } - - @Test - public void testDowngradeOneNode() throws Exception { - upgradeJudgement.setUseGrpcFeatures(true); - upgradeJudgement.setUseJraftFeatures(true); - Collection members = mockMember("1.3.2", "2.0.0", "2.0.0"); - upgradeJudgement.onEvent(MembersChangeEvent.builder().triggers(members).build()); - verify(raftPeerSet, atMostOnce()).init(); - verify(raftCore, atMostOnce()).init(); - verify(versionJudgement, atMostOnce()).reset(); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - assertFalse(upgradeJudgement.isUseGrpcFeatures()); - assertFalse(upgradeJudgement.isUseJraftFeatures()); - } - - @Test - public void testAlreadyUpgradedAndDowngradeOneNode() throws Exception { - when(upgradeStates.isUpgraded()).thenReturn(true); - upgradeJudgement.shutdown(); - upgradeJudgement = new UpgradeJudgement(raftPeerSet, raftCore, versionJudgement, memberManager, serviceManager, - upgradeStates, doubleWriteDelayTaskEngine); - upgradeJudgement.onEvent(MembersChangeEvent.builder().triggers(mockMember("1.3.2", "2.0.0", "2.0.0")).build()); - verify(raftPeerSet, atMostOnce()).init(); - verify(raftCore, atMostOnce()).init(); - verify(versionJudgement, atMostOnce()).reset(); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - assertFalse(upgradeJudgement.isUseGrpcFeatures()); - assertFalse(upgradeJudgement.isUseJraftFeatures()); - } - - @Test - public void testUpgradedBySpecifiedSelfUpgradeChecker() throws InterruptedException { - upgradeJudgement.shutdown(); - MockEnvironment mockEnvironment = new MockEnvironment(); - mockEnvironment.setProperty("upgrading.checker.type", "mock"); - mockEnvironment.setProperty(Constants.SUPPORT_UPGRADE_FROM_1X, "true"); - EnvUtil.setEnvironment(mockEnvironment); - mockMember("1.3.2", "2.0.0", "2.0.0"); - upgradeJudgement = new UpgradeJudgement(raftPeerSet, raftCore, versionJudgement, memberManager, serviceManager, - upgradeStates, doubleWriteDelayTaskEngine); - TimeUnit.MILLISECONDS.sleep(sleepForCheck); - assertTrue((Boolean) memberManager.getSelf().getExtendVal(MemberMetaDataConstants.READY_TO_UPGRADE)); - } - - private Collection mockMember(String... versions) { - Collection result = new HashSet<>(); - for (int i = 0; i < versions.length; i++) { - Member member = new Member(); - member.setPort(i); - if (StringUtils.isNotBlank(versions[i])) { - member.setExtendVal(MemberMetaDataConstants.VERSION, versions[i]); - } - result.add(member); - } - when(memberManager.getSelf()).thenReturn(result.iterator().next()); - when(memberManager.allMembers()).thenReturn(result); - return result; - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteEventListenerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteEventListenerTest.java deleted file mode 100644 index a18f1c2e0..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/upgrade/doublewrite/delay/DoubleWriteEventListenerTest.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 1999-2021 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.nacos.naming.core.v2.upgrade.doublewrite.delay; - -import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import com.alibaba.nacos.sys.env.Constants; -import com.alibaba.nacos.sys.env.EnvUtil; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.mock.env.MockEnvironment; - -/** - * {@link DoubleWriteEventListener} unit tests. - * - * @author chenglu - * @date 2021-09-01 23:34 - */ -@RunWith(MockitoJUnitRunner.class) -public class DoubleWriteEventListenerTest { - - private DoubleWriteEventListener doubleWriteEventListener; - - @Mock - private UpgradeJudgement upgradeJudgement; - - @Mock - private DoubleWriteDelayTaskEngine doubleWriteDelayTaskEngine; - - @Before - public void setUp() { - MockEnvironment environment = new MockEnvironment(); - environment.setProperty(Constants.SUPPORT_UPGRADE_FROM_1X, "true"); - EnvUtil.setEnvironment(environment); - EnvUtil.setIsStandalone(false); - doubleWriteEventListener = new DoubleWriteEventListener(upgradeJudgement, doubleWriteDelayTaskEngine); - } - - @After - public void tearDown() { - EnvUtil.setIsStandalone(true); - } - - @Test - public void testOnEvent() { - Mockito.when(upgradeJudgement.isUseGrpcFeatures()).thenReturn(true); - - Service service = Service.newService("A", "B", "C"); - ServiceEvent.ServiceChangedEvent serviceChangedEvent = new ServiceEvent.ServiceChangedEvent(service); - doubleWriteEventListener.onEvent(serviceChangedEvent); - - Mockito.verify(doubleWriteDelayTaskEngine).addTask(Mockito.any(), Mockito.any()); - } - - @Test - public void testDoubleWriteMetadataToV1() { - Mockito.when(upgradeJudgement.isUseGrpcFeatures()).thenReturn(true); - - Service service = Service.newService("A", "B", "C"); - doubleWriteEventListener.doubleWriteMetadataToV1(service, false); - - Mockito.verify(doubleWriteDelayTaskEngine).addTask(Mockito.any(), Mockito.any()); - } - - @Test - public void testDoubleWriteToV2() { - Mockito.when(upgradeJudgement.isUseGrpcFeatures()).thenReturn(false); - Mockito.when(upgradeJudgement.isAll20XVersion()).thenReturn(false); - - com.alibaba.nacos.naming.core.Service service = new com.alibaba.nacos.naming.core.Service(); - doubleWriteEventListener.doubleWriteToV2(service, false); - - Mockito.verify(doubleWriteDelayTaskEngine).addTask(Mockito.any(), Mockito.any()); - } - - @Test - public void testDoubleWriteMetadataToV2() { - Mockito.when(upgradeJudgement.isUseGrpcFeatures()).thenReturn(false); - Mockito.when(upgradeJudgement.isAll20XVersion()).thenReturn(false); - - com.alibaba.nacos.naming.core.Service service = new com.alibaba.nacos.naming.core.Service(); - doubleWriteEventListener.doubleWriteMetadataToV2(service, false, false); - - Mockito.verify(doubleWriteDelayTaskEngine).addTask(Mockito.any(), Mockito.any()); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/healthcheck/ClientBeatCheckTaskTest.java b/naming/src/test/java/com/alibaba/nacos/naming/healthcheck/ClientBeatCheckTaskTest.java deleted file mode 100644 index 4e06397af..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/healthcheck/ClientBeatCheckTaskTest.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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.nacos.naming.healthcheck; - -import com.alibaba.nacos.api.naming.PreservedMetadataKeys; -import com.alibaba.nacos.naming.core.DistroMapper; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.core.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import com.alibaba.nacos.naming.misc.GlobalConfig; -import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.push.UdpPushService; -import com.alibaba.nacos.sys.utils.ApplicationUtils; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.Spy; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.test.util.ReflectionTestUtils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class ClientBeatCheckTaskTest { - - @InjectMocks - @Spy - private ClientBeatCheckTask clientBeatCheckTask; - - @Mock - private DistroMapper distroMapperSpy; - - @Mock - private Service serviceSpy; - - @Mock - private GlobalConfig globalConfig; - - @Mock - private UdpPushService pushService; - - @Mock - private SwitchDomain switchDomain; - - @Mock - private UpgradeJudgement upgradeJudgement; - - @Mock - private ConfigurableApplicationContext context; - - @Before - public void init() { - ReflectionTestUtils.setField(clientBeatCheckTask, "service", serviceSpy); - Mockito.doReturn(distroMapperSpy).when(clientBeatCheckTask).getDistroMapper(); - Mockito.doReturn(globalConfig).when(clientBeatCheckTask).getGlobalConfig(); - Mockito.doReturn(pushService).when(clientBeatCheckTask).getPushService(); - Mockito.doReturn(switchDomain).when(clientBeatCheckTask).getSwitchDomain(); - ApplicationUtils.injectContext(context); - when(context.getBean(UpgradeJudgement.class)).thenReturn(upgradeJudgement); - } - - @Test - public void testHeartBeatNotTimeout() { - Instance instance = new Instance(); - instance.setLastBeat(System.currentTimeMillis()); - instance.setMarked(false); - instance.setHealthy(true); - Map metadata = new HashMap<>(); - metadata.put(PreservedMetadataKeys.HEART_BEAT_TIMEOUT, "1000000000"); - instance.setMetadata(metadata); - - Mockito.doReturn("test").when(serviceSpy).getName(); - Mockito.doReturn(true).when(distroMapperSpy).responsible(Mockito.anyString()); - clientBeatCheckTask.run(); - Assert.assertTrue(instance.isHealthy()); - } - - @Test - public void testHeartBeatTimeout() { - Instance instance = new Instance(); - instance.setLastBeat(System.currentTimeMillis() - 1000); - instance.setMarked(false); - instance.setHealthy(true); - Map metadata = new HashMap<>(); - metadata.put(PreservedMetadataKeys.HEART_BEAT_TIMEOUT, "10"); - instance.setMetadata(metadata); - List instances = new ArrayList<>(); - instances.add(instance); - Mockito.doReturn("test").when(serviceSpy).getName(); - Mockito.doReturn(true).when(distroMapperSpy).responsible(Mockito.anyString()); - Mockito.doReturn(true).when(switchDomain).isHealthCheckEnabled(); - - when(serviceSpy.allIPs(true)).thenReturn(instances); - - clientBeatCheckTask.run(); - Assert.assertFalse(instance.isHealthy()); - } - - @Test - public void testIpDeleteTimeOut() { - Instance instance = new Instance(); - instance.setLastBeat(System.currentTimeMillis()); - instance.setMarked(true); - instance.setHealthy(true); - Map metadata = new HashMap<>(); - metadata.put(PreservedMetadataKeys.IP_DELETE_TIMEOUT, "10"); - instance.setMetadata(metadata); - Mockito.doReturn(true).when(distroMapperSpy).responsible(null); - - clientBeatCheckTask.run(); - } - - @Test - public void testIpDeleteNotTimeOut() { - Instance instance = new Instance(); - instance.setLastBeat(System.currentTimeMillis()); - instance.setMarked(true); - instance.setHealthy(true); - Map metadata = new HashMap<>(); - metadata.put(PreservedMetadataKeys.IP_DELETE_TIMEOUT, "10000"); - instance.setMetadata(metadata); - - Mockito.doReturn(true).when(distroMapperSpy).responsible(null); - - clientBeatCheckTask.run(); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/healthcheck/extend/HealthCheckProcessorExtendV1Test.java b/naming/src/test/java/com/alibaba/nacos/naming/healthcheck/extend/HealthCheckProcessorExtendV1Test.java deleted file mode 100644 index e24a95cff..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/healthcheck/extend/HealthCheckProcessorExtendV1Test.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.nacos.naming.healthcheck.extend; - -import com.alibaba.nacos.naming.healthcheck.HealthCheckProcessor; -import com.alibaba.nacos.naming.healthcheck.MysqlHealthCheckProcessor; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.beans.factory.config.SingletonBeanRegistry; -import org.springframework.test.util.ReflectionTestUtils; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import static org.mockito.Mockito.verify; - -@RunWith(MockitoJUnitRunner.class) -public class HealthCheckProcessorExtendV1Test { - - @Mock - private SingletonBeanRegistry registry; - - private HealthCheckProcessorExtendV1 healthCheckProcessorExtendV1; - - private HealthCheckProcessor mysqlProcessor; - - @Before - public void setUp() { - healthCheckProcessorExtendV1 = new HealthCheckProcessorExtendV1(); - Collection processors = new ArrayList<>(); - mysqlProcessor = new MysqlHealthCheckProcessor(); - processors.add(mysqlProcessor); - - ReflectionTestUtils.setField(healthCheckProcessorExtendV1, "processors", processors); - ReflectionTestUtils.setField(healthCheckProcessorExtendV1, "registry", registry); - } - - @Test - public void addProcessor() { - Set origin = new HashSet<>(); - origin.add("HTTP"); - healthCheckProcessorExtendV1.addProcessor(origin); - - verify(registry).registerSingleton(healthCheckProcessorExtendV1 - .lowerFirstChar(mysqlProcessor.getClass().getSimpleName()), mysqlProcessor); - } -} \ No newline at end of file diff --git a/naming/src/test/java/com/alibaba/nacos/naming/healthcheck/interceptor/HealthCheckTaskInterceptWrapperTest.java b/naming/src/test/java/com/alibaba/nacos/naming/healthcheck/interceptor/HealthCheckTaskInterceptWrapperTest.java index b00391a18..e3e7bdc41 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/healthcheck/interceptor/HealthCheckTaskInterceptWrapperTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/healthcheck/interceptor/HealthCheckTaskInterceptWrapperTest.java @@ -24,7 +24,6 @@ import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; import com.alibaba.nacos.naming.core.v2.pojo.HealthCheckInstancePublishInfo; import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; import com.alibaba.nacos.naming.healthcheck.heartbeat.ClientBeatCheckTaskV2; import com.alibaba.nacos.naming.misc.GlobalConfig; import com.alibaba.nacos.naming.misc.SwitchDomain; @@ -77,9 +76,6 @@ public class HealthCheckTaskInterceptWrapperTest { @Mock private ConfigurableApplicationContext applicationContext; - @Mock - private UpgradeJudgement upgradeJudgement; - private IpPortBasedClient client; @Before @@ -88,12 +84,10 @@ public class HealthCheckTaskInterceptWrapperTest { when(applicationContext.getBean(GlobalConfig.class)).thenReturn(globalConfig); when(applicationContext.getBean(SwitchDomain.class)).thenReturn(switchDomain); when(applicationContext.getBean(DistroMapper.class)).thenReturn(distroMapper); - when(applicationContext.getBean(UpgradeJudgement.class)).thenReturn(upgradeJudgement); ApplicationUtils.injectContext(applicationContext); client = new IpPortBasedClient(CLIENT_ID, true); when(switchDomain.isHealthCheckEnabled()).thenReturn(true); when(distroMapper.responsible(client.getResponsibleId())).thenReturn(true); - when(upgradeJudgement.isUseGrpcFeatures()).thenReturn(true); ClientBeatCheckTaskV2 beatCheckTask = new ClientBeatCheckTaskV2(client); taskWrapper = new HealthCheckTaskInterceptWrapper(beatCheckTask); } @@ -169,7 +163,8 @@ public class HealthCheckTaskInterceptWrapperTest { Service service = Service.newService(NAMESPACE, GROUP_NAME, SERVICE_NAME); InstanceMetadata metadata = new InstanceMetadata(); metadata.getExtendData().put(PreservedMetadataKeys.HEART_BEAT_TIMEOUT, 1000L); - when(namingMetadataManager.getInstanceMetadata(service, instance.getMetadataId())).thenReturn(Optional.of(metadata)); + when(namingMetadataManager.getInstanceMetadata(service, instance.getMetadataId())) + .thenReturn(Optional.of(metadata)); when(globalConfig.isExpireInstance()).thenReturn(true); TimeUnit.SECONDS.sleep(1); taskWrapper.run(); diff --git a/naming/src/test/java/com/alibaba/nacos/naming/pojo/instance/HttpRequestInstanceBuilderTest.java b/naming/src/test/java/com/alibaba/nacos/naming/pojo/instance/HttpRequestInstanceBuilderTest.java index 4d45a6da1..7b2bb84b2 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/pojo/instance/HttpRequestInstanceBuilderTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/pojo/instance/HttpRequestInstanceBuilderTest.java @@ -29,8 +29,6 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import javax.servlet.http.HttpServletRequest; -import java.util.HashMap; -import java.util.Map; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertFalse; @@ -87,14 +85,6 @@ public class HttpRequestInstanceBuilderTest { @Test public void testBuildFull() throws NacosException { - Map mockMap = new HashMap<>(); - mockMap.put("weight", new String[] {""}); - mockMap.put("healthy", new String[] {""}); - mockMap.put("enabled", new String[] {""}); - mockMap.put("ephemeral", new String[] {""}); - mockMap.put("metadata", new String[] {""}); - mockMap.put(CommonParams.CLUSTER_NAME, new String[] {""}); - when(request.getParameterMap()).thenReturn(mockMap); when(request.getParameter("weight")).thenReturn("2"); when(request.getParameter("healthy")).thenReturn("false"); when(request.getParameter("enabled")).thenReturn("false"); @@ -119,9 +109,6 @@ public class HttpRequestInstanceBuilderTest { @Test(expected = NacosException.class) public void testBuildWithIllegalWeight() throws NacosException { - Map mockMap = new HashMap<>(); - mockMap.put("weight", new String[] {""}); - when(request.getParameterMap()).thenReturn(mockMap); when(request.getParameter("weight")).thenReturn("10001"); Instance actual = builder.setRequest(request).build(); } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/push/v1/ClientInfoTest.java b/naming/src/test/java/com/alibaba/nacos/naming/push/ClientInfoTest.java similarity index 97% rename from naming/src/test/java/com/alibaba/nacos/naming/push/v1/ClientInfoTest.java rename to naming/src/test/java/com/alibaba/nacos/naming/push/ClientInfoTest.java index e453dfab5..1f19d9b95 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/push/v1/ClientInfoTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/push/ClientInfoTest.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2020 Alibaba Group Holding Ltd. + * Copyright 1999-2021 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.alibaba.nacos.naming.push.v1; +package com.alibaba.nacos.naming.push; import com.alibaba.nacos.naming.misc.UtilsAndCommons; import org.junit.Test; diff --git a/naming/src/test/java/com/alibaba/nacos/naming/push/v1/NamingSubscriberServiceV1ImplTest.java b/naming/src/test/java/com/alibaba/nacos/naming/push/v1/NamingSubscriberServiceV1ImplTest.java deleted file mode 100644 index 6fd2fd5c1..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/push/v1/NamingSubscriberServiceV1ImplTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.push.v1; - -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.pojo.Subscriber; -import com.alibaba.nacos.sys.env.EnvUtil; -import org.junit.BeforeClass; -import org.junit.Test; -import org.springframework.mock.env.MockEnvironment; - -import java.net.InetSocketAddress; -import java.util.Collection; - -import static org.junit.Assert.assertEquals; - -public class NamingSubscriberServiceV1ImplTest { - - private static NamingSubscriberServiceV1Impl namingSubscriberService; - - @BeforeClass - public static void setUp() throws Exception { - EnvUtil.setEnvironment(new MockEnvironment()); - namingSubscriberService = new NamingSubscriberServiceV1Impl(); - InetSocketAddress socketAddress = new InetSocketAddress(8848); - PushClient pushClient = new PushClient("1", "G1@@S1", "", "", socketAddress, null, "", ""); - namingSubscriberService.addClient(pushClient); - namingSubscriberService.addClient("1", "DEFAULT_GROUP@@S2", "", "", socketAddress, null, "", ""); - } - - @Test - public void testGetSubscribersWithStringParam() { - Collection actual = namingSubscriberService.getSubscribers("1", "G1@@S1"); - assertEquals(1, actual.size()); - assertSubscriber("1", "G1@@S1", actual.iterator().next()); - } - - @Test - public void testGetSubscribersWithServiceParam() { - Service service = Service.newService("1", "G1", "S1"); - Collection actual = namingSubscriberService.getSubscribers(service); - assertEquals(1, actual.size()); - assertSubscriber("1", "G1@@S1", actual.iterator().next()); - } - - @Test - public void testGetFuzzySubscribersWithStringParam() { - Collection actual = namingSubscriberService.getFuzzySubscribers("1", "D@@S"); - assertEquals(1, actual.size()); - assertSubscriber("1", "DEFAULT_GROUP@@S2", actual.iterator().next()); - actual = namingSubscriberService.getFuzzySubscribers("1", "G@@S"); - assertEquals(2, actual.size()); - assertSubscriber("1", "DEFAULT_GROUP@@S2", actual.iterator().next()); - actual = namingSubscriberService.getFuzzySubscribers("1", "X@@S"); - assertEquals(0, actual.size()); - } - - @Test - public void testGetFuzzySubscribersWithServiceParam() { - Service service = Service.newService("1", "G", "S"); - Collection actual = namingSubscriberService.getFuzzySubscribers(service); - assertEquals(2, actual.size()); - assertSubscriber("1", "DEFAULT_GROUP@@S2", actual.iterator().next()); - } - - private void assertSubscriber(String namespaceId, String serviceName, Subscriber actual) { - assertEquals(namespaceId, actual.getNamespaceId()); - assertEquals(serviceName, actual.getServiceName()); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/push/v1/ServiceChangeEventTest.java b/naming/src/test/java/com/alibaba/nacos/naming/push/v1/ServiceChangeEventTest.java deleted file mode 100644 index 8e0d78ce9..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/push/v1/ServiceChangeEventTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 1999-2020 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.nacos.naming.push.v1; - -import com.alibaba.nacos.naming.core.Service; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class ServiceChangeEventTest { - - @Mock - private Service service; - - @Mock - private Object object; - - @Test - public void testGetService() { - ServiceChangeEvent serviceChangeEvent = new ServiceChangeEvent(object, service); - Service service = serviceChangeEvent.getService(); - - Assert.assertNotNull(service); - Assert.assertEquals(service, service); - } -} \ No newline at end of file diff --git a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java index eaa809ef0..cb5a7dfb7 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java @@ -21,7 +21,6 @@ import com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate; import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.v2.task.PushDelayTask; @@ -64,9 +63,6 @@ public class NamingSubscriberServiceV2ImplTest { @Mock private Client client; - @Mock - private UpgradeJudgement upgradeJudgement; - @Mock private SwitchDomain switchDomain; @@ -74,8 +70,8 @@ public class NamingSubscriberServiceV2ImplTest { @Before public void setUp() throws Exception { - subscriberService = new NamingSubscriberServiceV2Impl(clientManager, indexesManager, null, null, null, - upgradeJudgement, switchDomain); + subscriberService = new NamingSubscriberServiceV2Impl(clientManager, indexesManager, null, null, null, + switchDomain); ReflectionTestUtils.setField(subscriberService, "delayTaskEngine", delayTaskEngine); when(indexesManager.getAllClientsSubscribeService(service)).thenReturn(Collections.singletonList(testClientId)); when(indexesManager.getAllClientsSubscribeService(service1)) @@ -89,7 +85,6 @@ public class NamingSubscriberServiceV2ImplTest { new Subscriber("1.1.1.1:1111", "Test", "unknown", "1.1.1.1", "N", service.getGroupedServiceName(), 0)); when(client.getSubscriber(service1)).thenReturn( new Subscriber("1.1.1.1:1111", "Test", "unknown", "1.1.1.1", "N", service1.getGroupedServiceName(), 0)); - when(upgradeJudgement.isUseGrpcFeatures()).thenReturn(true); } @Test diff --git a/naming/src/test/java/com/alibaba/nacos/naming/raft/RaftStoreTest.java b/naming/src/test/java/com/alibaba/nacos/naming/raft/RaftStoreTest.java deleted file mode 100644 index ef5757850..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/raft/RaftStoreTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.nacos.naming.raft; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.naming.BaseTest; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.consistency.persistent.ClusterVersionJudgement; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftCore; -import com.alibaba.nacos.naming.consistency.persistent.raft.RaftStore; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.core.Instances; -import com.alibaba.nacos.sys.env.Constants; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; - -public class RaftStoreTest extends BaseTest { - - public RaftCore raftCore; - - public RaftStore raftStore; - - @Mock - private ClusterVersionJudgement versionJudgement; - - @Before - public void setUp() { - super.before(); - environment.setProperty(Constants.SUPPORT_UPGRADE_FROM_1X, "true"); - raftStore = new RaftStore(); - raftCore = new RaftCore(peerSet, switchDomain, null, null, raftStore, versionJudgement, null); - } - - @After - public void tearDown() throws NacosException { - raftCore.shutdown(); - raftStore.shutdown(); - } - - @Test - public void wrietDatum() throws Exception { - Datum datum = new Datum<>(); - String key = KeyBuilder.buildInstanceListKey(TEST_NAMESPACE, TEST_SERVICE_NAME, false); - datum.key = key; - datum.timestamp.getAndIncrement(); - datum.value = new Instances(); - Instance instance = new Instance("1.1.1.1", 1, TEST_CLUSTER_NAME); - datum.value.getInstanceList().add(instance); - instance = new Instance("2.2.2.2", 2, TEST_CLUSTER_NAME); - datum.value.getInstanceList().add(instance); - - raftStore.write(datum); - raftCore.init(); - Datum result = raftCore.getDatum(key); - - Assert.assertEquals(key, result.key); - Assert.assertEquals(1, result.timestamp.intValue()); - Assert.assertEquals(datum.value.toString(), result.value.toString()); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/remote/rpc/filter/GrpcRequestFilterTest.java b/naming/src/test/java/com/alibaba/nacos/naming/remote/rpc/filter/GrpcRequestFilterTest.java deleted file mode 100644 index 8f1c05819..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/remote/rpc/filter/GrpcRequestFilterTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 1999-2021 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.nacos.naming.remote.rpc.filter; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.remote.request.InstanceRequest; -import com.alibaba.nacos.api.remote.request.RequestMeta; -import com.alibaba.nacos.api.remote.response.Response; -import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement; -import com.alibaba.nacos.naming.remote.rpc.handler.InstanceRequestHandler; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; - -/** - * {@link GrpcRequestFilter} unit test. - * - * @author chenglu - * @date 2021-09-17 16:25 - */ -@RunWith(MockitoJUnitRunner.class) -public class GrpcRequestFilterTest { - - @InjectMocks - private GrpcRequestFilter grpcRequestFilter; - - @Mock - private UpgradeJudgement upgradeJudgement; - - @Test - public void testFilter() throws NacosException { - Mockito.when(upgradeJudgement.isUseGrpcFeatures()).thenReturn(true).thenReturn(false); - Response response = grpcRequestFilter.filter(new InstanceRequest(), new RequestMeta(), InstanceRequestHandler.class); - Assert.assertNull(response); - - try { - grpcRequestFilter.filter(new InstanceRequest(), new RequestMeta(), InstanceRequestHandler.class); - } catch (Exception e) { - Assert.assertTrue(e instanceof NacosException); - } - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/remote/udp/UdpConnectorTest.java b/naming/src/test/java/com/alibaba/nacos/naming/remote/udp/UdpConnectorTest.java index d420edc15..0c92846f4 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/remote/udp/UdpConnectorTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/remote/udp/UdpConnectorTest.java @@ -76,7 +76,7 @@ public class UdpConnectorTest { DatagramSocket oldSocket = (DatagramSocket) ReflectionTestUtils.getField(udpConnector, "udpSocket"); ReflectionTestUtils.setField(udpConnector, "udpSocket", udpSocket); doAnswer(invocationOnMock -> { - TimeUnit.MINUTES.sleep(1); + TimeUnit.SECONDS.sleep(3); return null; }).when(udpSocket).receive(any(DatagramPacket.class)); oldSocket.close(); diff --git a/naming/src/test/java/com/alibaba/nacos/naming/selector/v1/LabelSelectorTest.java b/naming/src/test/java/com/alibaba/nacos/naming/selector/v1/LabelSelectorTest.java deleted file mode 100644 index eb8f09410..000000000 --- a/naming/src/test/java/com/alibaba/nacos/naming/selector/v1/LabelSelectorTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 1999-2021 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.nacos.naming.selector.v1; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.common.utils.StringUtils; -import org.junit.Assert; -import org.junit.Test; - -import java.util.List; -import java.util.Set; - -public class LabelSelectorTest { - - private String expression = "CONSUMER.label.A=PROVIDER.label.A &CONSUMER.label.B=PROVIDER.label.B"; - - @Test - public void parseExpression() throws NacosException { - expression = StringUtils.deleteWhitespace(expression); - List terms = LabelSelector.ExpressionInterpreter.getTerms(expression); - Assert.assertEquals(7, terms.size()); - Set parseLables = LabelSelector.parseExpression(expression); - Assert.assertEquals(2, parseLables.size()); - String[] labs = parseLables.toArray(new String[] {}); - Assert.assertEquals("A", labs[0]); - Assert.assertEquals("B", labs[1]); - } -} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/utils/DistroUtilsTest.java b/naming/src/test/java/com/alibaba/nacos/naming/utils/DistroUtilsTest.java new file mode 100644 index 000000000..2930b3db4 --- /dev/null +++ b/naming/src/test/java/com/alibaba/nacos/naming/utils/DistroUtilsTest.java @@ -0,0 +1,173 @@ +/* + * Copyright 1999-2022 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.nacos.naming.utils; + +import com.alibaba.nacos.naming.constants.Constants; +import com.alibaba.nacos.naming.core.v2.client.Client; +import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; +import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; + +import static com.alibaba.nacos.api.common.Constants.DEFAULT_CLUSTER_NAME; +import static org.junit.Assert.assertEquals; + +/** + * Tests for {@link DistroUtils}. + * + * @author Pixy Yuan + * on 2021/10/9 + */ +public class DistroUtilsTest { + + private static final String NAMESPACE = "testNamespace-"; + + private static final String GROUP = "testGroup-"; + + private static final String SERVICE = "testName-"; + + private static final int N = 100000; + + private IpPortBasedClient client0; + + private IpPortBasedClient client1; + + @Before + public void setUp() throws Exception { + client0 = buildClient("127.0.0.1", 8848, false, true, DEFAULT_CLUSTER_NAME, + null); + HashMap metadata = new HashMap<>(); + metadata.put(Constants.PUBLISH_INSTANCE_WEIGHT, 2L); + metadata.put(Constants.PUBLISH_INSTANCE_ENABLE, false); + metadata.put("Custom.metadataId1", "abc"); + metadata.put("Custom.metadataId2", 123); + metadata.put("Custom.metadataId3", null); + client1 = buildClient("127.0.0.2", 8848, true, true, "cluster1", + metadata, 20); + } + + private IpPortBasedClient buildClient(String ip, int port, boolean ephemeral, boolean healthy, String cluster, + HashMap extendDatum) { + return buildClient(ip, port, ephemeral, healthy, cluster, extendDatum, 1); + } + + private IpPortBasedClient buildClient(String ip, int port, boolean ephemeral, boolean healthy, String cluster, + HashMap extendDatum, int serviceCount) { + InstancePublishInfo instance = new InstancePublishInfo(ip, port); + instance.setCluster(cluster); + instance.setHealthy(healthy); + IpPortBasedClient client = new IpPortBasedClient(String.format("%s:%s#%s", ip, port, ephemeral), ephemeral); + if (extendDatum != null) { + instance.setExtendDatum(extendDatum); + } + for (int i = 1; i < serviceCount + 1; i++) { + client.putServiceInstance(Service.newService(DistroUtilsTest.NAMESPACE + i, + DistroUtilsTest.GROUP + i, DistroUtilsTest.SERVICE + i, ephemeral), + instance); + } + return client; + } + + @Test + public void testHash0() { + assertEquals(-1320954445, DistroUtils.hash(client0)); + } + + @Test + public void testRevision0() { + assertEquals(-1713189600L, DistroUtils.stringHash(client0)); + } + + @Test + public void testChecksum0() { + for (int i = 0; i < 3; i++) { + assertEquals("2a3f62f84a4b6f2a979434276d546ac1", DistroUtils.checksum(client0)); + } + } + + @Test + public void testBuildUniqueString0() { + assertEquals("127.0.0.1:8848#false|testNamespace-1##testGroup-1@@testName-1##false_127.0.0.1:8848_1.0_true_true_DEFAULT_,", + DistroUtils.buildUniqueString(client0)); + } + + @Test + public void testBuildUniqueString1() { + HashMap metadata = new HashMap<>(); + metadata.put(Constants.PUBLISH_INSTANCE_WEIGHT, 2L); + metadata.put(Constants.PUBLISH_INSTANCE_ENABLE, false); + metadata.put("Custom.metadataId1", "abc"); + metadata.put("Custom.metadataId2", 123); + metadata.put("Custom.metadataId3", null); + Client client = buildClient("128.0.0.1", 8848, false, false, DEFAULT_CLUSTER_NAME, + metadata); + assertEquals("128.0.0.1:8848#false|" + + "testNamespace-1##testGroup-1@@testName-1##false_128.0.0.1:8848_2.0_false_false_DEFAULT_" + + "Custom.metadataId1:abc,Custom.metadataId2:123,Custom.metadataId3:null," + + "publishInstanceEnable:false,publishInstanceWeight:2,,", + DistroUtils.buildUniqueString(client)); + assertEquals(2128732271L, DistroUtils.stringHash(client)); + assertEquals("ac9bf94dc4bd6a35e5ff9734868eafea", DistroUtils.checksum(client)); + } + + @Test + public void testBuildUniqueString2() { + HashMap metadata = new HashMap<>(); + metadata.put(Constants.PUBLISH_INSTANCE_WEIGHT, 2L); + metadata.put(Constants.PUBLISH_INSTANCE_ENABLE, true); + metadata.put("Custom.metadataId1", "abc"); + Client client = buildClient("128.0.0.2", 7001, true, false, "cluster1", + metadata); + assertEquals("128.0.0.2:7001#true|" + + "testNamespace-1##testGroup-1@@testName-1##true_128.0.0.2:7001_2.0_false_true_cluster1_" + + "Custom.metadataId1:abc,publishInstanceEnable:true,publishInstanceWeight:2,,", + DistroUtils.buildUniqueString(client)); + assertEquals(775352583L, DistroUtils.stringHash(client)); + assertEquals("82d8e086a880f088320349b895b22948", DistroUtils.checksum(client)); + } + + @Test + public void performanceTestOfChecksum() { + long start = System.nanoTime(); + for (int i = 0; i < N; i++) { + DistroUtils.checksum(client1); + } + System.out.printf("Distro Verify Checksum Performance: %.2f ivk/ns\n", ((double) System.nanoTime() - start) / N); + } + + @Test + public void performanceTestOfStringHash() { + long start = System.nanoTime(); + for (int i = 0; i < N; i++) { + DistroUtils.stringHash(client1); + } + System.out.printf("Distro Verify Revision Performance: %.2f ivk/ns\n", ((double) System.nanoTime() - start) / N); + } + + @Test + public void performanceTestOfHash() { + long start = System.nanoTime(); + for (int i = 0; i < N; i++) { + DistroUtils.hash(client1); + } + System.out.printf("Distro Verify Hash Performance: %.2f ivk/ns\n", ((double) System.nanoTime() - start) / N); + } + +} \ No newline at end of file diff --git a/naming/src/test/java/com/alibaba/nacos/naming/utils/ServiceUtilTest.java b/naming/src/test/java/com/alibaba/nacos/naming/utils/ServiceUtilTest.java index da86d7520..c6883013d 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/utils/ServiceUtilTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/utils/ServiceUtilTest.java @@ -17,60 +17,12 @@ package com.alibaba.nacos.naming.utils; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; -import com.alibaba.nacos.naming.core.Service; import org.junit.Test; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; public class ServiceUtilTest { - @Test - public void testPageServiceName() { - Map input = new HashMap<>(); - input.put("Group@@Service", new Service()); - input.put("Service", new Service()); - List actual = ServiceUtil.pageServiceName(1, 20, input); - assertEquals(2, actual.size()); - assertEquals("Service", actual.get(0)); - assertEquals("Service", actual.get(1)); - } - - @Test - public void testSelectServiceWithGroupName() { - Service service1 = new Service(); - service1.setEnabled(true); - service1.setName("serviceName"); - service1.setGroupName("groupName"); - service1.setAppName("appName"); - service1.setNamespaceId("namespaceId"); - service1.setResetWeight(true); - Map services = new HashMap<>(); - services.put("service1", service1); - Map resultMap = ServiceUtil.selectServiceWithGroupName(services, "groupName"); - assertNotNull(resultMap); - } - - @Test - public void testSelectServiceBySelector() { - Service service1 = new Service(); - service1.setEnabled(true); - service1.setName("serviceName"); - service1.setGroupName("groupName"); - service1.setAppName("appName"); - service1.setNamespaceId("namespaceId"); - service1.setResetWeight(true); - Map serviceMap = new HashMap<>(); - serviceMap.put("service1", service1); - Map resultMap = ServiceUtil.selectServiceBySelector(serviceMap, - "{\"type\":\"label\",\"expression\":\"msg\"}"); - assertNotNull(resultMap); - } - @Test public void testSelectInstances() { ServiceInfo serviceInfo = new ServiceInfo(); diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthConfig.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthConfig.java index 89440b03e..d17a81dd7 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthConfig.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthConfig.java @@ -29,11 +29,9 @@ import org.springframework.context.annotation.Configuration; import org.springframework.ldap.core.LdapTemplate; import org.springframework.ldap.core.support.LdapContextSource; -import java.util.HashMap; -import java.util.Map; - /** * ldap auth config. + * * @author onewe */ @Configuration @@ -55,29 +53,25 @@ public class LdapAuthConfig { @Value(("${" + AuthConstants.NACOS_CORE_AUTH_LDAP_PASSWORD + ":password}")) private String password; + @Value(("${" + AuthConstants.NACOS_CORE_AUTH_LDAP_FILTER_PREFIX + ":uid}")) + private String filterPrefix; + @Bean @Conditional(ConditionOnLdapAuth.class) - public LdapTemplate ldapTemplate() { - LdapContextSource contextSource = new LdapContextSource(); - final Map config = new HashMap<>(16); - contextSource.setUrl(ldapUrl); - contextSource.setBase(ldapBaseDc); - contextSource.setUserDn(userDn); - contextSource.setPassword(password); - config.put("java.naming.ldap.attributes.binary", "objectGUID"); - config.put("com.sun.jndi.ldap.connect.timeout", ldapTimeOut); - contextSource.setPooled(true); - contextSource.setBaseEnvironmentProperties(config); - contextSource.afterPropertiesSet(); - return new LdapTemplate(contextSource); - + public LdapTemplate ldapTemplate(LdapContextSource ldapContextSource) { + return new LdapTemplate(ldapContextSource); + } + + @Bean + public LdapContextSource ldapContextSource() { + return new NacosLdapContextSource(ldapUrl, ldapBaseDc, userDn, password, ldapTimeOut); } @Bean @Conditional(ConditionOnLdapAuth.class) public LdapAuthenticationProvider ldapAuthenticationProvider(LdapTemplate ldapTemplate, NacosUserDetailsServiceImpl userDetailsService, NacosRoleServiceImpl nacosRoleService) { - return new LdapAuthenticationProvider(ldapTemplate, userDetailsService, nacosRoleService); + return new LdapAuthenticationProvider(ldapTemplate, userDetailsService, nacosRoleService, filterPrefix); } } diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthenticationProvider.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthenticationProvider.java index a212e598d..34f5c5b33 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthenticationProvider.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthenticationProvider.java @@ -52,11 +52,14 @@ public class LdapAuthenticationProvider implements AuthenticationProvider { private final LdapTemplate ldapTemplate; + private final String filterPrefix; + public LdapAuthenticationProvider(LdapTemplate ldapTemplate, NacosUserDetailsServiceImpl userDetailsService, - NacosRoleServiceImpl nacosRoleService) { + NacosRoleServiceImpl nacosRoleService, String filterPrefix) { this.ldapTemplate = ldapTemplate; this.nacosRoleService = nacosRoleService; this.userDetailsService = userDetailsService; + this.filterPrefix = filterPrefix; } @Override @@ -110,7 +113,7 @@ public class LdapAuthenticationProvider implements AuthenticationProvider { } private boolean ldapLogin(String username, String password) throws AuthenticationException { - return ldapTemplate.authenticate("", "(uid=" + username + ")", password); + return ldapTemplate.authenticate("", "(" + filterPrefix + "=" + username + ")", password); } @Override diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/NacosLdapContextSource.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/NacosLdapContextSource.java new file mode 100644 index 000000000..762200ecc --- /dev/null +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/NacosLdapContextSource.java @@ -0,0 +1,130 @@ +/* + * 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.nacos.plugin.auth.impl; + +import com.alibaba.nacos.common.tls.TlsHelper; +import com.alibaba.nacos.core.utils.Loggers; +import org.springframework.ldap.core.support.LdapContextSource; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; +import javax.naming.Context; +import javax.net.ssl.SSLSocketFactory; + +/** + * NacosLdapContextSource. + * + * @author karsonto + */ +public class NacosLdapContextSource extends LdapContextSource { + + private final Map config = new HashMap<>(16); + + private boolean useTsl = false; + + private static final String LDAPS = "ldaps"; + + public NacosLdapContextSource(String ldapUrl, String ldapBaseDc, String userDn, String password, + String ldapTimeOut) { + this.setUrl(ldapUrl); + this.setBase(ldapBaseDc); + this.setUserDn(userDn); + this.setPassword(password); + if (ldapUrl.toLowerCase().startsWith(LDAPS)) { + useTsl = true; + } + this.setPooled(true); + init(ldapTimeOut); + } + + /** + * init LdapContextSource config. + * + * @param ldapTimeOut ldap connection time out. + */ + public void init(String ldapTimeOut) { + if (useTsl) { + System.setProperty("com.sun.jndi.ldap.object.disableEndpointIdentification", "true"); + config.put("java.naming.security.protocol", "ssl"); + config.put("java.naming.ldap.factory.socket", LdapSslSocketFactory.class.getName()); + config.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + } + config.put("java.naming.ldap.attributes.binary", "objectGUID"); + config.put("com.sun.jndi.ldap.connect.timeout", ldapTimeOut); + this.setBaseEnvironmentProperties(config); + this.afterPropertiesSet(); + } + + @SuppressWarnings("checkstyle:EmptyLineSeparator") + public static class LdapSslSocketFactory extends SSLSocketFactory { + + private SSLSocketFactory socketFactory; + + public LdapSslSocketFactory() { + try { + this.socketFactory = TlsHelper.buildSslContext(true).getSocketFactory(); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + Loggers.AUTH.error("Failed to create SSLContext", e); + } + } + + @Override + public String[] getDefaultCipherSuites() { + return socketFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return socketFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket(Socket socket, String s, int i, boolean b) throws IOException { + return socketFactory.createSocket(socket, s, i, b); + } + + @Override + public Socket createSocket(String s, int i) throws IOException, UnknownHostException { + return socketFactory.createSocket(s, i); + } + + @Override + public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) + throws IOException, UnknownHostException { + return socketFactory.createSocket(s, i, inetAddress, i1); + } + + @Override + public Socket createSocket(InetAddress inetAddress, int i) throws IOException { + return socketFactory.createSocket(inetAddress, i); + } + + @Override + public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) + throws IOException { + return socketFactory.createSocket(inetAddress, i, inetAddress1, i1); + } + } + + +} diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/constant/AuthConstants.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/constant/AuthConstants.java index cb2239a62..33ca6d0ce 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/constant/AuthConstants.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/constant/AuthConstants.java @@ -60,4 +60,6 @@ public class AuthConstants { public static final String NACOS_CORE_AUTH_LDAP_USERDN = "nacos.core.auth.ldap.userDn"; public static final String NACOS_CORE_AUTH_LDAP_PASSWORD = "nacos.core.auth.ldap.password"; + + public static final String NACOS_CORE_AUTH_LDAP_FILTER_PREFIX = "nacos.core.auth.ldap.filter.prefix"; } diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/controller/PermissionController.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/controller/PermissionController.java index d8c6983bd..5e3ad990f 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/controller/PermissionController.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/controller/PermissionController.java @@ -19,8 +19,10 @@ package com.alibaba.nacos.plugin.auth.impl.controller; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.model.RestResultUtils; import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.config.server.model.Page; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.impl.constant.AuthConstants; +import com.alibaba.nacos.plugin.auth.impl.persistence.PermissionInfo; import com.alibaba.nacos.plugin.auth.impl.roles.NacosRoleServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; @@ -51,12 +53,27 @@ public class PermissionController { * @param pageSize page size * @return permission of a role */ - @GetMapping + @GetMapping(params = "search=accurate") @Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX + "permissions", action = ActionTypes.READ) public Object getPermissions(@RequestParam int pageNo, @RequestParam int pageSize, @RequestParam(name = "role", defaultValue = StringUtils.EMPTY) String role) { return nacosRoleService.getPermissionsFromDatabase(role, pageNo, pageSize); } + + /** + * Fuzzy Query permissions of a role. + * + * @param role the role + * @param pageNo page index + * @param pageSize page size + * @return permission of a role + */ + @GetMapping(params = "search=blur") + @Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX + "permissions", action = ActionTypes.READ) + public Page fuzzySearchPermission(@RequestParam int pageNo, @RequestParam int pageSize, + @RequestParam(name = "role", defaultValue = StringUtils.EMPTY) String role) { + return nacosRoleService.findPermissionsLike4Page(role, pageNo, pageSize); + } /** * Add a permission to a role. diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/controller/RoleController.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/controller/RoleController.java index 5c6ef6555..efcf5dfcd 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/controller/RoleController.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/controller/RoleController.java @@ -19,8 +19,10 @@ package com.alibaba.nacos.plugin.auth.impl.controller; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.model.RestResultUtils; import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.config.server.model.Page; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.impl.constant.AuthConstants; +import com.alibaba.nacos.plugin.auth.impl.persistence.RoleInfo; import com.alibaba.nacos.plugin.auth.impl.roles.NacosRoleServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; @@ -51,13 +53,31 @@ public class RoleController { * @param pageNo number index of page * @param pageSize page size * @param username optional, username of user + * @param role optional role * @return role list */ - @GetMapping + @GetMapping(params = "search=accurate") @Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX + "roles", action = ActionTypes.READ) public Object getRoles(@RequestParam int pageNo, @RequestParam int pageSize, - @RequestParam(name = "username", defaultValue = "") String username) { - return roleService.getRolesFromDatabase(username, pageNo, pageSize); + @RequestParam(name = "username", defaultValue = "") String username, + @RequestParam(name = "role", defaultValue = "") String role) { + return roleService.getRolesFromDatabase(username, role, pageNo, pageSize); + } + + /** + * Fuzzy query role information. + * @param pageNo number index of page + * @param pageSize page size + * @param username username of user + * @param role role + * @return role list + */ + @GetMapping(params = "search=blur") + @Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX + "roles", action = ActionTypes.READ) + public Page fuzzySearchRole(@RequestParam int pageNo, @RequestParam int pageSize, + @RequestParam(name = "username", defaultValue = "") String username, + @RequestParam(name = "role", defaultValue = "") String role) { + return roleService.findRolesLike4Page(username, role, pageNo, pageSize); } /** diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/controller/UserController.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/controller/UserController.java index ff6d53805..ef8b06a50 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/controller/UserController.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/controller/UserController.java @@ -22,6 +22,7 @@ import com.alibaba.nacos.auth.config.AuthConfigs; import com.alibaba.nacos.common.model.RestResult; import com.alibaba.nacos.common.model.RestResultUtils; import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.config.server.model.Page; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.exception.AccessException; import com.alibaba.nacos.plugin.auth.impl.JwtTokenManager; @@ -179,7 +180,7 @@ public class UserController { // same user return user.getUserName().equals(username); } - + /** * Get paged users. * @@ -188,10 +189,18 @@ public class UserController { * @return A collection of users, empty set if no user is found * @since 1.2.0 */ - @GetMapping + @GetMapping(params = "search=accurate") @Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX + "users", action = ActionTypes.READ) - public Object getUsers(@RequestParam int pageNo, @RequestParam int pageSize) { - return userDetailsService.getUsersFromDatabase(pageNo, pageSize); + public Page getUsers(@RequestParam int pageNo, @RequestParam int pageSize, + @RequestParam(name = "username", required = false, defaultValue = "") String username) { + return userDetailsService.getUsersFromDatabase(pageNo, pageSize, username); + } + + @GetMapping(params = "search=blur") + @Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX + "users", action = ActionTypes.READ) + public Page fuzzySearchUser(@RequestParam int pageNo, @RequestParam int pageSize, + @RequestParam(name = "username", required = false, defaultValue = "") String username) { + return userDetailsService.findUsersLike4Page(username, pageNo, pageSize); } /** diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedPermissionPersistServiceImpl.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedPermissionPersistServiceImpl.java index 9bb027d44..54152ad81 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedPermissionPersistServiceImpl.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedPermissionPersistServiceImpl.java @@ -47,6 +47,10 @@ public class EmbeddedPermissionPersistServiceImpl implements PermissionPersistSe @Autowired private EmbeddedStoragePersistServiceImpl persistService; + + private static final String PATTERN_STR = "*"; + + private static final String SQL_DERBY_ESCAPE_BACK_SLASH_FOR_LIKE = " ESCAPE '\\' "; @Override public Page getPermissions(String role, int pageNo, int pageSize) { @@ -103,5 +107,48 @@ public class EmbeddedPermissionPersistServiceImpl implements PermissionPersistSe EmbeddedStorageContextUtils.addSqlContext(sql, role, resource, action); databaseOperate.blockUpdate(); } - + + @Override + public Page findPermissionsLike4Page(String role, int pageNo, int pageSize) { + PaginationHelper helper = persistService.createPaginationHelper(); + + String sqlCountRows = "SELECT count(*) FROM permissions "; + + String sqlFetchRows = "SELECT role,resource,action FROM permissions "; + + StringBuilder where = new StringBuilder(" WHERE 1=1"); + List params = new ArrayList<>(); + if (StringUtils.isNotBlank(role)) { + where.append(" AND role LIKE ?"); + where.append(SQL_DERBY_ESCAPE_BACK_SLASH_FOR_LIKE); + params.add(generateLikeArgument(role)); + } + + Page pageInfo = helper + .fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, + PERMISSION_ROW_MAPPER); + + if (pageInfo == null) { + pageInfo = new Page<>(); + pageInfo.setTotalCount(0); + pageInfo.setPageItems(new ArrayList<>()); + } + return pageInfo; + } + + @Override + public String generateLikeArgument(String s) { + String underscore = "_"; + if (s.contains(underscore)) { + s = s.replaceAll(underscore, "\\\\_"); + } + String fuzzySearchSign = "\\*"; + String sqlLikePercentSign = "%"; + if (s.contains(PATTERN_STR)) { + return s.replaceAll(fuzzySearchSign, sqlLikePercentSign); + } else { + return s; + } + } + } diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedRolePersistServiceImpl.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedRolePersistServiceImpl.java index ede4c5316..71cf7d96e 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedRolePersistServiceImpl.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedRolePersistServiceImpl.java @@ -16,6 +16,10 @@ package com.alibaba.nacos.plugin.auth.impl.persistence; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.configuration.ConditionOnEmbeddedStorage; import com.alibaba.nacos.config.server.model.Page; @@ -23,14 +27,11 @@ import com.alibaba.nacos.config.server.service.repository.PaginationHelper; import com.alibaba.nacos.config.server.service.repository.embedded.DatabaseOperate; import com.alibaba.nacos.config.server.service.repository.embedded.EmbeddedStoragePersistServiceImpl; import com.alibaba.nacos.config.server.service.sql.EmbeddedStorageContextUtils; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Component; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import static com.alibaba.nacos.plugin.auth.impl.persistence.AuthRowMapperManager.ROLE_INFO_ROW_MAPPER; /** @@ -47,6 +48,10 @@ public class EmbeddedRolePersistServiceImpl implements RolePersistService { @Autowired private EmbeddedStoragePersistServiceImpl persistService; + + private static final String PATTERN_STR = "*"; + + private static final String SQL_DERBY_ESCAPE_BACK_SLASH_FOR_LIKE = " ESCAPE '\\' "; @Override public Page getRoles(int pageNo, int pageSize) { @@ -72,20 +77,23 @@ public class EmbeddedRolePersistServiceImpl implements RolePersistService { } @Override - public Page getRolesByUserName(String username, int pageNo, int pageSize) { + public Page getRolesByUserNameAndRoleName(String username, String role, int pageNo, int pageSize) { PaginationHelper helper = persistService.createPaginationHelper(); - String sqlCountRows = "SELECT count(*) FROM roles WHERE "; + String sqlCountRows = "SELECT count(*) FROM roles "; - String sqlFetchRows = "SELECT role,username FROM roles WHERE "; - - String where = " username= ? "; + String sqlFetchRows = "SELECT role,username FROM roles "; + + StringBuilder where = new StringBuilder(" WHERE 1 = 1 "); List params = new ArrayList<>(); if (StringUtils.isNotBlank(username)) { - params = Collections.singletonList(username); - } else { - where = " 1=1 "; + where.append(" AND username = ? "); + params.add(username); + } + if (StringUtils.isNotBlank(role)) { + where.append(" AND role = ? "); + params.add(role); } return helper.fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, @@ -147,8 +155,47 @@ public class EmbeddedRolePersistServiceImpl implements RolePersistService { @Override public List findRolesLikeRoleName(String role) { - String sql = "SELECT role FROM roles WHERE role LIKE ? "; + String sql = "SELECT role FROM roles WHERE role LIKE ? " + SQL_DERBY_ESCAPE_BACK_SLASH_FOR_LIKE; return databaseOperate.queryMany(sql, new String[] {"%" + role + "%"}, String.class); } - + + @Override + public String generateLikeArgument(String s) { + String underscore = "_"; + if (s.contains(underscore)) { + s = s.replaceAll(underscore, "\\\\_"); + } + String fuzzySearchSign = "\\*"; + String sqlLikePercentSign = "%"; + if (s.contains(PATTERN_STR)) { + return s.replaceAll(fuzzySearchSign, sqlLikePercentSign); + } else { + return s; + } + } + + @Override + public Page findRolesLike4Page(String username, String role, int pageNo, int pageSize) { + StringBuilder where = new StringBuilder(" WHERE 1 = 1 "); + List params = new ArrayList<>(); + + if (StringUtils.isNotBlank(username)) { + where.append(" AND username LIKE ? "); + params.add(generateLikeArgument(username)); + } + if (StringUtils.isNotBlank(role)) { + where.append(" AND role LIKE ? "); + params.add(generateLikeArgument(role)); + } + if (CollectionUtils.isNotEmpty(params)) { + where.append(SQL_DERBY_ESCAPE_BACK_SLASH_FOR_LIKE); + } + String sqlCountRows = "SELECT count(*) FROM roles"; + String sqlFetchRows = "SELECT role, username FROM roles"; + + PaginationHelper helper = persistService.createPaginationHelper(); + return helper.fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, + ROLE_INFO_ROW_MAPPER); + } + } diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedUserPersistServiceImpl.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedUserPersistServiceImpl.java index ec4b1ca77..08764b8ce 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedUserPersistServiceImpl.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedUserPersistServiceImpl.java @@ -16,12 +16,14 @@ package com.alibaba.nacos.plugin.auth.impl.persistence; +import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.configuration.ConditionOnEmbeddedStorage; import com.alibaba.nacos.config.server.model.Page; import com.alibaba.nacos.config.server.service.repository.PaginationHelper; import com.alibaba.nacos.config.server.service.repository.embedded.DatabaseOperate; import com.alibaba.nacos.config.server.service.repository.embedded.EmbeddedStoragePersistServiceImpl; import com.alibaba.nacos.config.server.service.sql.EmbeddedStorageContextUtils; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Component; @@ -45,6 +47,10 @@ public class EmbeddedUserPersistServiceImpl implements UserPersistService { @Autowired private EmbeddedStoragePersistServiceImpl persistService; + + private static final String PATTERN_STR = "*"; + + private static final String SQL_DERBY_ESCAPE_BACK_SLASH_FOR_LIKE = " ESCAPE '\\' "; /** * Execute create user operation. @@ -104,17 +110,22 @@ public class EmbeddedUserPersistServiceImpl implements UserPersistService { } @Override - public Page getUsers(int pageNo, int pageSize) { + public Page getUsers(int pageNo, int pageSize, String username) { PaginationHelper helper = persistService.createPaginationHelper(); - String sqlCountRows = "SELECT count(*) FROM users WHERE "; + String sqlCountRows = "SELECT count(*) FROM users "; - String sqlFetchRows = "SELECT username,password FROM users WHERE "; - - String where = " 1=1 "; + String sqlFetchRows = "SELECT username,password FROM users "; + + StringBuilder where = new StringBuilder(" WHERE 1 = 1 "); + List params = new ArrayList<>(); + if (StringUtils.isNotBlank(username)) { + where.append(" AND username = ? "); + params.add(username); + } Page pageInfo = helper - .fetchPage(sqlCountRows + where, sqlFetchRows + where, new ArrayList().toArray(), pageNo, + .fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, USER_ROW_MAPPER); if (pageInfo == null) { pageInfo = new Page<>(); @@ -126,7 +137,40 @@ public class EmbeddedUserPersistServiceImpl implements UserPersistService { @Override public List findUserLikeUsername(String username) { - String sql = "SELECT username FROM users WHERE username LIKE ? "; + String sql = "SELECT username FROM users WHERE username LIKE ? " + SQL_DERBY_ESCAPE_BACK_SLASH_FOR_LIKE; return databaseOperate.queryMany(sql, new String[] {"%" + username + "%"}, String.class); } + + @Override + public Page findUsersLike4Page(String username, int pageNo, int pageSize) { + String sqlCountRows = "SELECT count(*) FROM users "; + String sqlFetchRows = "SELECT username,password FROM users "; + + StringBuilder where = new StringBuilder(" WHERE 1 = 1 "); + List params = new ArrayList<>(); + if (StringUtils.isNotBlank(username)) { + where.append(" AND username LIKE ? "); + where.append(SQL_DERBY_ESCAPE_BACK_SLASH_FOR_LIKE); + params.add(generateLikeArgument(username)); + } + + PaginationHelper helper = persistService.createPaginationHelper(); + return helper.fetchPage(sqlCountRows + where, sqlFetchRows + where, + params.toArray(), pageNo, pageSize, USER_ROW_MAPPER); + } + + @Override + public String generateLikeArgument(String s) { + String underscore = "_"; + if (s.contains(underscore)) { + s = s.replaceAll(underscore, "\\\\_"); + } + String fuzzySearchSign = "\\*"; + String sqlLikePercentSign = "%"; + if (s.contains(PATTERN_STR)) { + return s.replaceAll(fuzzySearchSign, sqlLikePercentSign); + } else { + return s; + } + } } diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalPermissionPersistServiceImpl.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalPermissionPersistServiceImpl.java index 95fc86621..2064722c5 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalPermissionPersistServiceImpl.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalPermissionPersistServiceImpl.java @@ -48,6 +48,8 @@ public class ExternalPermissionPersistServiceImpl implements PermissionPersistSe private ExternalStoragePersistServiceImpl persistService; private JdbcTemplate jt; + + private static final String PATTERN_STR = "*"; @PostConstruct protected void init() { @@ -126,5 +128,53 @@ public class ExternalPermissionPersistServiceImpl implements PermissionPersistSe throw e; } } - + + @Override + public Page findPermissionsLike4Page(String role, int pageNo, int pageSize) { + PaginationHelper helper = persistService.createPaginationHelper(); + + String sqlCountRows = "SELECT count(*) FROM permissions "; + String sqlFetchRows = "SELECT role,resource,action FROM permissions "; + + StringBuilder where = new StringBuilder(" WHERE 1=1"); + List params = new ArrayList<>(); + if (StringUtils.isNotBlank(role)) { + where.append(" AND role LIKE ?"); + params.add(generateLikeArgument(role)); + } + + try { + Page pageInfo = helper + .fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, + PERMISSION_ROW_MAPPER); + + if (pageInfo == null) { + pageInfo = new Page<>(); + pageInfo.setTotalCount(0); + pageInfo.setPageItems(new ArrayList<>()); + } + + return pageInfo; + + } catch (CannotGetJdbcConnectionException e) { + LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); + throw e; + } + } + + @Override + public String generateLikeArgument(String s) { + String underscore = "_"; + if (s.contains(underscore)) { + s = s.replaceAll(underscore, "\\\\_"); + } + String fuzzySearchSign = "\\*"; + String sqlLikePercentSign = "%"; + if (s.contains(PATTERN_STR)) { + return s.replaceAll(fuzzySearchSign, sqlLikePercentSign); + } else { + return s; + } + } + } diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalRolePersistServiceImpl.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalRolePersistServiceImpl.java index 8cf5e40b2..89069765f 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalRolePersistServiceImpl.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalRolePersistServiceImpl.java @@ -33,7 +33,6 @@ import javax.annotation.PostConstruct; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import static com.alibaba.nacos.plugin.auth.impl.persistence.AuthRowMapperManager.ROLE_INFO_ROW_MAPPER; @@ -51,6 +50,8 @@ public class ExternalRolePersistServiceImpl implements RolePersistService { private ExternalStoragePersistServiceImpl persistService; private JdbcTemplate jt; + + private static final String PATTERN_STR = "*"; @PostConstruct protected void init() { @@ -85,20 +86,23 @@ public class ExternalRolePersistServiceImpl implements RolePersistService { } @Override - public Page getRolesByUserName(String username, int pageNo, int pageSize) { + public Page getRolesByUserNameAndRoleName(String username, String role, int pageNo, int pageSize) { PaginationHelper helper = persistService.createPaginationHelper(); - String sqlCountRows = "SELECT count(*) FROM roles WHERE "; + String sqlCountRows = "SELECT count(*) FROM roles "; - String sqlFetchRows = "SELECT role,username FROM roles WHERE "; - - String where = " username= ? "; + String sqlFetchRows = "SELECT role,username FROM roles "; + + StringBuilder where = new StringBuilder(" WHERE 1 = 1 "); List params = new ArrayList<>(); if (StringUtils.isNotBlank(username)) { - params = Collections.singletonList(username); - } else { - where = " 1=1 "; + where.append(" AND username = ? "); + params.add(username); + } + if (StringUtils.isNotBlank(role)) { + where.append(" AND role = ? "); + params.add(role); } try { @@ -168,7 +172,48 @@ public class ExternalRolePersistServiceImpl implements RolePersistService { List users = this.jt.queryForList(sql, new String[] {role}, String.class); return users; } - + + @Override + public String generateLikeArgument(String s) { + String underscore = "_"; + if (s.contains(underscore)) { + s = s.replaceAll(underscore, "\\\\_"); + } + String fuzzySearchSign = "\\*"; + String sqlLikePercentSign = "%"; + if (s.contains(PATTERN_STR)) { + return s.replaceAll(fuzzySearchSign, sqlLikePercentSign); + } else { + return s; + } + } + + @Override + public Page findRolesLike4Page(String username, String role, int pageNo, int pageSize) { + String sqlCountRows = "SELECT count(*) FROM roles"; + String sqlFetchRows = "SELECT role, username FROM roles"; + StringBuilder where = new StringBuilder(" WHERE 1 = 1 "); + List params = new ArrayList<>(); + + if (StringUtils.isNotBlank(username)) { + where.append(" AND username LIKE ? "); + params.add(generateLikeArgument(username)); + } + if (StringUtils.isNotBlank(role)) { + where.append(" AND role LIKE ? "); + params.add(generateLikeArgument(role)); + } + + PaginationHelper helper = persistService.createPaginationHelper(); + try { + return helper.fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, + ROLE_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); + throw e; + } + } + private static final class RoleInfoRowMapper implements RowMapper { @Override diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalUserPersistServiceImpl.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalUserPersistServiceImpl.java index b633041bc..e9b8e4211 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalUserPersistServiceImpl.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalUserPersistServiceImpl.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.plugin.auth.impl.persistence; +import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.configuration.ConditionOnExternalStorage; import com.alibaba.nacos.config.server.model.Page; import com.alibaba.nacos.config.server.service.repository.PaginationHelper; @@ -47,6 +48,8 @@ public class ExternalUserPersistServiceImpl implements UserPersistService { private ExternalStoragePersistServiceImpl persistService; private JdbcTemplate jt; + + private static final String PATTERN_STR = "*"; @PostConstruct protected void init() { @@ -126,19 +129,24 @@ public class ExternalUserPersistServiceImpl implements UserPersistService { } @Override - public Page getUsers(int pageNo, int pageSize) { + public Page getUsers(int pageNo, int pageSize, String username) { PaginationHelper helper = persistService.createPaginationHelper(); - String sqlCountRows = "SELECT count(*) FROM users WHERE "; - - String sqlFetchRows = "SELECT username,password FROM users WHERE "; - - String where = " 1=1 "; + String sqlCountRows = "SELECT count(*) FROM users "; + String sqlFetchRows = "SELECT username,password FROM users "; + + StringBuilder where = new StringBuilder(" WHERE 1 = 1 "); + List params = new ArrayList<>(); + if (StringUtils.isNotBlank(username)) { + where.append(" AND username = ? "); + params.add(username); + } + try { Page pageInfo = helper - .fetchPage(sqlCountRows + where, sqlFetchRows + where, new ArrayList().toArray(), pageNo, + .fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, USER_ROW_MAPPER); if (pageInfo == null) { pageInfo = new Page<>(); @@ -158,4 +166,41 @@ public class ExternalUserPersistServiceImpl implements UserPersistService { List users = this.jt.queryForList(sql, new String[] {username}, String.class); return users; } + + @Override + public Page findUsersLike4Page(String username, int pageNo, int pageSize) { + String sqlCountRows = "SELECT count(*) FROM users "; + String sqlFetchRows = "SELECT username,password FROM users "; + + StringBuilder where = new StringBuilder(" WHERE 1 = 1 "); + List params = new ArrayList<>(); + if (StringUtils.isNotBlank(username)) { + where.append(" AND username LIKE ? "); + params.add(generateLikeArgument(username)); + } + + PaginationHelper helper = persistService.createPaginationHelper(); + try { + return helper.fetchPage(sqlCountRows + where, sqlFetchRows + where, + params.toArray(), pageNo, pageSize, USER_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); + throw e; + } + } + + @Override + public String generateLikeArgument(String s) { + String underscore = "_"; + if (s.contains(underscore)) { + s = s.replaceAll(underscore, "\\\\_"); + } + String fuzzySearchSign = "\\*"; + String sqlLikePercentSign = "%"; + if (s.contains(PATTERN_STR)) { + return s.replaceAll(fuzzySearchSign, sqlLikePercentSign); + } else { + return s; + } + } } diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/PermissionPersistService.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/PermissionPersistService.java index 30cc446cf..614ff8f7b 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/PermissionPersistService.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/PermissionPersistService.java @@ -54,4 +54,8 @@ public interface PermissionPersistService { * @param action action */ void deletePermission(String role, String resource, String action); + + Page findPermissionsLike4Page(String role, int pageNo, int pageSize); + + String generateLikeArgument(String s); } diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/RolePersistService.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/RolePersistService.java index 99315f5e0..ac754e93c 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/RolePersistService.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/RolePersistService.java @@ -16,10 +16,10 @@ package com.alibaba.nacos.plugin.auth.impl.persistence; -import com.alibaba.nacos.config.server.model.Page; - import java.util.List; +import com.alibaba.nacos.config.server.model.Page; + /** * Role CRUD service. * @@ -46,7 +46,7 @@ public interface RolePersistService { * @param pageSize pageSize * @return roles page info */ - Page getRolesByUserName(String username, int pageNo, int pageSize); + Page getRolesByUserNameAndRoleName(String username, String role, int pageNo, int pageSize); /** * assign role to user. @@ -78,4 +78,22 @@ public interface RolePersistService { * @return roles */ List findRolesLikeRoleName(String role); + + /** + * Generate fuzzy search Sql. + * + * @param s origin string + * @return fuzzy search Sql + */ + String generateLikeArgument(String s); + + /**. + * fuzzy query role information based on roleName and username + * + * @param username username of user + * @param pageNo page number + * @param pageSize page size + * @return {@link Page} with {@link RoleInfo} generation + */ + Page findRolesLike4Page(String username, String role, int pageNo, int pageSize); } diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/UserPersistService.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/UserPersistService.java index b9058d5d5..a85a4b9cd 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/UserPersistService.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/persistence/UserPersistService.java @@ -67,7 +67,7 @@ public interface UserPersistService { * @param pageSize pageSize * @return user page info */ - Page getUsers(int pageNo, int pageSize); + Page getUsers(int pageNo, int pageSize, String username); /** * fuzzy query user by username. @@ -76,4 +76,8 @@ public interface UserPersistService { * @return usernames */ List findUserLikeUsername(String username); + + Page findUsersLike4Page(String username, int pageNo, int pageSize); + + String generateLikeArgument(String s); } diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/roles/NacosRoleServiceImpl.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/roles/NacosRoleServiceImpl.java index 88f671cb3..9bacd362f 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/roles/NacosRoleServiceImpl.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/roles/NacosRoleServiceImpl.java @@ -77,7 +77,7 @@ public class NacosRoleServiceImpl { private void reload() { try { Page roleInfoPage = rolePersistService - .getRolesByUserName(StringUtils.EMPTY, DEFAULT_PAGE_NO, Integer.MAX_VALUE); + .getRolesByUserNameAndRoleName(StringUtils.EMPTY, StringUtils.EMPTY, DEFAULT_PAGE_NO, Integer.MAX_VALUE); if (roleInfoPage == null) { return; } @@ -160,7 +160,7 @@ public class NacosRoleServiceImpl { public List getRoles(String username) { List roleInfoList = roleInfoMap.get(username); if (!authConfigs.isCachingEnabled() || roleInfoList == null) { - Page roleInfoPage = getRolesFromDatabase(username, DEFAULT_PAGE_NO, Integer.MAX_VALUE); + Page roleInfoPage = getRolesFromDatabase(username, StringUtils.EMPTY, DEFAULT_PAGE_NO, Integer.MAX_VALUE); if (roleInfoPage != null) { roleInfoList = roleInfoPage.getPageItems(); if (!Collections.isEmpty(roleInfoList)) { @@ -171,8 +171,8 @@ public class NacosRoleServiceImpl { return roleInfoList; } - public Page getRolesFromDatabase(String userName, int pageNo, int pageSize) { - Page roles = rolePersistService.getRolesByUserName(userName, pageNo, pageSize); + public Page getRolesFromDatabase(String userName, String role, int pageNo, int pageSize) { + Page roles = rolePersistService.getRolesByUserNameAndRoleName(userName, role, pageNo, pageSize); if (roles == null) { return new Page<>(); } @@ -279,4 +279,12 @@ public class NacosRoleServiceImpl { } return result.toString(); } + + public Page findRolesLike4Page(String username, String role, int pageNo, int pageSize) { + return rolePersistService.findRolesLike4Page(username, role, pageNo, pageSize); + } + + public Page findPermissionsLike4Page(String role, int pageNo, int pageSize) { + return permissionPersistService.findPermissionsLike4Page(role, pageNo, pageSize); + } } diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/users/NacosUserDetailsServiceImpl.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/users/NacosUserDetailsServiceImpl.java index c005ab3e5..3d74ad3c3 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/users/NacosUserDetailsServiceImpl.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/users/NacosUserDetailsServiceImpl.java @@ -17,6 +17,7 @@ package com.alibaba.nacos.plugin.auth.impl.users; import com.alibaba.nacos.auth.config.AuthConfigs; +import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.impl.persistence.UserPersistService; import com.alibaba.nacos.config.server.model.Page; import com.alibaba.nacos.plugin.auth.impl.persistence.User; @@ -52,7 +53,7 @@ public class NacosUserDetailsServiceImpl implements UserDetailsService { @Scheduled(initialDelay = 5000, fixedDelay = 15000) private void reload() { try { - Page users = getUsersFromDatabase(1, Integer.MAX_VALUE); + Page users = getUsersFromDatabase(1, Integer.MAX_VALUE, StringUtils.EMPTY); if (users == null) { return; } @@ -85,8 +86,8 @@ public class NacosUserDetailsServiceImpl implements UserDetailsService { userPersistService.updateUserPassword(username, password); } - public Page getUsersFromDatabase(int pageNo, int pageSize) { - return userPersistService.getUsers(pageNo, pageSize); + public Page getUsersFromDatabase(int pageNo, int pageSize, String username) { + return userPersistService.getUsers(pageNo, pageSize, username); } public User getUser(String username) { @@ -115,4 +116,8 @@ public class NacosUserDetailsServiceImpl implements UserDetailsService { public void deleteUser(String username) { userPersistService.deleteUser(username); } + + public Page findUsersLike4Page(String username, int pageNo, int pageSize) { + return userPersistService.findUsersLike4Page(username, pageNo, pageSize); + } } diff --git a/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthenticationProviderTest.java b/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthenticationProviderTest.java new file mode 100644 index 000000000..87d39c6e1 --- /dev/null +++ b/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthenticationProviderTest.java @@ -0,0 +1,137 @@ +/* + * Copyright 1999-2021 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.nacos.plugin.auth.impl; + +import com.alibaba.nacos.plugin.auth.impl.constant.AuthConstants; +import com.alibaba.nacos.plugin.auth.impl.persistence.RoleInfo; +import com.alibaba.nacos.plugin.auth.impl.roles.NacosRoleServiceImpl; +import com.alibaba.nacos.plugin.auth.impl.users.NacosUserDetailsServiceImpl; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; +import org.springframework.ldap.core.LdapTemplate; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class LdapAuthenticationProviderTest { + + @Mock + private NacosUserDetailsServiceImpl userDetailsService; + + @Mock + private NacosRoleServiceImpl nacosRoleService; + + @Mock + private LdapTemplate ldapTemplate; + + private LdapAuthenticationProvider ldapAuthenticationProvider; + + private List roleInfos = new ArrayList<>(); + + private final String adminUserName = "nacos"; + + private final String normalUserName = "normal"; + + private final String filterPrefix = "uid"; + + Method isAdmin; + + Method ldapLogin; + + private String defaultPassWord = "nacos"; + + @Before + public void setUp() throws NoSuchMethodException { + RoleInfo adminRole = new RoleInfo(); + adminRole.setRole(AuthConstants.GLOBAL_ADMIN_ROLE); + adminRole.setUsername(adminUserName); + roleInfos.add(adminRole); + when(nacosRoleService.getRoles(adminUserName)).thenReturn(roleInfos); + when(nacosRoleService.getRoles(normalUserName)).thenReturn(new ArrayList<>()); + when(ldapTemplate.authenticate("", "(" + filterPrefix + "=" + adminUserName + ")", defaultPassWord)).thenAnswer( + new Answer() { + @Override + public Boolean answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + String b = (String) args[1]; + String c = (String) args[2]; + if (defaultPassWord.equals(c)) { + return true; + } + return false; + } + }); + this.ldapAuthenticationProvider = new LdapAuthenticationProvider(ldapTemplate, userDetailsService, + nacosRoleService, filterPrefix); + isAdmin = LdapAuthenticationProvider.class.getDeclaredMethod("isAdmin", String.class); + isAdmin.setAccessible(true); + ldapLogin = LdapAuthenticationProvider.class.getDeclaredMethod("ldapLogin", String.class, String.class); + ldapLogin.setAccessible(true); + } + + @Test + public void testIsAdmin() { + try { + Boolean result = (Boolean) isAdmin.invoke(ldapAuthenticationProvider, adminUserName); + Assert.assertTrue(result); + } catch (IllegalAccessException e) { + Assert.fail(); + } catch (InvocationTargetException e) { + Assert.fail(); + } + try { + Boolean result = (Boolean) isAdmin.invoke(ldapAuthenticationProvider, normalUserName); + Assert.assertTrue(!result); + } catch (IllegalAccessException e) { + Assert.fail(); + } catch (InvocationTargetException e) { + Assert.fail(); + } + + } + + @Test + public void testldapLogin() { + try { + Boolean result = (Boolean) ldapLogin.invoke(ldapAuthenticationProvider, adminUserName, defaultPassWord); + Assert.assertTrue(result); + } catch (IllegalAccessException e) { + Assert.fail(); + } catch (InvocationTargetException e) { + Assert.fail(); + } + try { + Boolean result = (Boolean) ldapLogin.invoke(ldapAuthenticationProvider, adminUserName, "123"); + Assert.assertTrue(!result); + } catch (IllegalAccessException e) { + Assert.fail(); + } catch (InvocationTargetException e) { + Assert.fail(); + } + } +} diff --git a/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedRolePersistServiceImplTest.java b/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedRolePersistServiceImplTest.java index ddf11d693..595765fc4 100644 --- a/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedRolePersistServiceImplTest.java +++ b/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedRolePersistServiceImplTest.java @@ -70,7 +70,8 @@ public class EmbeddedRolePersistServiceImplTest { @Test public void testGetRolesByUserName() { - Page page = embeddedRolePersistService.getRolesByUserName("userName", 1, 10); + Page page = embeddedRolePersistService.getRolesByUserNameAndRoleName( + "userName", "roleName", 1, 10); Assert.assertNull(page); } diff --git a/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedUserPersistServiceImplTest.java b/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedUserPersistServiceImplTest.java index 86688e59b..19ed0ad32 100644 --- a/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedUserPersistServiceImplTest.java +++ b/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedUserPersistServiceImplTest.java @@ -91,7 +91,7 @@ public class EmbeddedUserPersistServiceImplTest { @Test public void testGetUsers() { - Page users = embeddedUserPersistService.getUsers(1, 10); + Page users = embeddedUserPersistService.getUsers(1, 10, "nacos"); Assert.assertNotNull(users); } diff --git a/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalRolePersistServiceImplTest.java b/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalRolePersistServiceImplTest.java index e5d6e5a46..fc3d39f86 100644 --- a/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalRolePersistServiceImplTest.java +++ b/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalRolePersistServiceImplTest.java @@ -68,7 +68,8 @@ public class ExternalRolePersistServiceImplTest { @Test public void testGetRolesByUserName() { - Page userName = externalRolePersistService.getRolesByUserName("userName", 1, 10); + Page userName = externalRolePersistService.getRolesByUserNameAndRoleName( + "userName", "roleName", 1, 10); Assert.assertNull(userName); } diff --git a/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalUserPersistServiceImplTest.java b/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalUserPersistServiceImplTest.java index 2e1c3d223..969076053 100644 --- a/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalUserPersistServiceImplTest.java +++ b/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/persistence/ExternalUserPersistServiceImplTest.java @@ -92,7 +92,7 @@ public class ExternalUserPersistServiceImplTest { @Test public void testGetUsers() { - Page users = externalUserPersistService.getUsers(1, 10); + Page users = externalUserPersistService.getUsers(1, 10, "nacos"); Assert.assertNotNull(users); } diff --git a/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/roles/NacosRoleServiceImplTest.java b/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/roles/NacosRoleServiceImplTest.java index b7000176b..7f79bb7ca 100644 --- a/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/roles/NacosRoleServiceImplTest.java +++ b/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/roles/NacosRoleServiceImplTest.java @@ -118,7 +118,7 @@ public class NacosRoleServiceImplTest { @Test public void getRolesFromDatabase() { - Page roleInfoPage = nacosRoleService.getRolesFromDatabase("nacos", 1, Integer.MAX_VALUE); + Page roleInfoPage = nacosRoleService.getRolesFromDatabase("nacos", "ROLE_ADMIN", 1, Integer.MAX_VALUE); Assert.assertEquals(roleInfoPage.getTotalCount(), 0); } diff --git a/plugin/datasource/pom.xml b/plugin/datasource/pom.xml new file mode 100644 index 000000000..dab0e0cd2 --- /dev/null +++ b/plugin/datasource/pom.xml @@ -0,0 +1,39 @@ + + + + + nacos-plugin + com.alibaba.nacos + ${revision} + + 4.0.0 + + nacos-datasource-plugin + nacos-datasource-plugin ${project.version} + http://nacos.io + + + + com.alibaba.nacos + nacos-common + provided + + + + \ No newline at end of file diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/MapperManager.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/MapperManager.java new file mode 100644 index 000000000..870985a2d --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/MapperManager.java @@ -0,0 +1,94 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource; + +import com.alibaba.nacos.common.spi.NacosServiceLoader; +import com.alibaba.nacos.plugin.datasource.mapper.Mapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +/** + * DataSource Plugin Mapper Management. + * + * @author hyx + **/ + +public class MapperManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(MapperManager.class); + + public static final Map> MAPPER_SPI_MAP = new HashMap<>(); + + private static final MapperManager INSTANCE = new MapperManager(); + + private MapperManager() { + loadInitial(); + } + + /** + * Get the instance of MapperManager. + * @return The instance of MapperManager. + */ + public static MapperManager instance() { + return INSTANCE; + } + + /** + * The init method. + */ + public void loadInitial() { + Collection mappers = NacosServiceLoader.load(Mapper.class); + for (Mapper mapper : mappers) { + Map mapperMap = MAPPER_SPI_MAP.getOrDefault(mapper.getDataSource(), new HashMap<>(16)); + mapperMap.put(mapper.getTableName(), mapper); + MAPPER_SPI_MAP.put(mapper.getDataSource(), mapperMap); + LOGGER.info("[MapperManager] Load Mapper({}) datasource({}) tableName({}) successfully.", + mapper.getClass(), mapper.getDataSource(), mapper.getTableName()); + } + } + + /** + * To join mapper in MAPPER_SPI_MAP. + * @param mapper The mapper you want join. + */ + public static synchronized void join(Mapper mapper) { + if (Objects.isNull(mapper)) { + return; + } + Map mapperMap = MAPPER_SPI_MAP.getOrDefault(mapper.getDataSource(), new HashMap<>(16)); + mapperMap.put(mapper.getTableName(), mapper); + MAPPER_SPI_MAP.put(mapper.getDataSource(), mapperMap); + LOGGER.warn("[MapperManager] join successfully."); + } + + /** + * Get the mapper by table name. + * @param tableName table name. + * @param dataSource the datasource. + * @return mapper. + */ + public Optional findMapper(String dataSource, String tableName) { + LOGGER.info("[findMapper] dataSource: {}, tableName: {}", dataSource, tableName); + return Optional.ofNullable(MAPPER_SPI_MAP.get(dataSource).get(tableName)); + } +} \ No newline at end of file diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/Message.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/constants/DataSourceConstant.java similarity index 65% rename from naming/src/main/java/com/alibaba/nacos/naming/misc/Message.java rename to plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/constants/DataSourceConstant.java index 0efafd5d7..b197c16a2 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/Message.java +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/constants/DataSourceConstant.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2018 Alibaba Group Holding Ltd. + * Copyright 1999-2022 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. @@ -14,22 +14,16 @@ * limitations under the License. */ -package com.alibaba.nacos.naming.misc; +package com.alibaba.nacos.plugin.datasource.constants; /** - * message. + * The data source name. * - * @author nacos - */ -public class Message { + * @author hyx + **/ + +public class DataSourceConstant { + public static final String MYSQL = "mysql"; - private String data; - - public String getData() { - return data; - } - - public void setData(String data) { - this.data = data; - } + public static final String DERBY = "derby"; } diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/constants/TableConstant.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/constants/TableConstant.java new file mode 100644 index 000000000..ac9938ec6 --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/constants/TableConstant.java @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.constants; + +/** + * The table name constant. + * + * @author hyx + **/ + +public class TableConstant { + public static final String CONFIG_INFO = "config_info"; + + public static final String CONFIG_INFO_AGGR = "config_info_aggr"; + + public static final String CONFIG_INFO_BETA = "config_info_beta"; + + public static final String CONFIG_INFO_TAG = "config_info_tag"; + + public static final String CONFIG_TAGS_RELATION = "config_tags_relation"; + + public static final String GROUP_CAPACITY = "group_capacity"; + + public static final String HIS_CONFIG_INFO = "his_config_info"; + + public static final String TENANT_CAPACITY = "tenant_capacity"; + + public static final String TENANT_INFO = "tenant_info"; +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoAggrMapperByDerby.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoAggrMapperByDerby.java new file mode 100644 index 000000000..09c9bef8d --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoAggrMapperByDerby.java @@ -0,0 +1,96 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.impl.derby; + +import com.alibaba.nacos.plugin.datasource.constants.DataSourceConstant; +import com.alibaba.nacos.plugin.datasource.constants.TableConstant; +import com.alibaba.nacos.plugin.datasource.mapper.AbstractMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoAggrMapper; + +import java.util.List; + +/** + * The derby implementation of ConfigInfoAggrMapper. + * + * @author hyx + **/ + +public class ConfigInfoAggrMapperByDerby extends AbstractMapper implements ConfigInfoAggrMapper { + + @Override + public String batchRemoveAggr(List datumList) { + final StringBuilder datumString = new StringBuilder(); + for (String datum : datumList) { + datumString.append('\'').append(datum).append("',"); + } + datumString.deleteCharAt(datumString.length() - 1); + return "DELETE FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND datum_id IN (" + + datumString.toString() + ")"; + } + + @Override + public String aggrConfigInfoCount(int size, boolean isIn) { + StringBuilder sql = new StringBuilder( + " SELECT count(*) FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND datum_id"); + if (isIn) { + sql.append(" IN ("); + } else { + sql.append(" NOT IN ("); + } + for (int i = 0; i < size; i++) { + if (i > 0) { + sql.append(", "); + } + sql.append('?'); + } + sql.append(')'); + + return sql.toString(); + } + + @Override + public String aggrConfigInfoCount() { + return " SELECT count(*) FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ?"; + } + + @Override + public String findConfigInfoAggr() { + return "SELECT data_id,group_id,tenant_id,datum_id,app_name,content FROM config_info_aggr WHERE data_id = ? AND " + + "group_id = ? AND tenant_id = ? ORDER BY datum_id"; + } + + @Override + public String findConfigInfoAggrByPageFetchRows(int startRow, int pageSize) { + return "SELECT data_id,group_id,tenant_id,datum_id,app_name,content FROM config_info_aggr WHERE data_id=? AND " + + "group_id=? AND tenant_id=? ORDER BY datum_id OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; + } + + @Override + public String findAllAggrGroup() { + return "SELECT DISTINCT data_id, group_id, tenant_id FROM config_info_aggr"; + } + + @Override + public String getTableName() { + return TableConstant.CONFIG_INFO_AGGR; + } + + @Override + public String getDataSource() { + return DataSourceConstant.DERBY; + } +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoBetaMapperByDerby.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoBetaMapperByDerby.java new file mode 100644 index 000000000..1bf49235a --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoBetaMapperByDerby.java @@ -0,0 +1,59 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.impl.derby; + +import com.alibaba.nacos.plugin.datasource.constants.DataSourceConstant; +import com.alibaba.nacos.plugin.datasource.constants.TableConstant; +import com.alibaba.nacos.plugin.datasource.mapper.AbstractMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoBetaMapper; + +/** + * The derby implementation of ConfigInfoBetaMapper. + * + * @author hyx + **/ + +public class ConfigInfoBetaMapperByDerby extends AbstractMapper implements ConfigInfoBetaMapper { + + @Override + public String updateConfigInfo4BetaCas() { + return "UPDATE config_info_beta SET content = ?,md5 = ?,beta_ips = ?,src_ip = ?,src_user = ?,gmt_modified = ?,app_name = ? " + + "WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND (md5 = ? OR md5 IS NULL OR md5 = '')"; + } + + @Override + public String count() { + return "SELECT COUNT(*) FROM config_info_beta"; + } + + @Override + public String findAllConfigInfoBetaForDumpAllFetchRows(int startRow, int pageSize) { + return " SELECT t.id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified,beta_ips " + + " FROM ( SELECT id FROM config_info_beta ORDER BY id OFFSET ? ROWS FETCH NEXT ? ROWS ONLY )" + + " g, config_info_beta t WHERE g.id = t.id "; + } + + @Override + public String getTableName() { + return TableConstant.CONFIG_INFO_BETA; + } + + @Override + public String getDataSource() { + return DataSourceConstant.DERBY; + } +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoMapperByDerby.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoMapperByDerby.java new file mode 100644 index 000000000..eb18099d4 --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoMapperByDerby.java @@ -0,0 +1,592 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.impl.derby; + +import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.plugin.datasource.constants.DataSourceConstant; +import com.alibaba.nacos.plugin.datasource.constants.TableConstant; +import com.alibaba.nacos.plugin.datasource.mapper.AbstractMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoMapper; + +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; + +/** + * The derby implementation of ConfigInfoMapper. + * + * @author hyx + **/ + +public class ConfigInfoMapperByDerby extends AbstractMapper implements ConfigInfoMapper { + + private static final String DATA_ID = "dataId"; + + private static final String GROUP = "group"; + + private static final String APP_NAME = "appName"; + + private static final String CONTENT = "content"; + + private static final String TENANT = "tenant"; + + @Override + public String findConfigMaxId() { + return "SELECT max(id) FROM config_info"; + } + + @Override + public String findAllDataIdAndGroup() { + return "SELECT DISTINCT data_id, group_id FROM config_info"; + } + + @Override + public String findConfigInfoByDataIdFetchRows(int startRow, int pageSize) { + return "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id = ? AND " + + "tenant_id = ?" + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findConfigInfoByDataIdAndAppCountRows() { + return "SELECT count(*) FROM config_info WHERE data_id = ? AND tenant_id = ? AND app_name = ?"; + } + + @Override + public String findConfigInfoByDataIdAndAppFetchRows(int startRow, int pageSize) { + return "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id = ? AND " + + "tenant_id = ? AND app_name = ?" + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + + " ROWS ONLY"; + } + + @Override + public String count() { + return "SELECT COUNT(*) FROM config_info"; + } + + @Override + public String findConfigInfoByAppCountRows() { + return "SELECT count(*) FROM config_info WHERE tenant_id LIKE ? AND app_name = ?"; + } + + @Override + public String findConfigInfoByAppFetchRows(int startRow, int pageSize) { + return "SELECT ID,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE tenant_id LIKE ? AND " + + "app_name = ?" + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String configInfoLikeTenantCount() { + return "SELECT count(*) FROM config_info WHERE tenant_id LIKE ?"; + } + + @Override + public String getTenantIdList() { + return "SELECT tenant_id FROM config_info WHERE tenant_id != '' GROUP BY tenant_id OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; + } + + @Override + public String getGroupIdList() { + return "SELECT group_id FROM config_info WHERE tenant_id ='' GROUP BY group_id OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; + } + + @Override + public String findAllConfigKey() { + return " SELECT data_id,group_id,app_name FROM " + + " ( SELECT id FROM config_info WHERE tenant_id LIKE ? ORDER BY id OFFSET ? ROWS FETCH NEXT ? ROWS ONLY ) " + + "g, config_info t WHERE g.id = t.id "; + } + + @Override + public String findAllConfigInfoBaseFetchRows(int startRow, int pageSize) { + return "SELECT t.id,data_id,group_id,content,md5 " + + " FROM ( SELECT id FROM config_info ORDER BY id OFFSET ? ROWS FETCH NEXT ? ROWS ONLY ) " + + " g, config_info t WHERE g.id = t.id "; + } + + @Override + public String findAllConfigInfoForDumpAllFetchRows(int startRow, int pageSize) { + return " SELECT t.id,data_id,group_id,tenant_id,app_name,content,type,md5,gmt_modified " + + " FROM ( SELECT id FROM config_info ORDER BY id OFFSET ? ROWS FETCH NEXT ? ROWS ONLY)" + + " g, config_info t WHERE g.id = t.id "; + } + + @Override + public String findAllConfigInfoFragment() { + return "SELECT id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified,type FROM config_info WHERE id > ? " + + "ORDER BY id ASC OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; + } + + @Override + public String findChangeConfig() { + return "SELECT data_id, group_id, tenant_id, app_name, content, gmt_modified FROM config_info WHERE " + + "gmt_modified > = ? AND gmt_modified <= ?"; + } + + @Override + public String findChangeConfigCountRows(Map params, final Timestamp startTime, + final Timestamp endTime) { + final String tenant = params.get(TENANT); + final String dataId = params.get(DATA_ID); + final String group = params.get(GROUP); + final String appName = params.get(APP_NAME); + final String sqlCountRows = "SELECT count(*) FROM config_info WHERE "; + String where = " 1=1 "; + + if (!StringUtils.isBlank(dataId)) { + where += " AND data_id LIKE ? "; + } + if (!StringUtils.isBlank(group)) { + where += " AND group_id LIKE ? "; + } + + if (!StringUtils.isBlank(tenant)) { + where += " AND tenant_id = ? "; + } + + if (!StringUtils.isBlank(appName)) { + where += " AND app_name = ? "; + } + if (startTime != null) { + where += " AND gmt_modified >=? "; + } + if (endTime != null) { + where += " AND gmt_modified <=? "; + } + return sqlCountRows + where; + } + + @Override + public String findChangeConfigFetchRows(Map params, final Timestamp startTime, + final Timestamp endTime, int startRow, int pageSize, long lastMaxId) { + final String tenant = params.get(TENANT); + final String dataId = params.get(DATA_ID); + final String group = params.get(GROUP); + final String appName = params.get(APP_NAME); + final String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,app_name,content,type,md5,gmt_modified FROM" + + " config_info WHERE "; + String where = " 1=1 "; + + if (!StringUtils.isBlank(dataId)) { + where += " AND data_id LIKE ? "; + } + if (!StringUtils.isBlank(group)) { + where += " AND group_id LIKE ? "; + } + + if (!StringUtils.isBlank(tenant)) { + where += " AND tenant_id = ? "; + } + + if (!StringUtils.isBlank(appName)) { + where += " AND app_name = ? "; + } + if (startTime != null) { + where += " AND gmt_modified >=? "; + } + if (endTime != null) { + where += " AND gmt_modified <=? "; + } + return sqlFetchRows + where + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String listGroupKeyMd5ByPageFetchRows() { + return " SELECT t.id,data_id,group_id,tenant_id,app_name,type,md5,gmt_modified " + + "FROM ( SELECT id FROM config_info ORDER BY id OFFSET ? ROWS FETCH NEXT ? ROWS ONLY ) g, config_info t WHERE g.id = t.id"; + } + + @Override + public String findAllConfigInfo4Export(List ids, Map params) { + String sql = "SELECT id,data_id,group_id,tenant_id,app_name,content,type,md5,gmt_create,gmt_modified,src_user," + + "src_ip,c_desc,c_use,effect,c_schema,encrypted_data_key FROM config_info"; + StringBuilder where = new StringBuilder(" WHERE "); + if (!CollectionUtils.isEmpty(ids)) { + where.append(" id IN ("); + for (int i = 0; i < ids.size(); i++) { + if (i != 0) { + where.append(", "); + } + where.append('?'); + } + where.append(") "); + } else { + where.append(" tenant_id = ? "); + if (!StringUtils.isEmpty(params.get(DATA_ID))) { + where.append(" AND data_id LIKE ? "); + } + if (StringUtils.isNotEmpty(params.get(GROUP))) { + where.append(" AND group_id = ? "); + } + if (StringUtils.isNotEmpty(params.get(APP_NAME))) { + where.append(" AND app_name = ? "); + } + } + return sql + where; + } + + @Override + public String findConfigInfoByBatch(int paramSize) { + String sqlStart = "SELECT data_id, group_id, tenant_id, app_name, content FROM config_info" + + " WHERE group_id = ? AND tenant_id = ? AND data_id IN ("; + String sqlEnd = ")"; + StringBuilder subQuerySql = new StringBuilder(); + + for (int i = 0; i < paramSize; i++) { + subQuerySql.append('?'); + if (i != paramSize - 1) { + subQuerySql.append(','); + } + } + + return sqlStart + subQuerySql.toString() + sqlEnd; + } + + @Override + public String findConfigInfoLikeCountRows(Map params) { + final String sqlCountRows = "SELECT count(*) FROM config_info WHERE "; + String where = " 1=1 "; + if (!StringUtils.isEmpty(params.get(DATA_ID))) { + where += " AND data_id LIKE ? "; + } + if (!StringUtils.isBlank(params.get(GROUP))) { + where += " AND group_id LIKE ? "; + } + where += " AND tenant_id LIKE ? "; + if (!StringUtils.isBlank(params.get(APP_NAME))) { + where += " AND app_name = ? "; + } + if (!StringUtils.isBlank(params.get(CONTENT))) { + where += " AND content LIKE ? "; + } + return sqlCountRows + where; + } + + @Override + public String findConfigInfoLikeFetchRows(Map params, int startRow, int pageSize) { + final String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE "; + String where = " 1=1 "; + if (!StringUtils.isEmpty(params.get(DATA_ID))) { + where += " AND data_id LIKE ? "; + } + if (!StringUtils.isBlank(params.get(GROUP))) { + where += " AND group_id LIKE ? "; + } + where += " AND tenant_id LIKE ? "; + if (!StringUtils.isBlank(params.get(APP_NAME))) { + where += " AND app_name = ? "; + } + if (!StringUtils.isBlank(params.get(CONTENT))) { + where += " AND content LIKE ? "; + } + return sqlFetchRows + where + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findConfigInfoBaseLikeCountRows(Map params) { + final String sqlCountRows = "SELECT count(*) FROM config_info WHERE "; + String where = " 1=1 AND tenant_id='' "; + if (!StringUtils.isBlank(params.get(DATA_ID))) { + where += " AND data_id LIKE ? "; + } + if (!StringUtils.isBlank(params.get(GROUP))) { + where += " AND group_id LIKE ? "; + } + if (!StringUtils.isBlank(params.get(CONTENT))) { + where += " AND content LIKE ? "; + } + return sqlCountRows + where; + } + + @Override + public String findConfigInfoBaseLikeFetchRows(Map params, int startRow, int pageSize) { + final String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,content FROM config_info WHERE "; + String where = " 1=1 AND tenant_id='' "; + if (!StringUtils.isBlank(params.get(DATA_ID))) { + where += " AND data_id LIKE ? "; + } + if (!StringUtils.isBlank(params.get(GROUP))) { + where += " AND group_id LIKE ? "; + } + if (!StringUtils.isBlank(params.get(CONTENT))) { + where += " AND content LIKE ? "; + } + return sqlFetchRows + where + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findConfigInfoByDataIdCountRows() { + return "SELECT count(*) FROM config_info WHERE data_id = ? AND tenant_id = ?"; + } + + @Override + public String findConfigInfoByDataIdAndAdvanceCountRows(Map params) { + final String appName = params.get("appName"); + StringBuilder sqlCount = new StringBuilder("SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=? "); + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" AND app_name=? "); + } + + return sqlCount.toString(); + } + + @Override + public String findConfigInfoByDataIdAndAdvanceFetchRows(Map params, int startRow, int pageSize) { + final String appName = params.get("appName"); + StringBuilder sql = new StringBuilder( + "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND tenant_id=? "); + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND app_name=? "); + } + + return sql.toString() + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findConfigInfo4PageCountRows(Map params) { + final String appName = params.get(APP_NAME); + final String dataId = params.get(DATA_ID); + final String group = params.get(GROUP); + final String sqlCount = "SELECT count(*) FROM config_info"; + StringBuilder where = new StringBuilder(" WHERE "); + where.append(" tenant_id=? "); + if (StringUtils.isNotBlank(dataId)) { + where.append(" AND data_id=? "); + } + if (StringUtils.isNotBlank(group)) { + where.append(" AND group_id=? "); + } + if (StringUtils.isNotBlank(appName)) { + where.append(" AND app_name=? "); + } + return sqlCount + where; + } + + @Override + public String findConfigInfo4PageFetchRows(Map params, int startRow, int pageSize) { + final String appName = params.get(APP_NAME); + final String dataId = params.get(DATA_ID); + final String group = params.get(GROUP); + final String sql = "SELECT id,data_id,group_id,tenant_id,app_name,content,type FROM config_info"; + StringBuilder where = new StringBuilder(" WHERE "); + where.append(" tenant_id=? "); + if (StringUtils.isNotBlank(dataId)) { + where.append(" AND data_id=? "); + } + if (StringUtils.isNotBlank(group)) { + where.append(" AND group_id=? "); + } + if (StringUtils.isNotBlank(appName)) { + where.append(" AND app_name=? "); + } + return sql + where + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findConfigInfoBaseByDataIdCountRows() { + return "SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=?"; + } + + @Override + public String findConfigInfoBaseByDataIdFetchRows(int startRow, int pageSize) { + return "SELECT id,data_id,group_id,content FROM config_info WHERE data_id=? " + "AND tenant_id=?" + " OFFSET " + + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findConfigInfoByGroupCountRows() { + return "SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=?"; + } + + @Override + public String findConfigInfoByGroupFetchRows(int startRow, int pageSize) { + return "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE group_id=? AND " + + "tenant_id=?" + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findConfigInfoByGroupAndAppCountRows() { + return "SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=? AND app_name =?"; + } + + @Override + public String findConfigInfoByGroupAndAppFetchRows(int startRow, int pageSize) { + return "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE group_id=? AND " + + "tenant_id=? AND app_name =?" + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findConfigInfoByAdvanceCountRows(Map params) { + final String appName = params.get("appName"); + StringBuilder sqlCount = new StringBuilder("SELECT count(*) FROM config_info WHERE tenant_id LIKE ? "); + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" AND app_name=? "); + } + return sqlCount.toString(); + } + + @Override + public String findConfigInfoByAdvanceFetchRows(Map params, int startRow, int pageSize) { + final String appName = params.get("appName"); + StringBuilder sql = new StringBuilder( + "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info where tenant_id LIKE ? "); + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND app_name=? "); + } + return sql.toString() + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findConfigInfoBaseByGroupCountRows() { + return "SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=?"; + } + + @Override + public String findConfigInfoBaseByGroupFetchRows(int startRow, int pageSize) { + return "SELECT id,data_id,group_id,content FROM config_info WHERE group_id=? " + "AND tenant_id=?" + " OFFSET " + + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findConfigInfoLike4PageCountRows(Map params) { + final String appName = params.get("appName"); + final String content = params.get("content"); + final String dataId = params.get("dataId"); + final String group = params.get(GROUP); + final String sqlCountRows = "SELECT count(*) FROM config_info"; + StringBuilder where = new StringBuilder(" WHERE "); + where.append(" tenant_id LIKE ? "); + if (!StringUtils.isBlank(dataId)) { + where.append(" AND data_id LIKE ? "); + } + if (!StringUtils.isBlank(group)) { + where.append(" AND group_id LIKE ? "); + } + if (!StringUtils.isBlank(appName)) { + where.append(" AND app_name = ? "); + } + if (!StringUtils.isBlank(content)) { + where.append(" AND content LIKE ? "); + } + return sqlCountRows + where; + } + + @Override + public String findConfigInfoLike4PageFetchRows(Map params, int startRow, int pageSize) { + final String appName = params.get("appName"); + final String content = params.get("content"); + final String dataId = params.get(DATA_ID); + final String group = params.get(GROUP); + final String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,app_name,content,encrypted_data_key FROM config_info"; + StringBuilder where = new StringBuilder(" WHERE "); + where.append(" tenant_id LIKE ? "); + if (!StringUtils.isBlank(dataId)) { + where.append(" AND data_id LIKE ? "); + } + if (!StringUtils.isBlank(group)) { + where.append(" AND group_id LIKE ? "); + } + if (!StringUtils.isBlank(appName)) { + where.append(" AND app_name = ? "); + } + if (!StringUtils.isBlank(content)) { + where.append(" AND content LIKE ? "); + } + return sqlFetchRows + where + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findAllConfigInfoFetchRows(int startRow, int pageSize) { + return " SELECT t.id,data_id,group_id,tenant_id,app_name,content,md5 " + + " FROM ( SELECT id FROM config_info WHERE tenant_id LIKE ? ORDER BY id OFFSET ? ROWS FETCH NEXT ? ROWS ONLY )" + + " g, config_info t WHERE g.id = t.id "; + } + + @Override + public String findConfigInfoAdvanceInfo(Map params) { + final String appName = params.get("appName"); + + StringBuilder sql = new StringBuilder( + "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=? "); + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND app_name=? "); + } + + return sql.toString(); + } + + @Override + public String findConfigInfoByGroupAndAdvanceCountRows(Map params) { + final String appName = params.get("appName"); + StringBuilder sqlCount = new StringBuilder( + "SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=? "); + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" AND app_name=? "); + } + return sqlCount.toString(); + } + + @Override + public String findConfigInfoByGroupAndAdvanceFetchRows(Map params, int startRow, int pageSize) { + final String appName = params.get("appName"); + StringBuilder sql = new StringBuilder( + "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE group_id=? AND tenant_id=? "); + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND app_name=? "); + } + return sql.toString() + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findConfigInfosByIds(int idSize) { + StringBuilder sql = new StringBuilder( + "SELECT id,data_id,group_id,tenant_id,app_name,content,md5 FROM config_info WHERE "); + sql.append("id IN ("); + for (int i = 0; i < idSize; i++) { + if (i != 0) { + sql.append(", "); + } + sql.append('?'); + } + sql.append(") "); + return sql.toString(); + } + + @Override + public String removeConfigInfoByIdsAtomic(int size) { + StringBuilder sql = new StringBuilder("DELETE FROM config_info WHERE "); + sql.append("id IN ("); + for (int i = 0; i < size; i++) { + if (i != 0) { + sql.append(", "); + } + sql.append('?'); + } + sql.append(") "); + return sql.toString(); + } + + @Override + public String getTableName() { + return TableConstant.CONFIG_INFO; + } + + @Override + public String getDataSource() { + return DataSourceConstant.DERBY; + } +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoTagMapperByDerby.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoTagMapperByDerby.java new file mode 100644 index 000000000..dc4b6b130 --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoTagMapperByDerby.java @@ -0,0 +1,64 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.impl.derby; + +import com.alibaba.nacos.plugin.datasource.constants.DataSourceConstant; +import com.alibaba.nacos.plugin.datasource.constants.TableConstant; +import com.alibaba.nacos.plugin.datasource.mapper.AbstractMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoTagMapper; + +/** + * The derby implementation of ConfigInfoTagMapper. + * + * @author hyx + **/ + +public class ConfigInfoTagMapperByDerby extends AbstractMapper implements ConfigInfoTagMapper { + + @Override + public String updateConfigInfo4TagCas() { + return "UPDATE config_info_tag SET content = ?, md5 = ?, src_ip = ?,src_user = ?,gmt_modified = ?,app_name = ? " + + "WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND tag_id = ? AND (md5 = ? OR md5 IS NULL OR md5 = '')"; + } + + @Override + public String configInfoTagCount() { + return " SELECT count(*) FROM config_info_tag "; + } + + @Override + public String count() { + return " SELECT count(*) FROM config_info_tag "; + } + + @Override + public String findAllConfigInfoTagForDumpAllFetchRows(int startRow, int pageSize) { + return " SELECT t.id,data_id,group_id,tenant_id,tag_id,app_name,content,md5,gmt_modified " + + " FROM ( SELECT id FROM config_info_tag ORDER BY id OFFSET ? ROWS FETCH NEXT ? ROWS ONLY ) " + + " g, config_info_tag t WHERE g.id = t.id "; + } + + @Override + public String getTableName() { + return TableConstant.CONFIG_INFO_TAG; + } + + @Override + public String getDataSource() { + return DataSourceConstant.DERBY; + } +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoTagsRelationMapperByDerby.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoTagsRelationMapperByDerby.java new file mode 100644 index 000000000..142eada34 --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/ConfigInfoTagsRelationMapperByDerby.java @@ -0,0 +1,343 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.impl.derby; + +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.plugin.datasource.constants.DataSourceConstant; +import com.alibaba.nacos.plugin.datasource.constants.TableConstant; +import com.alibaba.nacos.plugin.datasource.mapper.AbstractMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigTagsRelationMapper; + +import java.util.Map; + +/** + * The derby implementation of ConfigTagsRelationMapper. + * + * @author hyx + **/ + +public class ConfigInfoTagsRelationMapperByDerby extends AbstractMapper implements ConfigTagsRelationMapper { + + @Override + public String findConfigInfoAdvanceInfo(Map params, int tagSize) { + final String appName = params.get("appName"); + StringBuilder sql = new StringBuilder( + "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " + + "config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.group_id=? AND a.tenant_id=? "); + sql.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + sql.append(", "); + } + sql.append('?'); + } + sql.append(") "); + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND a.app_name=? "); + } + return sql.toString(); + } + + @Override + public String findConfigInfoByDataIdAndAdvanceCountRows(Map params, int tagSize) { + final String appName = params.get("appName"); + StringBuilder sqlCount = new StringBuilder( + "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.tenant_id=? "); + + sqlCount.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + sqlCount.append(", "); + } + sqlCount.append('?'); + } + sqlCount.append(") "); + + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" AND a.app_name=? "); + } + return sqlCount.toString(); + } + + @Override + public String findConfigInfoByDataIdAndAdvanceFetchRows(Map params, int tagSize, int startRow, int pageSize) { + final String appName = params.get("appName"); + StringBuilder sql = new StringBuilder( + "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " + + "config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.tenant_id=? "); + + sql.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + sql.append(", "); + } + sql.append('?'); + } + sql.append(") "); + + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND a.app_name=? "); + } + return sql.toString() + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findConfigInfo4PageCountRows(final Map params, int tagSize) { + final String appName = params.get("appName"); + final String dataId = params.get("dataId"); + final String group = params.get("group"); + StringBuilder where = new StringBuilder(" WHERE "); + final String sqlCount = "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id"; + + where.append(" a.tenant_id=? "); + + if (StringUtils.isNotBlank(dataId)) { + where.append(" AND a.data_id=? "); + } + if (StringUtils.isNotBlank(group)) { + where.append(" AND a.group_id=? "); + } + if (StringUtils.isNotBlank(appName)) { + where.append(" AND a.app_name=? "); + } + + where.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + where.append(", "); + } + where.append('?'); + } + where.append(") "); + return sqlCount + where; + } + + @Override + public String findConfigInfo4PageFetchRows(final Map params, int tagSize, int startRow, int pageSize) { + final String appName = params.get("appName"); + final String dataId = params.get("dataId"); + final String group = params.get("group"); + StringBuilder where = new StringBuilder(" WHERE "); + final String sql = "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " + + "config_tags_relation b ON a.id=b.id"; + + where.append(" a.tenant_id=? "); + + if (StringUtils.isNotBlank(dataId)) { + where.append(" AND a.data_id=? "); + } + if (StringUtils.isNotBlank(group)) { + where.append(" AND a.group_id=? "); + } + if (StringUtils.isNotBlank(appName)) { + where.append(" AND a.app_name=? "); + } + + where.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + where.append(", "); + } + where.append('?'); + } + where.append(") "); + return sql + where + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findConfigInfoByGroupAndAdvanceCountRows(final Map params, int tagSize) { + final String appName = params.get("appName"); + StringBuilder sqlCount = new StringBuilder( + "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.group_id=? AND a.tenant_id=? "); + sqlCount.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + sqlCount.append(", "); + } + sqlCount.append('?'); + } + sqlCount.append(") "); + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" AND a.app_name=? "); + } + return sqlCount.toString(); + } + + @Override + public String findConfigInfoByGroupAndAdvanceFetchRows(final Map params, int tagSize, int startRow, int pageSize) { + final String appName = params.get("appName"); + StringBuilder sql = new StringBuilder( + "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " + + "config_tags_relation b ON a.id=b.id WHERE a.group_id=? AND a.tenant_id=? "); + + sql.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + sql.append(", "); + } + sql.append('?'); + } + sql.append(") "); + + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND a.app_name=? "); + } + return sql.toString() + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String findConfigInfoLike4PageCountRows(final Map params, int tagSize) { + final String appName = params.get("appName"); + final String content = params.get("content"); + final String dataId = params.get("dataId"); + final String group = params.get("group"); + StringBuilder where = new StringBuilder(" WHERE "); + final String sqlCountRows = "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id "; + where.append(" a.tenant_id LIKE ? "); + if (!StringUtils.isBlank(dataId)) { + where.append(" AND a.data_id LIKE ? "); + } + if (!StringUtils.isBlank(group)) { + where.append(" AND a.group_id LIKE ? "); + } + if (!StringUtils.isBlank(appName)) { + where.append(" AND a.app_name = ? "); + } + if (!StringUtils.isBlank(content)) { + where.append(" AND a.content LIKE ? "); + } + + where.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + where.append(", "); + } + where.append('?'); + } + where.append(") "); + return sqlCountRows + where; + } + + @Override + public String findConfigInfoLike4PageFetchRows(final Map params, int tagSize, int startRow, int pageSize) { + final String appName = params.get("appName"); + final String content = params.get("content"); + final String dataId = params.get("dataId"); + final String group = params.get("group"); + StringBuilder where = new StringBuilder(" WHERE "); + final String sqlFetchRows = + "SELECT a.ID,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " + + "config_tags_relation b ON a.id=b.id "; + + where.append(" a.tenant_id LIKE ? "); + if (!StringUtils.isBlank(dataId)) { + where.append(" AND a.data_id LIKE ? "); + } + if (!StringUtils.isBlank(group)) { + where.append(" AND a.group_id LIKE ? "); + } + if (!StringUtils.isBlank(appName)) { + where.append(" AND a.app_name = ? "); + } + if (!StringUtils.isBlank(content)) { + where.append(" AND a.content LIKE ? "); + } + + where.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + where.append(", "); + } + where.append('?'); + } + where.append(") "); + return sqlFetchRows + where + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String addConfigTagRelationAtomic() { + return "INSERT INTO config_tags_relation(id,tag_name,tag_type,data_id,group_id,tenant_id) " + + "VALUES(?,?,?,?,?,?)"; + } + + @Override + public String removeTagByIdAtomic() { + return "DELETE FROM config_tags_relation WHERE id=?"; + } + + @Override + public String getConfigTagsByTenant() { + return "SELECT tag_name FROM config_tags_relation WHERE tenant_id = ? "; + } + + @Override + public String selectTagByConfig() { + return "SELECT tag_name FROM config_tags_relation WHERE data_id=? AND group_id=? AND tenant_id = ? "; + } + + @Override + public String findConfigInfoByAdvanceCountRows(Map params, int tagSize) { + final String appName = params.get("appName"); + StringBuilder sqlCount = new StringBuilder( + "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.tenant_id=? "); + + sqlCount.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + sqlCount.append(", "); + } + sqlCount.append('?'); + } + sqlCount.append(") "); + + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" AND a.app_name=? "); + } + return sqlCount.toString(); + } + + @Override + public String findConfigInfoByAdvanceFetchRows(Map params, int tagSize, int startRow, int pageSize) { + final String appName = params.get("appName"); + StringBuilder sql = new StringBuilder( + "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " + + "config_tags_relation b ON a.id=b.id WHERE a.tenant_id=? "); + sql.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + sql.append(", "); + } + sql.append('?'); + } + sql.append(") "); + + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND a.app_name=? "); + } + return sql.toString() + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; + } + + @Override + public String getTableName() { + return TableConstant.CONFIG_TAGS_RELATION; + } + + @Override + public String getDataSource() { + return DataSourceConstant.DERBY; + } +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/HistoryConfigInfoMapperByDerby.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/HistoryConfigInfoMapperByDerby.java new file mode 100644 index 000000000..ac2035fc7 --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/derby/HistoryConfigInfoMapperByDerby.java @@ -0,0 +1,75 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.impl.derby; + +import com.alibaba.nacos.plugin.datasource.constants.DataSourceConstant; +import com.alibaba.nacos.plugin.datasource.constants.TableConstant; +import com.alibaba.nacos.plugin.datasource.mapper.AbstractMapper; +import com.alibaba.nacos.plugin.datasource.mapper.HistoryConfigInfoMapper; + +/** + * The derby implementation of ConfigInfoMapper. + * + * @author hyx + **/ + +public class HistoryConfigInfoMapperByDerby extends AbstractMapper implements HistoryConfigInfoMapper { + + @Override + public String removeConfigHistory() { + return "DELETE FROM his_config_info WHERE id IN( " + + "SELECT id FROM his_config_info WHERE gmt_modified < ? OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY)"; + } + + @Override + public String findConfigHistoryCountByTime() { + return "SELECT count(*) FROM his_config_info WHERE gmt_modified < ?"; + } + + @Override + public String findDeletedConfig() { + return "SELECT DISTINCT data_id, group_id, tenant_id FROM his_config_info WHERE op_type = 'D' AND " + + "gmt_modified >=? AND gmt_modified <= ?"; + } + + @Override + public String findConfigHistoryCountRows() { + return "SELECT count(*) FROM his_config_info WHERE data_id = ? AND group_id = ? AND tenant_id = ?"; + } + + @Override + public String findConfigHistoryFetchRows() { + return "SELECT nid,data_id,group_id,tenant_id,app_name,src_ip,src_user,op_type,gmt_create,gmt_modified FROM his_config_info " + + "WHERE data_id = ? AND group_id = ? AND tenant_id = ? ORDER BY nid DESC"; + } + + @Override + public String detailPreviousConfigHistory() { + return "SELECT nid,data_id,group_id,tenant_id,app_name,content,md5,src_user,src_ip,op_type,gmt_create,gmt_modified " + + "FROM his_config_info WHERE nid = (SELECT max(nid) FROM his_config_info WHERE id = ?)"; + } + + @Override + public String getTableName() { + return TableConstant.HIS_CONFIG_INFO; + } + + @Override + public String getDataSource() { + return DataSourceConstant.DERBY; + } +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigInfoAggrMapperByMySql.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigInfoAggrMapperByMySql.java new file mode 100644 index 000000000..af30fb7dc --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigInfoAggrMapperByMySql.java @@ -0,0 +1,96 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.impl.mysql; + +import com.alibaba.nacos.plugin.datasource.constants.DataSourceConstant; +import com.alibaba.nacos.plugin.datasource.constants.TableConstant; +import com.alibaba.nacos.plugin.datasource.mapper.AbstractMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoAggrMapper; + +import java.util.List; + +/** + * The mysql implementation of ConfigInfoAggrMapper. + * + * @author hyx + **/ + +public class ConfigInfoAggrMapperByMySql extends AbstractMapper implements ConfigInfoAggrMapper { + + @Override + public String batchRemoveAggr(List datumList) { + final StringBuilder datumString = new StringBuilder(); + for (String datum : datumList) { + datumString.append('\'').append(datum).append("',"); + } + datumString.deleteCharAt(datumString.length() - 1); + return "DELETE FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND datum_id IN (" + + datumString.toString() + ")"; + } + + @Override + public String aggrConfigInfoCount(int size, boolean isIn) { + StringBuilder sql = new StringBuilder( + " SELECT count(*) FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND datum_id"); + if (isIn) { + sql.append(" IN ("); + } else { + sql.append(" NOT IN ("); + } + for (int i = 0; i < size; i++) { + if (i > 0) { + sql.append(", "); + } + sql.append('?'); + } + sql.append(')'); + + return sql.toString(); + } + + @Override + public String aggrConfigInfoCount() { + return " SELECT count(*) FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ?"; + } + + @Override + public String findConfigInfoAggr() { + return "SELECT data_id,group_id,tenant_id,datum_id,app_name,content FROM " + + "config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ? ORDER BY datum_id"; + } + + @Override + public String findConfigInfoAggrByPageFetchRows(int startRow, int pageSize) { + return "SELECT data_id,group_id,tenant_id,datum_id,app_name,content FROM config_info_aggr WHERE data_id= ? AND " + + "group_id= ? AND tenant_id= ? ORDER BY datum_id LIMIT ?,?"; + } + + @Override + public String findAllAggrGroup() { + return "SELECT DISTINCT data_id, group_id, tenant_id FROM config_info_aggr"; + } + + @Override + public String getTableName() { + return TableConstant.CONFIG_INFO_AGGR; + } + + @Override + public String getDataSource() { + return DataSourceConstant.MYSQL; + } +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigInfoBetaMapperByMySql.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigInfoBetaMapperByMySql.java new file mode 100644 index 000000000..182a5ae5d --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigInfoBetaMapperByMySql.java @@ -0,0 +1,59 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.impl.mysql; + +import com.alibaba.nacos.plugin.datasource.constants.DataSourceConstant; +import com.alibaba.nacos.plugin.datasource.constants.TableConstant; +import com.alibaba.nacos.plugin.datasource.mapper.AbstractMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoBetaMapper; + +/** + * The mysql implementation of ConfigInfoBetaMapper. + * + * @author hyx + **/ + +public class ConfigInfoBetaMapperByMySql extends AbstractMapper implements ConfigInfoBetaMapper { + + @Override + public String updateConfigInfo4BetaCas() { + return "UPDATE config_info_beta SET content = ?,md5 = ?,beta_ips = ?,src_ip = ?,src_user = ?,gmt_modified = ?,app_name = ? " + + "WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND (md5 = ? or md5 is null or md5 = '')"; + } + + @Override + public String count() { + return "SELECT count(*) FROM config_info_beta"; + } + + @Override + public String findAllConfigInfoBetaForDumpAllFetchRows(int startRow, int pageSize) { + return " SELECT t.id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified,beta_ips,encrypted_data_key " + + " FROM ( SELECT id FROM config_info_beta ORDER BY id LIMIT ?,? )" + + " g, config_info_beta t WHERE g.id = t.id "; + } + + @Override + public String getTableName() { + return TableConstant.CONFIG_INFO_BETA; + } + + @Override + public String getDataSource() { + return DataSourceConstant.MYSQL; + } +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigInfoMapperByMySql.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigInfoMapperByMySql.java new file mode 100644 index 000000000..6a64bebc3 --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigInfoMapperByMySql.java @@ -0,0 +1,595 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.impl.mysql; + +import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.plugin.datasource.constants.DataSourceConstant; +import com.alibaba.nacos.plugin.datasource.constants.TableConstant; +import com.alibaba.nacos.plugin.datasource.mapper.AbstractMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoMapper; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * The mysql implementation of ConfigInfoMapper. + * + * @author hyx + **/ + +public class ConfigInfoMapperByMySql extends AbstractMapper implements ConfigInfoMapper { + + private static final String DATA_ID = "dataId"; + + private static final String GROUP = "group"; + + private static final String APP_NAME = "appName"; + + private static final String CONTENT = "content"; + + private static final String TENANT = "tenant"; + + @Override + public String findConfigMaxId() { + return "SELECT max(id) FROM config_info"; + } + + @Override + public String findAllDataIdAndGroup() { + return "SELECT DISTINCT data_id, group_id FROM config_info"; + } + + @Override + public String findConfigInfoByDataIdFetchRows(int startRow, int pageSize) { + return "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info " + + "WHERE data_id = ? AND tenant_id = ? " + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findConfigInfoByDataIdAndAppCountRows() { + return "SELECT count(*) FROM config_info WHERE data_id= ? AND tenant_id= ? AND app_name= ?"; + } + + @Override + public String findConfigInfoByDataIdAndAppFetchRows(int startRow, int pageSize) { + return "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info " + + "WHERE data_id= ? AND tenant_id= ? AND app_name= ?" + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String count() { + return "SELECT count(*) FROM config_info"; + } + + @Override + public String findConfigInfoByAppCountRows() { + return "SELECT count(*) FROM config_info WHERE tenant_id LIKE ? AND app_name= ?"; + } + + @Override + public String findConfigInfoByAppFetchRows(int startRow, int pageSize) { + return "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info" + + " WHERE tenant_id LIKE ? AND app_name= ?" + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String configInfoLikeTenantCount() { + return "SELECT count(*) FROM config_info WHERE tenant_id LIKE ?"; + } + + @Override + public String getTenantIdList() { + return "SELECT tenant_id FROM config_info WHERE tenant_id != '' GROUP BY tenant_id LIMIT ?, ?"; + } + + @Override + public String getGroupIdList() { + return "SELECT group_id FROM config_info WHERE tenant_id ='' GROUP BY group_id LIMIT ?, ?"; + } + + @Override + public String findAllConfigKey() { + return " SELECT data_id,group_id,app_name FROM ( " + + " SELECT id FROM config_info WHERE tenant_id LIKE ? ORDER BY id LIMIT ?, ? )" + + " g, config_info t WHERE g.id = t.id "; + } + + @Override + public String findAllConfigInfoBaseFetchRows(int startRow, int pageSize) { + return " SELECT t.id,data_id,group_id,content,md5" + + " FROM ( SELECT id FROM config_info ORDER BY id LIMIT ?,? ) " + + " g, config_info t WHERE g.id = t.id "; + } + + @Override + public String findAllConfigInfoForDumpAllFetchRows(int startRow, int pageSize) { + return " SELECT t.id,type,data_id,group_id,tenant_id,app_name,content,type,md5,gmt_modified " + + " FROM ( SELECT id FROM config_info ORDER BY id LIMIT ?,? )" + + " g, config_info t WHERE g.id = t.id "; + } + + @Override + public String findAllConfigInfoFragment() { + return "SELECT id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified,type,encrypted_data_key " + + "FROM config_info WHERE id > ? ORDER BY id ASC LIMIT ?,?"; + } + + @Override + public String findChangeConfig() { + return "SELECT data_id, group_id, tenant_id, app_name, content, gmt_modified,encrypted_data_key " + + "FROM config_info WHERE gmt_modified >= ? AND gmt_modified <= ?"; + } + + @Override + public String findChangeConfigCountRows(Map params, final Timestamp startTime, + final Timestamp endTime) { + final String tenant = params.get(TENANT); + final String dataId = params.get(DATA_ID); + final String group = params.get(GROUP); + final String appName = params.get(APP_NAME); + final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + final String sqlCountRows = "SELECT count(*) FROM config_info WHERE "; + String where = " 1=1 "; + if (!StringUtils.isBlank(dataId)) { + where += " AND data_id LIKE ? "; + } + if (!StringUtils.isBlank(group)) { + where += " AND group_id LIKE ? "; + } + + if (!StringUtils.isBlank(tenantTmp)) { + where += " AND tenant_id = ? "; + } + + if (!StringUtils.isBlank(appName)) { + where += " AND app_name = ? "; + } + if (startTime != null) { + where += " AND gmt_modified >=? "; + } + if (endTime != null) { + where += " AND gmt_modified <=? "; + } + return sqlCountRows + where; + } + + @Override + public String findChangeConfigFetchRows(Map params, final Timestamp startTime, + final Timestamp endTime, int startRow, int pageSize, long lastMaxId) { + final String tenant = params.get(TENANT); + final String dataId = params.get(DATA_ID); + final String group = params.get(GROUP); + final String appName = params.get(APP_NAME); + final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + final String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,app_name,content,type,md5,gmt_modified FROM config_info WHERE "; + String where = " 1=1 "; + if (!StringUtils.isBlank(dataId)) { + where += " AND data_id LIKE ? "; + } + if (!StringUtils.isBlank(group)) { + where += " AND group_id LIKE ? "; + } + + if (!StringUtils.isBlank(tenantTmp)) { + where += " AND tenant_id = ? "; + } + + if (!StringUtils.isBlank(appName)) { + where += " AND app_name = ? "; + } + if (startTime != null) { + where += " AND gmt_modified >=? "; + } + if (endTime != null) { + where += " AND gmt_modified <=? "; + } + return sqlFetchRows + where + " AND id > " + lastMaxId + " ORDER BY id ASC" + " LIMIT " + 0 + "," + pageSize; + } + + @Override + public String listGroupKeyMd5ByPageFetchRows() { + return " SELECT t.id,data_id,group_id,tenant_id,app_name,md5,type,gmt_modified,encrypted_data_key FROM " + + "( SELECT id FROM config_info ORDER BY id LIMIT ?,? ) g, config_info t WHERE g.id = t.id"; + } + + @Override + public String findAllConfigInfo4Export(List ids, Map params) { + String tenant = params.get("tenant"); + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + String sql = + "SELECT id,data_id,group_id,tenant_id,app_name,content,type,md5,gmt_create,gmt_modified,src_user,src_ip," + + "c_desc,c_use,effect,c_schema,encrypted_data_key FROM config_info"; + StringBuilder where = new StringBuilder(" WHERE "); + List paramList = new ArrayList<>(); + if (!CollectionUtils.isEmpty(ids)) { + where.append(" id IN ("); + for (int i = 0; i < ids.size(); i++) { + if (i != 0) { + where.append(", "); + } + where.append('?'); + paramList.add(ids.get(i)); + } + where.append(") "); + } else { + where.append(" tenant_id= ? "); + paramList.add(tenantTmp); + if (!StringUtils.isBlank(params.get(DATA_ID))) { + where.append(" AND data_id LIKE ? "); + } + if (StringUtils.isNotBlank(params.get(GROUP))) { + where.append(" AND group_id= ? "); + } + if (StringUtils.isNotBlank(params.get(APP_NAME))) { + where.append(" AND app_name= ? "); + } + } + return sql + where; + } + + @Override + public String findConfigInfoByBatch(int paramSize) { + String sqlStart = "SELECT data_id, group_id, tenant_id, app_name, content " + + "FROM config_info WHERE group_id = ? AND tenant_id = ? AND data_id IN ("; + String sqlEnd = ")"; + StringBuilder subQuerySql = new StringBuilder(); + + for (int i = 0; i < paramSize; i++) { + subQuerySql.append('?'); + if (i != paramSize - 1) { + subQuerySql.append(','); + } + } + return sqlStart + subQuerySql.toString() + sqlEnd; + } + + @Override + public String findConfigInfoLikeCountRows(Map params) { + final String sqlCountRows = "SELECT count(*) FROM config_info WHERE "; + String where = " 1=1 "; + if (!StringUtils.isEmpty(params.get(DATA_ID))) { + where += " AND data_id LIKE ? "; + } + if (!StringUtils.isEmpty(params.get(GROUP))) { + where += " AND group_id LIKE ? "; + } + where += " AND tenant_id LIKE ? "; + if (!StringUtils.isEmpty(params.get(APP_NAME))) { + where += " AND app_name = ? "; + } + if (!StringUtils.isBlank(params.get(CONTENT))) { + where += " AND content LIKE ? "; + } + + return sqlCountRows + where; + } + + @Override + public String findConfigInfoLikeFetchRows(Map params, int startRow, int pageSize) { + final String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE "; + String where = " 1=1 "; + if (!StringUtils.isEmpty(params.get(DATA_ID))) { + where += " AND data_id LIKE ? "; + } + if (!StringUtils.isEmpty(params.get(GROUP))) { + where += " AND group_id LIKE ? "; + } + where += " AND tenant_id LIKE ? "; + if (!StringUtils.isEmpty(params.get(APP_NAME))) { + where += " AND app_name = ? "; + } + if (!StringUtils.isBlank(params.get(CONTENT))) { + where += " AND content LIKE ? "; + } + + return sqlFetchRows + where + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findConfigInfoBaseLikeCountRows(Map params) { + final String sqlCountRows = "SELECT count(*) FROM config_info WHERE "; + String where = " 1=1 AND tenant_id='' "; + + if (!StringUtils.isBlank(params.get(DATA_ID))) { + where += " AND data_id LIKE ? "; + } + if (!StringUtils.isBlank(params.get(GROUP))) { + where += " AND group_id LIKE "; + } + if (!StringUtils.isBlank(params.get(CONTENT))) { + where += " AND content LIKE ? "; + } + return sqlCountRows + where; + } + + @Override + public String findConfigInfoBaseLikeFetchRows(Map params, int startRow, int pageSize) { + final String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,content FROM config_info WHERE "; + String where = " 1=1 AND tenant_id='' "; + if (!StringUtils.isBlank(params.get(DATA_ID))) { + where += " AND data_id LIKE ? "; + } + if (!StringUtils.isBlank(params.get(GROUP))) { + where += " AND group_id LIKE "; + } + if (!StringUtils.isBlank(params.get(CONTENT))) { + where += " AND content LIKE ? "; + } + return sqlFetchRows + where + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findConfigInfoByDataIdCountRows() { + return "SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=?"; + } + + @Override + public String findConfigInfoByDataIdAndAdvanceCountRows(Map params) { + final String appName = params.get(APP_NAME); + StringBuilder sqlCount = new StringBuilder("SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=? "); + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" AND app_name=? "); + } + return sqlCount.toString(); + } + + @Override + public String findConfigInfoByDataIdAndAdvanceFetchRows(Map params, int startRow, int pageSize) { + final String appName = params.get(APP_NAME); + StringBuilder sql = new StringBuilder( + "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND tenant_id=? "); + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND app_name=? "); + } + return sql.toString() + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findConfigInfo4PageCountRows(Map params) { + final String appName = params.get(APP_NAME); + final String dataId = params.get(DATA_ID); + final String group = params.get(GROUP); + final String sqlCount = "SELECT count(*) FROM config_info"; + StringBuilder where = new StringBuilder(" WHERE "); + where.append(" tenant_id=? "); + if (StringUtils.isNotBlank(dataId)) { + where.append(" AND data_id=? "); + } + if (StringUtils.isNotBlank(group)) { + where.append(" AND group_id=? "); + } + if (StringUtils.isNotBlank(appName)) { + where.append(" AND app_name=? "); + } + return sqlCount + where; + } + + @Override + public String findConfigInfo4PageFetchRows(Map params, int startRow, int pageSize) { + final String appName = params.get(APP_NAME); + final String dataId = params.get(DATA_ID); + final String group = params.get(GROUP); + final String sql = "SELECT id,data_id,group_id,tenant_id,app_name,content,type,encrypted_data_key FROM config_info"; + StringBuilder where = new StringBuilder(" WHERE "); + where.append(" tenant_id=? "); + if (StringUtils.isNotBlank(dataId)) { + where.append(" AND data_id=? "); + } + if (StringUtils.isNotBlank(group)) { + where.append(" AND group_id=? "); + } + if (StringUtils.isNotBlank(appName)) { + where.append(" AND app_name=? "); + } + return sql + where + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findConfigInfoBaseByDataIdCountRows() { + return "SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=?"; + } + + @Override + public String findConfigInfoBaseByDataIdFetchRows(int startRow, int pageSize) { + return "SELECT id,data_id,group_id,content FROM config_info WHERE data_id=? AND tenant_id=?" + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findConfigInfoByGroupCountRows() { + return "SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=?"; + } + + @Override + public String findConfigInfoByGroupFetchRows(int startRow, int pageSize) { + return "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE" + + " group_id=? AND tenant_id=?" + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findConfigInfoByGroupAndAppCountRows() { + return "SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=? AND app_name =?"; + } + + @Override + public String findConfigInfoByGroupAndAppFetchRows(int startRow, int pageSize) { + return "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE " + + "group_id=? AND tenant_id=? AND app_name =?" + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findConfigInfoByAdvanceCountRows(Map params) { + final String appName = params.get("appName"); + StringBuilder sqlCount = new StringBuilder("SELECT count(*) FROM config_info WHERE tenant_id LIKE ? "); + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" AND app_name=? "); + } + return sqlCount.toString(); + } + + @Override + public String findConfigInfoByAdvanceFetchRows(Map params, int startRow, int pageSize) { + final String appName = params.get("appName"); + StringBuilder sql = new StringBuilder( + "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE tenant_id LIKE ? "); + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND app_name=? "); + } + return sql.toString() + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findConfigInfoBaseByGroupCountRows() { + return "SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=?"; + } + + @Override + public String findConfigInfoBaseByGroupFetchRows(int startRow, int pageSize) { + return "SELECT id,data_id,group_id,content FROM config_info WHERE group_id=? AND tenant_id=?" + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findConfigInfoLike4PageCountRows(Map params) { + String dataId = params.get(DATA_ID); + String group = params.get(GROUP); + final String appName = params.get(APP_NAME); + final String content = params.get(CONTENT); + final String sqlCountRows = "SELECT count(*) FROM config_info"; + StringBuilder where = new StringBuilder(" WHERE "); + where.append(" tenant_id LIKE ? "); + if (!StringUtils.isBlank(dataId)) { + where.append(" AND data_id LIKE ? "); + } + if (!StringUtils.isBlank(group)) { + where.append(" AND group_id LIKE ? "); + } + if (!StringUtils.isBlank(appName)) { + where.append(" AND app_name = ? "); + } + if (!StringUtils.isBlank(content)) { + where.append(" AND content LIKE ? "); + } + return sqlCountRows + where; + } + + @Override + public String findConfigInfoLike4PageFetchRows(Map params, int startRow, int pageSize) { + String dataId = params.get(DATA_ID); + String group = params.get(GROUP); + final String appName = params.get(APP_NAME); + final String content = params.get(CONTENT); + final String sqlFetchRows = "SELECT id,data_id,group_id,tenant_id,app_name,content,encrypted_data_key FROM config_info"; + StringBuilder where = new StringBuilder(" WHERE "); + where.append(" tenant_id LIKE ? "); + if (!StringUtils.isBlank(dataId)) { + where.append(" AND data_id LIKE ? "); + } + if (!StringUtils.isBlank(group)) { + where.append(" AND group_id LIKE ? "); + } + if (!StringUtils.isBlank(appName)) { + where.append(" AND app_name = ? "); + } + if (!StringUtils.isBlank(content)) { + where.append(" AND content LIKE ? "); + } + return sqlFetchRows + where + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findAllConfigInfoFetchRows(int startRow, int pageSize) { + return " SELECT t.id,data_id,group_id,tenant_id,app_name,content,md5 " + + " FROM ( SELECT id FROM config_info WHERE tenant_id LIKE ? ORDER BY id LIMIT ?,? )" + + " g, config_info t WHERE g.id = t.id "; + } + + @Override + public String findConfigInfoAdvanceInfo(Map params) { + final String appName = params.get("appName"); + + StringBuilder sql = new StringBuilder( + "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=? "); + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND app_name=? "); + } + + return sql.toString(); + } + + @Override + public String findConfigInfoByGroupAndAdvanceCountRows(Map params) { + final String appName = params.get(APP_NAME); + StringBuilder sqlCount = new StringBuilder( + "SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=? "); + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" AND app_name=? "); + } + return sqlCount.toString(); + } + + @Override + public String findConfigInfoByGroupAndAdvanceFetchRows(Map params, int startRow, int pageSize) { + final String appName = params.get(APP_NAME); + StringBuilder sql = new StringBuilder( + "SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE group_id=? AND tenant_id=? "); + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND app_name=? "); + } + return sql.toString() + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findConfigInfosByIds(int idSize) { + StringBuilder sql = new StringBuilder( + "SELECT ID,data_id,group_id,tenant_id,app_name,content,md5 FROM config_info WHERE "); + sql.append("id IN ("); + for (int i = 0; i < idSize; i++) { + if (i != 0) { + sql.append(", "); + } + sql.append('?'); + } + sql.append(") "); + return sql.toString(); + } + + @Override + public String removeConfigInfoByIdsAtomic(int size) { + StringBuilder sql = new StringBuilder("DELETE FROM config_info WHERE "); + sql.append("id IN ("); + for (int i = 0; i < size; i++) { + if (i != 0) { + sql.append(", "); + } + sql.append('?'); + } + sql.append(") "); + return sql.toString(); + } + + @Override + public String getTableName() { + return TableConstant.CONFIG_INFO; + } + + @Override + public String getDataSource() { + return DataSourceConstant.MYSQL; + } +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigInfoTagMapperByMySql.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigInfoTagMapperByMySql.java new file mode 100644 index 000000000..6337284e3 --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigInfoTagMapperByMySql.java @@ -0,0 +1,64 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.impl.mysql; + +import com.alibaba.nacos.plugin.datasource.constants.DataSourceConstant; +import com.alibaba.nacos.plugin.datasource.constants.TableConstant; +import com.alibaba.nacos.plugin.datasource.mapper.AbstractMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoTagMapper; + +/** + * The mysql implementation of ConfigInfoTagMapper. + * + * @author hyx + **/ + +public class ConfigInfoTagMapperByMySql extends AbstractMapper implements ConfigInfoTagMapper { + + @Override + public String updateConfigInfo4TagCas() { + return "UPDATE config_info_tag SET content = ?, md5 = ?, src_ip = ?,src_user = ?,gmt_modified = ?,app_name = ? " + + "WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND tag_id = ? AND (md5 = ? OR md5 IS NULL OR md5 = '')"; + } + + @Override + public String configInfoTagCount() { + return "SELECT count(ID) FROM config_info_tag"; + } + + @Override + public String count() { + return "SELECT count(*) FROM config_info_tag"; + } + + @Override + public String findAllConfigInfoTagForDumpAllFetchRows(int startRow, int pageSize) { + return " SELECT t.id,data_id,group_id,tenant_id,tag_id,app_name,content,md5,gmt_modified " + + " FROM ( SELECT id FROM config_info_tag ORDER BY id LIMIT ?,? ) " + + "g, config_info_tag t WHERE g.id = t.id "; + } + + @Override + public String getTableName() { + return TableConstant.CONFIG_INFO_TAG; + } + + @Override + public String getDataSource() { + return DataSourceConstant.MYSQL; + } +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigTagsRelationMapperByMySql.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigTagsRelationMapperByMySql.java new file mode 100644 index 000000000..8a3349f57 --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/ConfigTagsRelationMapperByMySql.java @@ -0,0 +1,334 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.impl.mysql; + +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.plugin.datasource.constants.DataSourceConstant; +import com.alibaba.nacos.plugin.datasource.constants.TableConstant; +import com.alibaba.nacos.plugin.datasource.mapper.AbstractMapper; +import com.alibaba.nacos.plugin.datasource.mapper.ConfigTagsRelationMapper; + +import java.util.Map; + +/** + * The mysql implementation of ConfigTagsRelationMapper. + * + * @author hyx + **/ + +public class ConfigTagsRelationMapperByMySql extends AbstractMapper implements ConfigTagsRelationMapper { + + @Override + public String findConfigInfoAdvanceInfo(Map params, int tagSize) { + final String appName = params.get("appName"); + StringBuilder sql = new StringBuilder( + "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " + + "config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.group_id=? AND a.tenant_id=? "); + sql.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + sql.append(", "); + } + sql.append('?'); + } + sql.append(") "); + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND a.app_name=? "); + } + return sql.toString(); + } + + @Override + public String findConfigInfoByDataIdAndAdvanceCountRows(Map params, int tagSize) { + final String appName = params.get("appName"); + StringBuilder sqlCount = new StringBuilder( + "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.tenant_id=? "); + sqlCount.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + sqlCount.append(", "); + } + sqlCount.append('?'); + } + sqlCount.append(") "); + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" AND a.app_name=? "); + } + return sqlCount.toString(); + } + + @Override + public String findConfigInfoByDataIdAndAdvanceFetchRows(Map params, int tagSize, int startRow, int pageSize) { + final String appName = params.get("appName"); + StringBuilder sql = new StringBuilder( + "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " + + "config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.tenant_id=? "); + sql.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + sql.append(", "); + } + sql.append('?'); + } + sql.append(") "); + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND a.app_name=? "); + } + return sql.toString() + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findConfigInfo4PageCountRows(final Map params, final int tagSize) { + final String appName = params.get("appName"); + final String dataId = params.get("dataId"); + final String group = params.get("group"); + StringBuilder where = new StringBuilder(" WHERE "); + final String sqlCount = "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id"; + where.append(" a.tenant_id=? "); + if (StringUtils.isNotBlank(dataId)) { + where.append(" AND a.data_id=? "); + } + if (StringUtils.isNotBlank(group)) { + where.append(" AND a.group_id=? "); + } + if (StringUtils.isNotBlank(appName)) { + where.append(" AND a.app_name=? "); + } + where.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + where.append(", "); + } + where.append('?'); + } + where.append(") "); + return sqlCount + where; + } + + @Override + public String findConfigInfo4PageFetchRows(Map params, int tagSize, int startRow, int pageSize) { + final String appName = params.get("appName"); + final String dataId = params.get("dataId"); + final String group = params.get("group"); + StringBuilder where = new StringBuilder(" WHERE "); + final String sql = "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " + + "config_tags_relation b ON a.id=b.id"; + + where.append(" a.tenant_id=? "); + + if (StringUtils.isNotBlank(dataId)) { + where.append(" AND a.data_id=? "); + } + if (StringUtils.isNotBlank(group)) { + where.append(" AND a.group_id=? "); + } + if (StringUtils.isNotBlank(appName)) { + where.append(" AND a.app_name=? "); + } + + where.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + where.append(", "); + } + where.append('?'); + } + where.append(") "); + return sql + where + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findConfigInfoByGroupAndAdvanceCountRows(final Map params, int tagSize) { + final String appName = params.get("appName"); + StringBuilder sqlCount = new StringBuilder( + "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.group_id=? AND a.tenant_id=? "); + sqlCount.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + sqlCount.append(", "); + } + sqlCount.append('?'); + } + sqlCount.append(") "); + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" AND a.app_name=? "); + } + return sqlCount.toString(); + } + + @Override + public String findConfigInfoByGroupAndAdvanceFetchRows(final Map params, int tagSize, int startRow, int pageSize) { + final String appName = params.get("appName"); + StringBuilder sql = new StringBuilder( + "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " + + "config_tags_relation b ON a.id=b.id WHERE a.group_id=? AND a.tenant_id=? "); + + sql.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + sql.append(", "); + } + sql.append('?'); + } + sql.append(") "); + + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND a.app_name=? "); + } + return sql.toString() + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String findConfigInfoLike4PageCountRows(final Map params, int tagSize) { + final String appName = params.get("appName"); + final String content = params.get("content"); + final String dataId = params.get("dataId"); + final String group = params.get("group"); + StringBuilder where = new StringBuilder(" WHERE "); + final String sqlCountRows = "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id "; + + where.append(" a.tenant_id LIKE ? "); + if (!StringUtils.isBlank(dataId)) { + where.append(" AND a.data_id LIKE ? "); + } + if (!StringUtils.isBlank(group)) { + where.append(" AND a.group_id LIKE ? "); + } + if (!StringUtils.isBlank(appName)) { + where.append(" AND a.app_name = ? "); + } + if (!StringUtils.isBlank(content)) { + where.append(" AND a.content LIKE ? "); + } + + where.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + where.append(", "); + } + where.append('?'); + } + where.append(") "); + return sqlCountRows + where; + } + + @Override + public String findConfigInfoLike4PageFetchRows(final Map params, int tagSize, int startRow, int pageSize) { + final String appName = params.get("appName"); + final String content = params.get("content"); + final String dataId = params.get("dataId"); + final String group = params.get("group"); + StringBuilder where = new StringBuilder(" WHERE "); + final String sqlFetchRows = "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content " + + "FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id "; + + where.append(" a.tenant_id LIKE ? "); + if (!StringUtils.isBlank(dataId)) { + where.append(" AND a.data_id LIKE ? "); + } + if (!StringUtils.isBlank(group)) { + where.append(" AND a.group_id LIKE ? "); + } + if (!StringUtils.isBlank(appName)) { + where.append(" AND a.app_name = ? "); + } + if (!StringUtils.isBlank(content)) { + where.append(" AND a.content LIKE ? "); + } + + where.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + where.append(", "); + } + where.append('?'); + } + where.append(") "); + return sqlFetchRows + where + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String addConfigTagRelationAtomic() { + return "INSERT INTO config_tags_relation(id,tag_name,tag_type,data_id,group_id,tenant_id) VALUES(?,?,?,?,?,?)"; + } + + @Override + public String removeTagByIdAtomic() { + return "DELETE FROM config_tags_relation WHERE id=?"; + } + + @Override + public String getConfigTagsByTenant() { + return "SELECT tag_name FROM config_tags_relation WHERE tenant_id = ? "; + } + + @Override + public String selectTagByConfig() { + return "SELECT tag_name FROM config_tags_relation WHERE data_id=? AND group_id=? AND tenant_id = ? "; + } + + @Override + public String findConfigInfoByAdvanceCountRows(Map params, int tagSize) { + final String appName = params.get("appName"); + StringBuilder sqlCount = new StringBuilder( + "SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.tenant_id=? "); + + sqlCount.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + sqlCount.append(", "); + } + sqlCount.append('?'); + } + sqlCount.append(") "); + + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" AND a.app_name=? "); + } + return sqlCount.toString(); + } + + @Override + public String findConfigInfoByAdvanceFetchRows(Map params, int tagSize, int startRow, int pageSize) { + final String appName = params.get("appName"); + StringBuilder sql = new StringBuilder( + "SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " + + "config_tags_relation b ON a.id=b.id WHERE a.tenant_id=? "); + sql.append(" AND b.tag_name IN ("); + for (int i = 0; i < tagSize; i++) { + if (i != 0) { + sql.append(", "); + } + sql.append('?'); + } + sql.append(") "); + if (StringUtils.isNotBlank(appName)) { + sql.append(" AND a.app_name=? "); + } + return sql.toString() + " LIMIT " + startRow + "," + pageSize; + } + + @Override + public String getTableName() { + return TableConstant.CONFIG_TAGS_RELATION; + } + + @Override + public String getDataSource() { + return DataSourceConstant.MYSQL; + } +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/HistoryConfigInfoMapperByMySql.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/HistoryConfigInfoMapperByMySql.java new file mode 100644 index 000000000..f35c0fa39 --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/impl/mysql/HistoryConfigInfoMapperByMySql.java @@ -0,0 +1,73 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.impl.mysql; + +import com.alibaba.nacos.plugin.datasource.constants.DataSourceConstant; +import com.alibaba.nacos.plugin.datasource.constants.TableConstant; +import com.alibaba.nacos.plugin.datasource.mapper.AbstractMapper; +import com.alibaba.nacos.plugin.datasource.mapper.HistoryConfigInfoMapper; + +/** + * The mysql implementation of HistoryConfigInfoMapper. + * + * @author hyx + **/ + +public class HistoryConfigInfoMapperByMySql extends AbstractMapper implements HistoryConfigInfoMapper { + + @Override + public String removeConfigHistory() { + return "DELETE FROM his_config_info WHERE gmt_modified < ? LIMIT ?"; + } + + @Override + public String findConfigHistoryCountByTime() { + return "SELECT count(*) FROM his_config_info WHERE gmt_modified < ?"; + } + + @Override + public String findDeletedConfig() { + return "SELECT DISTINCT data_id, group_id, tenant_id FROM his_config_info WHERE op_type = 'D' AND gmt_modified >= ? AND gmt_modified <= ?"; + } + + @Override + public String findConfigHistoryCountRows() { + return "SELECT count(*) FROM his_config_info WHERE data_id = ? AND group_id = ? AND tenant_id = ?"; + } + + @Override + public String findConfigHistoryFetchRows() { + return "SELECT nid,data_id,group_id,tenant_id,app_name,src_ip,src_user,op_type,gmt_create,gmt_modified FROM his_config_info " + + "WHERE data_id = ? AND group_id = ? AND tenant_id = ? ORDER BY nid DESC"; + } + + @Override + public String detailPreviousConfigHistory() { + return "SELECT nid,data_id,group_id,tenant_id,app_name,content,md5,src_user,src_ip,op_type,gmt_create,gmt_modified " + + "FROM his_config_info WHERE nid = (SELECT max(nid) FROM his_config_info WHERE id = ?) "; + } + + @Override + public String getTableName() { + return TableConstant.HIS_CONFIG_INFO; + } + + @Override + public String getDataSource() { + return DataSourceConstant.MYSQL; + } +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/AbstractMapper.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/AbstractMapper.java new file mode 100644 index 000000000..7cf525113 --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/AbstractMapper.java @@ -0,0 +1,132 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.mapper; + +import java.util.List; + +/** + * The abstract mapper contains CRUD methods. + * + * @author hyx + **/ + +public abstract class AbstractMapper implements Mapper { + + @Override + public String select(List columns, List where) { + StringBuilder sql = new StringBuilder(); + String method = "SELECT "; + sql.append(method); + for (int i = 0; i < columns.size(); i++) { + sql.append(columns.get(i)); + if (i == columns.size() - 1) { + sql.append(" "); + } else { + sql.append(","); + } + } + sql.append(" FROM "); + sql.append(getTableName()); + sql.append(" "); + + if (where.size() == 0) { + return sql.toString(); + } + + sql.append("WHERE "); + for (int i = 0; i < where.size(); i++) { + sql.append(where.get(i)).append(" = ").append("?"); + if (i != where.size() - 1) { + sql.append(" AND "); + } + } + return sql.toString(); + } + + @Override + public String insert(List columns) { + StringBuilder sql = new StringBuilder(); + String method = "INSERT INTO "; + sql.append(method); + sql.append(getTableName()); + + int size = columns.size(); + sql.append("("); + for (int i = 0; i < size; i++) { + sql.append(columns.get(i)); + if (i != columns.size() - 1) { + sql.append(", "); + } + } + sql.append(") "); + + sql.append("VALUES"); + sql.append("("); + for (int i = 0; i < size; i++) { + sql.append("?"); + if (i != columns.size() - 1) { + sql.append(","); + } + } + sql.append(")"); + return sql.toString(); + } + + @Override + public String update(List columns, List where) { + StringBuilder sql = new StringBuilder(); + String method = "UPDATE "; + sql.append(method); + sql.append(getTableName()).append(" ").append("SET "); + + for (int i = 0; i < columns.size(); i++) { + sql.append(columns.get(i)).append(" = ").append("?"); + if (i != columns.size() - 1) { + sql.append(","); + } + } + + if (where.size() == 0) { + return sql.toString(); + } + + sql.append("WHERE "); + + for (int i = 0; i < where.size(); i++) { + sql.append(where.get(i)).append(" = ").append("?"); + if (i != where.size() - 1) { + sql.append(" AND "); + } + } + return sql.toString(); + } + + @Override + public String delete(List params) { + StringBuilder sql = new StringBuilder(); + String method = "DELETE "; + sql.append(method).append(" FROM ").append(getTableName()).append(" ").append("WHERE "); + for (int i = 0; i < params.size(); i++) { + sql.append(params.get(i)).append(" ").append("=").append(" ? "); + if (i != params.size() - 1) { + sql.append("AND "); + } + } + + return sql.toString(); + } +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigInfoAggrMapper.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigInfoAggrMapper.java new file mode 100644 index 000000000..a6847702e --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigInfoAggrMapper.java @@ -0,0 +1,89 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.mapper; + +import java.util.List; + +/** + * The mapper of config info. + * + * @author hyx + **/ + +public interface ConfigInfoAggrMapper extends Mapper { + + /** + * To delete aggregated data in bulk, you need to specify a list of datum. + * The default sql: + * DELETE FROM config_info_aggr WHERE data_id=? AND group_id=? AND tenant_id=? AND datum_id IN (...) + * + * @param datumList datumList + * @return The sql of deleting aggregated data in bulk. + */ + String batchRemoveAggr(List datumList); + + /** + * Get count of aggregation config info. + * The default sql: + * SELECT count(*) FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ? + * + * @param size datum id list size + * @param isIn search condition + * @return The sql of getting count of aggregation config info. + */ + String aggrConfigInfoCount(int size, boolean isIn); + + /** + * Get count of aggregation config info. + * The default sql: + * SELECT count(*) FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ? + * + * @return The count of getting count of aggregation config info. + */ + String aggrConfigInfoCount(); + + /** + * Find all data before aggregation under a dataId. It is guaranteed not to return NULL. + * The default sql: + * SELECT data_id,group_id,tenant_id,datum_id,app_name,content + * FROM config_info_aggr WHERE data_id=? AND group_id=? AND tenant_id=? ORDER BY datum_id + * + * @return The sql of finding all data before aggregation under a dataId. + */ + String findConfigInfoAggr(); + + /** + * Query aggregation config info. + * The default sql: + * SELECT data_id,group_id,tenant_id,datum_id,app_name,content FROM config_info_aggr WHERE data_id=? AND + * group_id=? AND tenant_id=? ORDER BY datum_id LIMIT ?,? + * + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of querying aggregation config info. + */ + String findConfigInfoAggrByPageFetchRows(int startRow, int pageSize); + + /** + * Find all aggregated data sets. + * The default sql: + * SELECT DISTINCT data_id, group_id, tenant_id FROM config_info_aggr + * + * @return The sql of finding all aggregated data sets. + */ + String findAllAggrGroup(); +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigInfoBetaMapper.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigInfoBetaMapper.java new file mode 100644 index 000000000..1f46dc47e --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigInfoBetaMapper.java @@ -0,0 +1,56 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.mapper; + +/** + * The beta config info mapper. + * + * @author hyx + **/ + +public interface ConfigInfoBetaMapper extends Mapper { + + /** + * Update beta configuration information. + * UPDATE config_info_beta SET content=?, md5=?, beta_ips=?, src_ip=?,src_user=?,gmt_modified=?,app_name=? + * WHERE data_id=? AND group_id=? AND tenant_id=? AND (md5=? or md5 is null or md5='') + * + * @return The sql of updating beta configuration information. + */ + String updateConfigInfo4BetaCas(); + + /** + * Query the count of beta configuration information. + * The default sql: + * SELECT count(*) FROM config_info_beta + * + * @return The sql of querying the count of beta configuration information. + */ + String count(); + + /** + * Query all beta config info for dump task. + * The default sql: + * SELECT t.id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified,beta_ips,encrypted_data_key + * FROM ( SELECT id FROM config_info_beta ORDER BY id LIMIT ?,? ) g, config_info_beta t WHERE g.id = t.id + * + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of querying all beta config info for dump task. + */ + String findAllConfigInfoBetaForDumpAllFetchRows(int startRow, int pageSize); +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigInfoMapper.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigInfoMapper.java new file mode 100644 index 000000000..7807274e6 --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigInfoMapper.java @@ -0,0 +1,547 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.mapper; + +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; + +/** + * The mapper of config info. + * + * @author hyx + **/ + +public interface ConfigInfoMapper extends Mapper { + + /** + * Get the maxId. + * The default sql: + * SELECT max(id) FROM config_info + * + * @return the sql of getting the maxId. + */ + String findConfigMaxId(); + + /** + * Find all dataId and group. + * The default sql: + * SELECT DISTINCT data_id, group_id FROM config_info + * + * @return The sql of finding all dataId and group. + */ + String findAllDataIdAndGroup(); + + /** + * Query configuration information based on dataId. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND tenant_id=? + * + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql to + */ + String findConfigInfoByDataIdFetchRows(int startRow, int pageSize); + + /** + * Query the count of the configInfo by dataId. + * The default sql: + * SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=? AND app_name=? + * + * @return The num of the count of configInfo. + */ + String findConfigInfoByDataIdAndAppCountRows(); + + /** + * Query configuration information based on dataId. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND tenant_id=? AND app_name=? + * + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of query configuration information based on dataId. + */ + String findConfigInfoByDataIdAndAppFetchRows(int startRow, int pageSize); + + /** + * The count of config_info table sql. + * The default sql: + * SELECT count(*) FROM config_info + * + * @return The sql of the count of config_info table. + */ + String count(); + + + /** + * Query the count of config_info by tenantId and appName. + * The default sql: + * SELECT count(*) FROM config_info WHERE tenant_id LIKE ? AND app_name=? + * + * @return The sql of querying the count of config_info. + */ + String findConfigInfoByAppCountRows(); + + /** + * Query configuration information based on group. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE tenant_id LIKE ? AND app_name=? + * + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of querying configration information based on group. + */ + String findConfigInfoByAppFetchRows(int startRow, int pageSize); + + /** + * Returns the number of configuration items. + * The default sql: + * SELECT count(*) FROM config_info WHERE tenant_id LIKE ? + * + * @return The sql of querying the number of configuration items. + */ + String configInfoLikeTenantCount(); + + /** + * Get tenant id list by page. + * The default sql: + * SELECT tenant_id FROM config_info WHERE tenant_id != '' GROUP BY tenant_id LIMIT ?, ? + * + * @return The sql of getting tenant id list by page. + */ + String getTenantIdList(); + + /** + * Get group id list by page. + * The default sql: + * SELECT group_id FROM config_info WHERE tenant_id ='' GROUP BY group_id LIMIT ?, ? + * + * @return The sql of getting group id list by page. + */ + String getGroupIdList(); + + /** + * Query all configuration information by page. + * The default sql: + * SELECT data_id,group_id,app_name FROM ( + * SELECT id FROM config_info WHERE tenant_id LIKE ? ORDER BY id LIMIT ?, ? ) g, + * config_info t WHERE g.id = t.id " + * + * @return The sql of querying all configuration information. + */ + String findAllConfigKey(); + + /** + * Query all configuration information by page. + * The default sql: + * SELECT t.id,data_id,group_id,content,md5 FROM ( + * SELECT id FROM config_info ORDER BY id LIMIT ?,?) g, + * config_info t WHERE g.id = t.id + * + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of querying all configuration information by page. + */ + String findAllConfigInfoBaseFetchRows(int startRow, int pageSize); + + /** + * Query all configuration information by page for dump task. + * The default sql: + * SELECT t.id,type,data_id,group_id,tenant_id,app_name,content,type,md5,gmt_modified + * FROM ( SELECT id FROM config_info ORDER BY id LIMIT ?,? ) g, config_info t + * WHERE g.id = t.id + * + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of querying all configuration information by page for dump task. + */ + String findAllConfigInfoForDumpAllFetchRows(int startRow, int pageSize); + + /** + * Query all config info. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified,type,encrypted_data_key + * FROM config_info WHERE id > ? ORDER BY id ASC LIMIT ?,? + * + * @return The sql of querying all config info. + */ + String findAllConfigInfoFragment(); + + /** + * Query change config. + * The default sql: + * SELECT data_id, group_id, tenant_id, app_name, content, gmt_modified,encrypted_data_key + * FROM config_info WHERE gmt_modified >=? AND gmt_modified <= ? + * + * @return The sql of querying change config. + */ + String findChangeConfig(); + + /** + * Get the count of config information. + * The default sql: + * SELECT count(*) FROM config_info WHERE ... + * + * @param params The map of params, the key is the parameter name(dataId, groupId, tenantId, appName, startTime, endTime, content), + * the value is the key's value. + * @param startTime start time + * @param endTime end time + * @return The sql of getting the count of config information. + */ + String findChangeConfigCountRows(Map params, final Timestamp startTime, final Timestamp endTime); + + /** + * According to the time period and configuration conditions to query the eligible configuration. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content,type,md5,gmt_modified FROM config_info WHERE ... + * + * @param params The map of params, the key is the parameter name(dataId, groupId, tenantId, appName, startTime, endTime, content), + * the value is the key's value. + * @param startTime start time + * @param endTime end time + * @param startRow The start index. + * @param pageSize The size of page. + * @param lastMaxId The max id. + * @return The sql of getting config information according to the time period. + */ + String findChangeConfigFetchRows(Map params, final Timestamp startTime, final Timestamp endTime, + int startRow, int pageSize, long lastMaxId); + + /** + * list group key md5 by page. + * The default sql: + * SELECT t.id,data_id,group_id,tenant_id,app_name,md5,type,gmt_modified,encrypted_data_key FROM ( + * SELECT id FROM config_info ORDER BY id LIMIT ?,? ) g, config_info t + * WHERE g.id = t.id + * + * @return The sql of listing group key md5 by page. + */ + String listGroupKeyMd5ByPageFetchRows(); + + /** + * query all configuration information according to group, appName, tenant (for export). + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content,type,md5,gmt_create,gmt_modified, + * src_user,src_ip,c_desc,c_use,effect,c_schema,encrypted_data_key + * FROM config_info WHERE ... + * + * @param ids ids + * @param params The map of params, the key is the parameter name(dataId, group, appName), + * the value is the key's value. + * @return Collection of ConfigInfo objects + */ + String findAllConfigInfo4Export(List ids, Map params); + + /** + * Use select in to realize batch query of db records; subQueryLimit specifies the number of conditions in in, with + * an upper limit of 20. + * The default sql: + * SELECT data_id, group_id, tenant_id, app_name, content FROM config_info WHERE group_id = ? AND tenant_id = ? AND data_id IN (...) + * + * @param paramSize The size of ids. + * @return The sql to get config information by batch. + */ + String findConfigInfoByBatch(int paramSize); + + /** + * Get the count of config information. + * The default sql: + * SELECT count(*) FROM config_info WHERE ... + * + * @param params The map of params, the key is the parameter name(dataId, groupId, tenant_id, appName, content), + * the value is the key's value. + * @return The sql of getting the count of config information. + */ + String findConfigInfoLikeCountRows(Map params); + + /** + * Get the config information. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE + * + * @param params The map of params, the key is the parameter name(dataId, groupId, tenant_id, appName, content), + * the value is the key's value. + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of getting the config information. + */ + String findConfigInfoLikeFetchRows(Map params, int startRow, int pageSize); + + /** + * Get the count of config information. + * The default sql: + * SELECT count(*) FROM config_info WHERE ... + * + * @param params The map of params, the key is the parameter name(dataId, groupId, tenant_id, content), + * the value is the arbitrary object. + * @return The sql of getting the count of config information. + */ + String findConfigInfoBaseLikeCountRows(Map params); + + /** + * Get the config information. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,content FROM config_info WHERE ... + * + * @param params The map of params, the key is the parameter name(dataId, groupId, tenant_id, content), + * the value is the key's value. + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of getting the config information. + */ + String findConfigInfoBaseLikeFetchRows(Map params, int startRow, int pageSize); + + /** + * To get number of config information by dataId and tenantId. + * The default sql: + * SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=? + * + * @return The sql of query number. + */ + String findConfigInfoByDataIdCountRows(); + + /** + * find the count of data_id AND tenant_id. + * The default sql: + * SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=? ... + * + * @param params The map of appName/ + * @return The sql of finding the count of data_id and tenant_id. + */ + String findConfigInfoByDataIdAndAdvanceCountRows(Map params); + + /** + * find config info. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND tenant_id=? ... + * + * @param params The map of app name. + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of finding config info. + */ + String findConfigInfoByDataIdAndAdvanceFetchRows(Map params, int startRow, int pageSize); + + /** + * find the count of config info. + * The default sql: + * SELECT count(*) FROM config_info ... + * + * @param params The mpa of dataId, groupId and appName. + * @return The count of config info. + */ + String findConfigInfo4PageCountRows(Map params); + + /** + * find config info. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content,type,encrypted_data_key FROM config_info ... + * + * @param params The mpa of dataId, groupId and appName. + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of finding config info. + */ + String findConfigInfo4PageFetchRows(Map params, int startRow, int pageSize); + + /** + * The count of querying configuration information based on dataId. + * The default sql: + * SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=? + * + * @return The sql of query count. + */ + String findConfigInfoBaseByDataIdCountRows(); + + /** + * Query configuration information based on dataId. + * The default sql: + * SELECT id,data_id,group_id,content FROM config_info WHERE data_id=? AND tenant_id=? + * + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of query configuration information based on dataId. + */ + String findConfigInfoBaseByDataIdFetchRows(int startRow, int pageSize); + + /** + * Get the count of querying configuration information based on group. + * The default sql: + * SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=? + * + * @return The count of config info by groupId and tenantId. + */ + String findConfigInfoByGroupCountRows(); + + /** + * Get the config info by groupId and tenantId. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE group_id=? AND tenant_id=? + * + * @param startRow The start index. + * @param pageSize The size of page. + * @return Get the config info by groupId and tenantId. + */ + String findConfigInfoByGroupFetchRows(int startRow, int pageSize); + + /** + * The count of querying configuration information based on group. + * The default sql: + * SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=? AND app_name =? + * + * @return The sql of the count of querying configuration information based on group. + */ + String findConfigInfoByGroupAndAppCountRows(); + + /** + * Query configuration information based on group. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE group_id=? AND tenant_id=? AND app_name =? + * + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of querying configuration information based on group. + */ + String findConfigInfoByGroupAndAppFetchRows(int startRow, int pageSize); + + /** + * Query configuration information count. + * The default sql: + * SELECT count(*) FROM config_info WHERE tenant_id LIKE ? ... + * + * @param params The map of appName. + * @return Query configuration information count. + */ + String findConfigInfoByAdvanceCountRows(Map params); + + /** + * Query configuration information. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE tenant_id LIKE ? ... + * + * @param startRow The start index. + * @param pageSize The size of page. + * @param params The map of appName. + * @return Query configuration information. + */ + String findConfigInfoByAdvanceFetchRows(Map params, int startRow, int pageSize); + + /** + * Query configuration information count based on group. + * The default sql: + * SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=? + * + * @return Query configuration information count based on group. + */ + String findConfigInfoBaseByGroupCountRows(); + + /** + * Query configuration information based on group. + * The default sql: + * SELECT id,data_id,group_id,content FROM config_info WHERE group_id=? AND tenant_id=? + * + * @param startRow The start index. + * @param pageSize The size of page. + * @return Query configuration information based on group. + */ + String findConfigInfoBaseByGroupFetchRows(int startRow, int pageSize); + + /** + * Query config info count. + * The default sql: + * SELECT count(*) FROM config_info ... + * + * @param params The map of dataId, group, appName, content + * @return The sql of querying config info count + */ + String findConfigInfoLike4PageCountRows(Map params); + + /** + * Query config info. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content,encrypted_data_key FROM config_info ... + * + * @param params The map of dataId, group, appName, content + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of querying config info + */ + String findConfigInfoLike4PageFetchRows(Map params, int startRow, int pageSize); + + /** + * Query all configuration information by page. + * The default sql: + * SELECT t.id,data_id,group_id,tenant_id,app_name,content,md5 " + * + " FROM ( SELECT id FROM config_info WHERE tenant_id LIKE ? ORDER BY id LIMIT ?,? )" + * + " g, config_info t WHERE g.id = t.id + * + * @param startRow The start index. + * @param pageSize The size of page. + * @return Query all configuration information by page. + */ + String findAllConfigInfoFetchRows(int startRow, int pageSize); + + /** + * Query configuration information based on dataId and group. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=? ... + * + * @param params The map of appName. + * @return The sql of querying configuration information based on dataId and group. + */ + String findConfigInfoAdvanceInfo(Map params); + + /** + * Query configuration information count. + * The default sql: + * SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=? ... + * + * @param params The map of appName. + * @return The sql of querying configuration information count + */ + String findConfigInfoByGroupAndAdvanceCountRows(Map params); + + /** + * Query configuration information. + * The default sql: + * SELECT id,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE group_id=? AND tenant_id=? ... + * + * @param params The map of appName. + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of querying configuration information + */ + String findConfigInfoByGroupAndAdvanceFetchRows(Map params, int startRow, int pageSize); + + /** + * find ConfigInfo by ids. + * The default sql: + * SELECT ID,data_id,group_id,tenant_id,app_name,content,md5 FROM config_info WHERE id IN (...) + * + * @param idSize the size of ids. + * @return find ConfigInfo by ids. + */ + String findConfigInfosByIds(int idSize); + + /** + * Remove configuration; database atomic operation, minimum SQL action, no business encapsulation. + * + * @param size The size of ids. + * @return The sql of removing configuration. + */ + String removeConfigInfoByIdsAtomic(int size); +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigInfoTagMapper.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigInfoTagMapper.java new file mode 100644 index 000000000..2f487f298 --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigInfoTagMapper.java @@ -0,0 +1,67 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.mapper; + +/** + * The config tag info mapper. + * + * @author hyx + **/ + +public interface ConfigInfoTagMapper extends Mapper { + + /** + * Update tag configuration information. + * The default sql: + * UPDATE config_info_tag SET content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?,app_name=? WHERE + * data_id=? AND group_id=? AND tenant_id=? AND tag_id=? AND (md5=? or md5 is null or md5='') + * + * @return The sql of updating tag configuration information. + */ + String updateConfigInfo4TagCas(); + + /** + * Returns the number of beta configuration items. + * The default sql: + * SELECT count(ID) FROM config_info_tag + * + * @return The sql of querying the number of beta configuration items. + */ + String configInfoTagCount(); + + /** + * The count of config_info table sql. + * The default sql: + * SELECT count(*) FROM config_info_tag + * + * @return The sql of the count of config_info table. + */ + String count(); + + /** + * Query all tag config info for dump task. + * The default sql: + * SELECT t.id,data_id,group_id,tenant_id,tag_id,app_name,content,md5,gmt_modified + * FROM ( SELECT id FROM config_info_tag ORDER BY id LIMIT ?,? ) g, + * config_info_tag t WHERE g.id = t.id + * + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of querying all tag config info for dump task. + */ + String findAllConfigInfoTagForDumpAllFetchRows(int startRow, int pageSize); +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigTagsRelationMapper.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigTagsRelationMapper.java new file mode 100644 index 000000000..36b15ad93 --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/ConfigTagsRelationMapper.java @@ -0,0 +1,202 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.mapper; + +import java.util.Map; + +/** + * The config with tags mapper. + * + * @author hyx + **/ + +public interface ConfigTagsRelationMapper extends Mapper { + + + /** + * Get the count of relations. + * The default sql: + * SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.tenant_id=? ... + * + * @param params The map of dataId and tenantId. + * @param tagSize the tags name size. + * @return The sql of getting the count of relations. + */ + String findConfigInfoByDataIdAndAdvanceCountRows(Map params, int tagSize); + + /** + * Find config info. + * The default sql: + * SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN + * config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.tenant_id AND b.tag_name IN (...) ... + * + * @param params The map of appName. + * @param tagSize the tags name size. + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of finding config info. + */ + String findConfigInfoByDataIdAndAdvanceFetchRows(Map params, int tagSize, int startRow, int pageSize); + + /** + * Get the count of config info. + * The default sql: + * SELECT count(*) FROM config_info WHERE ... + * + * @param params The map of params, the key is the parameter name(dataId, groupId, tenantId, appName, startTime, endTime, content), + * the value is the key's value. + * @param tagSize the tags name size. + * @return The sql of get config info. + */ + String findConfigInfo4PageCountRows(final Map params, int tagSize); + + /** + * Find config info. + * The default sql: + * SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN + * config_tags_relation b ON a.id=b.i ... + * + * @param params The keys and values are dataId and group. + * @param tagSize the tags name size. + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of finding config info. + */ + String findConfigInfo4PageFetchRows(final Map params, int tagSize, int startRow, int pageSize); + + /** + * Get the count of config information by group id and tenant id and tag name. + * The default sql: + * SELECT count(*) FROM config_info WHERE group_id=? AND tenant_id=? AND b.tag_name IN (...) + * + * @param params The keys and values are dataId and group. + * @param tagSize the tags name size. + * @return The sql of querying configuration information. + */ + String findConfigInfoByGroupAndAdvanceCountRows(final Map params, int tagSize); + + /** + * Query configuration information. + * The default sql: + * SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN " + * config_tags_relation b ON a.id=b.id WHERE a.tenant_id=? AND b.tag_name IN (...) ... + * + * @param params the keys and values are dataId and group. + * @param tagSize the tags name size. + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of querying configuration information. + */ + String findConfigInfoByGroupAndAdvanceFetchRows(final Map params, int tagSize, int startRow, int pageSize); + + /** + * Get the count of config information by config tags relation. + * The default sql: + * SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id + * + * @param params the keys and values are dataId and group. + * @param tagSize the tags name size. + * @return The sql of getting the count of config information. + */ + String findConfigInfoLike4PageCountRows(final Map params, int tagSize); + + /** + * Query config info. + * The default sql: + * SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content + * FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id + * + * @param params the keys and values are dataId and group. + * @param tagSize the tags name size. + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of querying config info. + */ + String findConfigInfoLike4PageFetchRows(final Map params, int tagSize, int startRow, int pageSize); + + /** + * Add configuration; database atomic operation, minimum sql action, no business encapsulation. + * The default sql: + * INSERT INTO config_tags_relation(id,tag_name,tag_type,data_id,group_id,tenant_id) VALUES(?,?,?,?,?,?) + * + * @return The sql of adding configuration. + */ + String addConfigTagRelationAtomic(); + + /** + * Delete tag. + * The default sql: + * DELETE FROM config_tags_relation WHERE id=? + * + * @return The sql of deleting tag. + */ + String removeTagByIdAtomic(); + + /** + * Query config tag list. + * The default sql: + * SELECT tag_name FROM config_tags_relation WHERE tenant_id = ? + * + * @return The sql of querying config tag list + */ + String getConfigTagsByTenant(); + + /** + * Query tag list. + * The default sql: + * SELECT tag_name FROM config_tags_relation WHERE data_id=? AND group_id=? AND tenant_id = ? + * + * @return The sql of querying tag list + */ + String selectTagByConfig(); + + /** + * The number of config. + * The default sql: + * SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.tenant_id=? + * + * @param params The map of appName. + * @param tagSize The size of tags. + * @return The number of config. + */ + String findConfigInfoByAdvanceCountRows(Map params, int tagSize); + + /** + * Query configuration information. + * The default sql: + * SELECT count(*) FROM config_info a LEFT JOIN config_tags_relation b ON a.id=b.id WHERE a.tenant_id=? + * + * @param params The map of appName. + * @param tagSize The size of tags. + * @param startRow The start index. + * @param pageSize The size of page. + * @return The sql of querying configuration information. + */ + String findConfigInfoByAdvanceFetchRows(Map params, int tagSize, int startRow, int pageSize); + + /** + * The default sql: + * SELECT a.id,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content FROM config_info a LEFT JOIN + * config_tags_relation b ON a.id=b.id WHERE a.data_id=? AND a.group_id=? AND a.tenant_id=? + * AND b.tag_name IN (...) ...; + * + * @param params The map of appName. + * @param tagSize the tags name size. + * @return * Query configuration information based on dataId and group. + */ + String findConfigInfoAdvanceInfo(Map params, int tagSize); +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/HistoryConfigInfoMapper.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/HistoryConfigInfoMapper.java new file mode 100644 index 000000000..b69de91da --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/HistoryConfigInfoMapper.java @@ -0,0 +1,82 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.mapper; + +/** + * The history config info mapper. + * + * @author hyx + **/ + +public interface HistoryConfigInfoMapper extends Mapper { + + /** + * Delete data before startTime. + * The default sql: + * DELETE FROM his_config_info WHERE gmt_modified < ? LIMIT ? + * + * @return The sql of deleting data before startTime. + */ + String removeConfigHistory(); + + /** + * Get the number of configurations before the specified time. + * The default sql: + * SELECT count(*) FROM his_config_info WHERE gmt_modified < ? + * + * @return The sql of getting the number of configurations before the specified time. + */ + String findConfigHistoryCountByTime(); + + /** + * Query deleted config. + * The default sql: + * SELECT DISTINCT data_id, group_id, tenant_id FROM his_config_info WHERE op_type = 'D' AND gmt_modified >=? AND gmt_modified <= ? + * + * @return The sql of querying deleted config. + */ + String findDeletedConfig(); + + /** + * Query the numbers of history config information by data_id, group_id AND tenant_id. + * The default sql: + * SELECT count(*) FROM his_config_info WHERE data_id = ? AND group_id = ? AND tenant_id = ? + * + * @return The sql of querying the numbers of history config information by data_id, group_id AND tenant_id. + */ + String findConfigHistoryCountRows(); + + /** + * List configuration history change record. + * The default sql: + * SELECT nid,data_id,group_id,tenant_id,app_name,src_ip,src_user,op_type,gmt_create,gmt_modified FROM his_config_info + * WHERE data_id = ? AND group_id = ? AND tenant_id = ? ORDER BY nid DESC + * + * @return The sql of listing configuration history change record. + */ + String findConfigHistoryFetchRows(); + + /** + * Get previous config detail. + * The default sql: + * SELECT nid,data_id,group_id,tenant_id,app_name,content,md5,src_user,src_ip,op_type,gmt_create,gmt_modified + * FROM his_config_info WHERE nid = (SELECT max(nid) FROM his_config_info WHERE id = ?) + * + * @return The sql of getting previous config detail. + */ + String detailPreviousConfigHistory(); +} diff --git a/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/Mapper.java b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/Mapper.java new file mode 100644 index 000000000..db43af0e8 --- /dev/null +++ b/plugin/datasource/src/main/java/com/alibaba/nacos/plugin/datasource/mapper/Mapper.java @@ -0,0 +1,70 @@ +/* + * Copyright 1999-2022 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.nacos.plugin.datasource.mapper; + +import java.util.List; + +/** + * The parent class of the all mappers. + * + * @author hyx + **/ + +public interface Mapper { + + /** + * The select method contains columns and where params. + * @param columns The columns + * @param where The where params + * @return The sql of select + */ + String select(List columns, List where); + + /** + * The insert method contains columns. + * @param columns The columns + * @return The sql of insert + */ + String insert(List columns); + + /** + * The update method contains columns and where params. + * @param columns The columns + * @param where The where params + * @return The sql of update + */ + String update(List columns, List where); + + /** + * The delete method contains. + * @param params The params + * @return The sql of delete + */ + String delete(List params); + + /** + * Get the name of table. + * @return The name of table. + */ + String getTableName(); + + /** + * Get the datasource name. + * @return The name of datasource. + */ + String getDataSource(); +} \ No newline at end of file diff --git a/plugin/datasource/src/main/resources/META-INF/services/com.alibaba.nacos.plugin.datasource.mapper.Mapper b/plugin/datasource/src/main/resources/META-INF/services/com.alibaba.nacos.plugin.datasource.mapper.Mapper new file mode 100644 index 000000000..8db94fc03 --- /dev/null +++ b/plugin/datasource/src/main/resources/META-INF/services/com.alibaba.nacos.plugin.datasource.mapper.Mapper @@ -0,0 +1,29 @@ +# +# Copyright 1999-2022 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. +# + +com.alibaba.nacos.plugin.datasource.impl.mysql.ConfigInfoAggrMapperByMySql +com.alibaba.nacos.plugin.datasource.impl.mysql.ConfigInfoBetaMapperByMySql +com.alibaba.nacos.plugin.datasource.impl.mysql.ConfigInfoMapperByMySql +com.alibaba.nacos.plugin.datasource.impl.mysql.ConfigInfoTagMapperByMySql +com.alibaba.nacos.plugin.datasource.impl.mysql.ConfigTagsRelationMapperByMySql +com.alibaba.nacos.plugin.datasource.impl.mysql.HistoryConfigInfoMapperByMySql + +com.alibaba.nacos.plugin.datasource.impl.derby.ConfigInfoAggrMapperByDerby +com.alibaba.nacos.plugin.datasource.impl.derby.ConfigInfoBetaMapperByDerby +com.alibaba.nacos.plugin.datasource.impl.derby.ConfigInfoMapperByDerby +com.alibaba.nacos.plugin.datasource.impl.derby.ConfigInfoTagMapperByDerby +com.alibaba.nacos.plugin.datasource.impl.derby.ConfigInfoTagsRelationMapperByDerby +com.alibaba.nacos.plugin.datasource.impl.derby.HistoryConfigInfoMapperByDerby \ No newline at end of file diff --git a/plugin/encryption/src/main/java/com/alibaba/nacos/plugin/encryption/handler/EncryptionHandler.java b/plugin/encryption/src/main/java/com/alibaba/nacos/plugin/encryption/handler/EncryptionHandler.java index fa3f68748..2d8d00774 100644 --- a/plugin/encryption/src/main/java/com/alibaba/nacos/plugin/encryption/handler/EncryptionHandler.java +++ b/plugin/encryption/src/main/java/com/alibaba/nacos/plugin/encryption/handler/EncryptionHandler.java @@ -23,7 +23,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Optional; -import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -51,9 +50,9 @@ public class EncryptionHandler { if (!checkCipher(dataId)) { return Pair.with("", content); } - String algorithmName = parseAlgorithmName(dataId); - Optional optional = EncryptionPluginManager.instance() - .findEncryptionService(algorithmName); + Optional algorithmName = parseAlgorithmName(dataId); + Optional optional = algorithmName + .flatMap(EncryptionPluginManager.instance()::findEncryptionService); if (!optional.isPresent()) { LOGGER.warn("[EncryptionHandler] [encryptHandler] No encryption program with the corresponding name found"); return Pair.with("", content); @@ -76,9 +75,9 @@ public class EncryptionHandler { if (!checkCipher(dataId)) { return Pair.with("", content); } - String algorithmName = parseAlgorithmName(dataId); - Optional optional = EncryptionPluginManager.instance() - .findEncryptionService(algorithmName); + Optional algorithmName = parseAlgorithmName(dataId); + Optional optional = algorithmName + .flatMap(EncryptionPluginManager.instance()::findEncryptionService); if (!optional.isPresent()) { LOGGER.warn("[EncryptionHandler] [decryptHandler] No encryption program with the corresponding name found"); return Pair.with("", content); @@ -95,17 +94,17 @@ public class EncryptionHandler { * @param dataId dataId * @return algorithm name */ - private static String parseAlgorithmName(String dataId) { - return Stream.of(dataId.split("-")).collect(Collectors.toList()).get(1); + private static Optional parseAlgorithmName(String dataId) { + return Stream.of(dataId.split("-")).skip(1).findFirst(); } /** * Check if encryption and decryption is needed. * * @param dataId dataId - * @return boolean + * @return boolean whether data id needs encrypt */ private static boolean checkCipher(String dataId) { - return dataId.startsWith(PREFIX); + return dataId.startsWith(PREFIX) && !PREFIX.equals(dataId); } } diff --git a/plugin/encryption/src/test/java/com/alibaba/nacos/plugin/encryption/handler/EncryptionHandlerTest.java b/plugin/encryption/src/test/java/com/alibaba/nacos/plugin/encryption/handler/EncryptionHandlerTest.java index e3c8867fa..8eca559f3 100644 --- a/plugin/encryption/src/test/java/com/alibaba/nacos/plugin/encryption/handler/EncryptionHandlerTest.java +++ b/plugin/encryption/src/test/java/com/alibaba/nacos/plugin/encryption/handler/EncryptionHandlerTest.java @@ -30,17 +30,19 @@ import org.junit.Test; */ public class EncryptionHandlerTest { + private EncryptionPluginService mockEncryptionPluginService; + @Before public void setUp() { - EncryptionPluginManager.join(new EncryptionPluginService() { + mockEncryptionPluginService = new EncryptionPluginService() { @Override public String encrypt(String secretKey, String content) { - return content; + return secretKey + content; } @Override public String decrypt(String secretKey, String content) { - return content; + return content.replaceFirst(secretKey, ""); } @Override @@ -50,19 +52,20 @@ public class EncryptionHandlerTest { @Override public String algorithmName() { - return "aes"; + return "mockAlgo"; } - + @Override public String encryptSecretKey(String secretKey) { - return secretKey; + return secretKey + secretKey; } - + @Override public String decryptSecretKey(String secretKey) { - return secretKey; + return generateSecretKey(); } - }); + }; + EncryptionPluginManager.join(mockEncryptionPluginService); } @Test @@ -76,4 +79,55 @@ public class EncryptionHandlerTest { Pair pair = EncryptionHandler.decryptHandler("test-dataId", "12345678", "content"); Assert.assertNotNull(pair); } + + @Test + public void testCornerCaseDataIdAlgoParse() { + String dataId = "cipher-"; + Pair pair = EncryptionHandler.encryptHandler(dataId, "content"); + Assert.assertNotNull("should not throw exception when parsing enc algo for dataId '" + dataId + "'", pair); + } + + @Test + public void testUnknownAlgorithmNameEnc() { + String dataId = "cipher-mySM4-application"; + String content = "content"; + Pair pair = EncryptionHandler.encryptHandler(dataId, content); + Assert.assertNotNull(pair); + Assert.assertEquals("should return original content if algorithm is not defined.", content, pair.getSecond()); + } + + @Test + public void testUnknownAlgorithmNameDecrypt() { + String dataId = "cipher-mySM4-application"; + String content = "content"; + Pair pair = EncryptionHandler.decryptHandler(dataId, "", content); + Assert.assertNotNull(pair); + Assert.assertEquals("should return original content if algorithm is not defined.", content, pair.getSecond()); + } + + @Test + public void testEncrypt() { + String dataId = "cipher-mockAlgo-application"; + String content = "content"; + String sec = mockEncryptionPluginService.generateSecretKey(); + Pair pair = EncryptionHandler.encryptHandler(dataId, content); + Assert.assertNotNull(pair); + Assert.assertEquals("should return encrypted content.", + mockEncryptionPluginService.encrypt(sec, content), pair.getSecond()); + Assert.assertEquals("should return encrypted secret key.", + mockEncryptionPluginService.encryptSecretKey(sec), pair.getFirst()); + } + + @Test + public void testDecrypt() { + String dataId = "cipher-mockAlgo-application"; + String oContent = "content"; + String oSec = mockEncryptionPluginService.generateSecretKey(); + String content = mockEncryptionPluginService.encrypt(oSec, oContent); + String sec = mockEncryptionPluginService.encryptSecretKey(oSec); + Pair pair = EncryptionHandler.decryptHandler(dataId, sec, content); + Assert.assertNotNull(pair); + Assert.assertEquals("should return original content.", oContent, pair.getSecond()); + Assert.assertEquals("should return original secret key.", oSec, pair.getFirst()); + } } \ No newline at end of file diff --git a/plugin/pom.xml b/plugin/pom.xml index c09db8f57..159d5040a 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -32,6 +32,8 @@ auth encryption + trace + datasource pom diff --git a/plugin/trace/pom.xml b/plugin/trace/pom.xml new file mode 100644 index 000000000..d21a47d28 --- /dev/null +++ b/plugin/trace/pom.xml @@ -0,0 +1,39 @@ + + + + + + nacos-plugin + com.alibaba.nacos + ${revision} + + 4.0.0 + + nacos-trace-plugin + nacos-trace-plugin ${project.version} + http://nacos.io + + + + com.alibaba.nacos + nacos-common + provided + + + diff --git a/plugin/trace/src/main/java/com/alibaba/nacos/plugin/trace/NacosTracePluginManager.java b/plugin/trace/src/main/java/com/alibaba/nacos/plugin/trace/NacosTracePluginManager.java new file mode 100644 index 000000000..558a22197 --- /dev/null +++ b/plugin/trace/src/main/java/com/alibaba/nacos/plugin/trace/NacosTracePluginManager.java @@ -0,0 +1,59 @@ +/* + * Copyright 1999-2021 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.nacos.plugin.trace; + +import com.alibaba.nacos.common.spi.NacosServiceLoader; +import com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Nacos trace event subscriber manager. + * + * @author xiweng.yy + */ +public class NacosTracePluginManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(NacosTracePluginManager.class); + + private static final NacosTracePluginManager INSTANCE = new NacosTracePluginManager(); + + private final Map traceSubscribers; + + private NacosTracePluginManager() { + this.traceSubscribers = new ConcurrentHashMap<>(); + Collection plugins = NacosServiceLoader.load(NacosTraceSubscriber.class); + for (NacosTraceSubscriber each : plugins) { + this.traceSubscribers.put(each.getName(), each); + LOGGER.info("[TracePluginManager] Load NacosTraceSubscriber({}) name({}) successfully.", each.getClass(), + each.getName()); + } + } + + public static NacosTracePluginManager getInstance() { + return INSTANCE; + } + + public Collection getAllTraceSubscribers() { + return new HashSet<>(traceSubscribers.values()); + } +} diff --git a/plugin/trace/src/main/java/com/alibaba/nacos/plugin/trace/spi/NacosTraceSubscriber.java b/plugin/trace/src/main/java/com/alibaba/nacos/plugin/trace/spi/NacosTraceSubscriber.java new file mode 100644 index 000000000..c8ec7a380 --- /dev/null +++ b/plugin/trace/src/main/java/com/alibaba/nacos/plugin/trace/spi/NacosTraceSubscriber.java @@ -0,0 +1,60 @@ +/* + * Copyright 1999-2021 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.nacos.plugin.trace.spi; + +import com.alibaba.nacos.common.trace.event.TraceEvent; + +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Nacos trace event subscriber. + * + * @author xiweng.yy + */ +public interface NacosTraceSubscriber { + + /** + * Get the plugin name, if the same name has loaded by nacos, the older one will be replaced by new one. + * + * @return plugin name + */ + String getName(); + + /** + * Event callback. + * + * @param event {@link TraceEvent} + */ + void onEvent(TraceEvent event); + + /** + * Returns which trace events are this subscriber interested in. + * + * @return The interested event types. + */ + List> subscribeTypes(); + + /** + * It is up to the listener to determine whether the callback is asynchronous or synchronous. + * + * @return {@link Executor} + */ + default Executor executor() { + return null; + } +} diff --git a/plugin/trace/src/test/java/com/alibaba/nacos/plugin/trace/NacosTracePluginManagerTest.java b/plugin/trace/src/test/java/com/alibaba/nacos/plugin/trace/NacosTracePluginManagerTest.java new file mode 100644 index 000000000..7df422568 --- /dev/null +++ b/plugin/trace/src/test/java/com/alibaba/nacos/plugin/trace/NacosTracePluginManagerTest.java @@ -0,0 +1,47 @@ +/* + * Copyright 1999-2021 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.nacos.plugin.trace; + +import com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; + +public class NacosTracePluginManagerTest { + + @BeforeClass + public static void setUp() { + NacosTracePluginManager.getInstance(); + } + + @Test + public void testGetAllTraceSubscribers() { + assertFalse(NacosTracePluginManager.getInstance().getAllTraceSubscribers().isEmpty()); + assertContainsTestPlugin(); + } + + private void assertContainsTestPlugin() { + for (NacosTraceSubscriber each : NacosTracePluginManager.getInstance().getAllTraceSubscribers()) { + if ("trace-plugin-mock".equals(each.getName())) { + return; + } + } + Assert.fail("No found plugin named 'trace-plugin-mock'"); + } +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/upgrade/UpgradeStatesTest.java b/plugin/trace/src/test/java/com/alibaba/nacos/plugin/trace/mock/MockNacosTraceSubscriber.java similarity index 54% rename from naming/src/test/java/com/alibaba/nacos/naming/core/v2/upgrade/UpgradeStatesTest.java rename to plugin/trace/src/test/java/com/alibaba/nacos/plugin/trace/mock/MockNacosTraceSubscriber.java index b3667e911..4816b847d 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/upgrade/UpgradeStatesTest.java +++ b/plugin/trace/src/test/java/com/alibaba/nacos/plugin/trace/mock/MockNacosTraceSubscriber.java @@ -14,26 +14,26 @@ * limitations under the License. */ -package com.alibaba.nacos.naming.core.v2.upgrade; +package com.alibaba.nacos.plugin.trace.mock; -import org.junit.Assert; -import org.junit.Test; +import com.alibaba.nacos.common.trace.event.TraceEvent; +import com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber; -/** - * {@link UpgradeStates} unit tests. - * - * @author chenglu - * @date 2021-09-01 22:04 - */ -public class UpgradeStatesTest { +import java.util.List; + +public class MockNacosTraceSubscriber implements NacosTraceSubscriber { - private UpgradeStates upgradeStates = new UpgradeStates(); + @Override + public String getName() { + return "trace-plugin-mock"; + } - @Test - public void testOnEvent() { - UpgradeStates.UpgradeStateChangedEvent changedEvent = new UpgradeStates.UpgradeStateChangedEvent(true); - upgradeStates.onEvent(changedEvent); + @Override + public void onEvent(TraceEvent event) { + } - Assert.assertTrue(upgradeStates.isUpgraded()); + @Override + public List> subscribeTypes() { + return null; } } diff --git a/naming/src/test/resources/META-INF/services/com.alibaba.nacos.naming.core.v2.upgrade.SelfUpgradeChecker b/plugin/trace/src/test/resources/META-INF/services/com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber similarity index 82% rename from naming/src/test/resources/META-INF/services/com.alibaba.nacos.naming.core.v2.upgrade.SelfUpgradeChecker rename to plugin/trace/src/test/resources/META-INF/services/com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber index 468f5031b..a50700753 100644 --- a/naming/src/test/resources/META-INF/services/com.alibaba.nacos.naming.core.v2.upgrade.SelfUpgradeChecker +++ b/plugin/trace/src/test/resources/META-INF/services/com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber @@ -1,10 +1,10 @@ # -# Copyright 1999-2020 Alibaba Group Holding Ltd. +# Copyright 1999-2021 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 -# +#r # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software @@ -14,4 +14,4 @@ # limitations under the License. # -com.alibaba.nacos.naming.core.v2.upgrade.MockSelfUpgradeChecker +com.alibaba.nacos.plugin.trace.mock.MockNacosTraceSubscriber diff --git a/pom.xml b/pom.xml index cbe977756..ca9ddb45a 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ - 2.1.1 + 2.2.0-SNAPSHOT UTF-8 UTF-8 @@ -145,7 +145,7 @@ 1.2 1.30.2 1.17.0 - 3.16.1 + 3.16.3 ${grpc-java.version} 4.0.63 1.10.19 @@ -153,6 +153,7 @@ 3.4.2 1.3.8 1.3.8 + 1.32 @@ -713,6 +714,16 @@ nacos-encryption-plugin ${project.version} + + com.alibaba.nacos + nacos-trace-plugin + ${project.version} + + + com.alibaba.nacos + nacos-datasource-plugin + ${project.version} + ${project.groupId} nacos-auth @@ -975,6 +986,12 @@ 2.9.0 compile + + + org.yaml + snakeyaml + ${SnakeYaml.version} + diff --git a/sys/src/main/java/com/alibaba/nacos/sys/env/EnvUtil.java b/sys/src/main/java/com/alibaba/nacos/sys/env/EnvUtil.java index 74ffd9113..3ab087aaa 100644 --- a/sys/src/main/java/com/alibaba/nacos/sys/env/EnvUtil.java +++ b/sys/src/main/java/com/alibaba/nacos/sys/env/EnvUtil.java @@ -17,7 +17,6 @@ package com.alibaba.nacos.sys.env; import com.alibaba.nacos.common.JustForTest; -import com.alibaba.nacos.common.utils.ConvertUtils; import com.alibaba.nacos.common.utils.IoUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.ThreadUtils; @@ -86,7 +85,7 @@ public class EnvUtil { private static final String CUSTOM_CONFIG_LOCATION_PROPERTY = "spring.config.additional-location"; - private static final String DEFAULT_CONFIG_LOCATION = "application.properties"; + private static final String DEFAULT_CONFIG_LOCATION = "application.properties"; private static final String DEFAULT_RESOURCE_PATH = "/application.properties"; @@ -208,17 +207,6 @@ public class EnvUtil { EnvUtil.isStandalone = isStandalone; } - /** - * Whether open upgrade from 1.X nacos server. Might effect `doubleWrite` and `Old raft`. - * - * @since 2.1.0 - * @return {@code true} open upgrade feature, otherwise {@code false}, default {@code false} - * @deprecated 2.2.0 - */ - public static boolean isSupportUpgradeFrom1X() { - return ConvertUtils.toBoolean(getProperty(Constants.SUPPORT_UPGRADE_FROM_1X), false); - } - /** * Standalone mode or not. */ @@ -252,7 +240,8 @@ public class EnvUtil { if (StringUtils.isBlank(nacosHomePath)) { String nacosHome = System.getProperty(NACOS_HOME_KEY); if (StringUtils.isBlank(nacosHome)) { - nacosHome = Paths.get(System.getProperty(NACOS_HOME_PROPERTY), NACOS_HOME_ADDITIONAL_FILEPATH).toString(); + nacosHome = Paths.get(System.getProperty(NACOS_HOME_PROPERTY), NACOS_HOME_ADDITIONAL_FILEPATH) + .toString(); } return nacosHome; } @@ -281,13 +270,14 @@ public class EnvUtil { public static float getLoad() { return (float) OperatingSystemBeanManager.getOperatingSystemBean().getSystemLoadAverage(); } - + public static float getCpu() { return (float) OperatingSystemBeanManager.getSystemCpuUsage(); } public static float getMem() { - return (float) (1 - OperatingSystemBeanManager.getFreePhysicalMem() / OperatingSystemBeanManager.getTotalPhysicalMem()); + return (float) (1 - OperatingSystemBeanManager.getFreePhysicalMem() / OperatingSystemBeanManager + .getTotalPhysicalMem()); } public static String getConfPath() { @@ -424,8 +414,8 @@ public class EnvUtil { * Get available processor numbers from environment. * *

- * If there are setting of {@code nacos.core.sys.basic.processors} in config/JVM/system, use it. - * If no setting, use the one time {@code ThreadUtils.getSuitableThreadCount()}. + * If there are setting of {@code nacos.core.sys.basic.processors} in config/JVM/system, use it. If no setting, use + * the one time {@code ThreadUtils.getSuitableThreadCount()}. *

* * @return available processor numbers from environment, will not lower than 1. @@ -460,8 +450,9 @@ public class EnvUtil { if (scale < 0 || scale > 1) { throw new IllegalArgumentException("processors scale must between 0 and 1"); } - double result = getProperty(Constants.AVAILABLE_PROCESSORS_BASIC, int.class, - ThreadUtils.getSuitableThreadCount(1)) * scale; + double result = + getProperty(Constants.AVAILABLE_PROCESSORS_BASIC, int.class, ThreadUtils.getSuitableThreadCount(1)) + * scale; return result > 1 ? (int) result : 1; } } diff --git a/sys/src/main/java/com/alibaba/nacos/sys/env/NacosAutoRefreshPropertySourceLoader.java b/sys/src/main/java/com/alibaba/nacos/sys/env/NacosAutoRefreshPropertySourceLoader.java deleted file mode 100644 index b1d34664c..000000000 --- a/sys/src/main/java/com/alibaba/nacos/sys/env/NacosAutoRefreshPropertySourceLoader.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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.nacos.sys.env; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.common.JustForTest; -import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.sys.file.FileChangeEvent; -import com.alibaba.nacos.sys.file.FileWatcher; -import com.alibaba.nacos.sys.file.WatchFileCenter; -import org.springframework.boot.env.OriginTrackedMapPropertySource; -import org.springframework.boot.env.PropertySourceLoader; -import org.springframework.core.env.PropertySource; -import org.springframework.core.io.Resource; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Support for configuring automatic refresh and loading into the Environment. - * - * @author liaochuntao - */ -@Deprecated -public class NacosAutoRefreshPropertySourceLoader implements PropertySourceLoader { - - private final Map properties = new ConcurrentHashMap<>(16); - - private Resource holder = null; - - @Override - public String[] getFileExtensions() { - return new String[] {"properties"}; - } - - @Override - public List> load(String name, Resource resource) throws IOException { - holder = resource; - Map tmp = loadProperties(resource); - properties.putAll(tmp); - - try { - WatchFileCenter.registerWatcher(EnvUtil.getConfPath(), new FileWatcher() { - @Override - public void onChange(FileChangeEvent event) { - try { - Map tmp1 = loadProperties(holder); - properties.putAll(tmp1); - } catch (IOException ignore) { - - } - } - - @Override - public boolean interest(String context) { - return StringUtils.contains(context, "application.properties"); - } - }); - } catch (NacosException ignore) { - - } - - if (properties.isEmpty()) { - return Collections.emptyList(); - } - return Collections.singletonList(new OriginTrackedMapPropertySource("nacos_application_conf", properties)); - } - - private Map loadProperties(Resource resource) throws IOException { - return new OriginTrackedPropertiesLoader(resource).load(); - } - - @JustForTest - protected Map getProperties() { - return properties; - } -} diff --git a/sys/src/main/java/com/alibaba/nacos/sys/env/NacosDefaultPropertySourceEnvironmentPostProcessor.java b/sys/src/main/java/com/alibaba/nacos/sys/env/NacosDefaultPropertySourceEnvironmentPostProcessor.java deleted file mode 100644 index ce9d156ef..000000000 --- a/sys/src/main/java/com/alibaba/nacos/sys/env/NacosDefaultPropertySourceEnvironmentPostProcessor.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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.nacos.sys.env; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.env.EnvironmentPostProcessor; -import org.springframework.core.Ordered; -import org.springframework.core.env.CompositePropertySource; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.Environment; -import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertySource; -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; -import org.springframework.core.io.support.EncodedResource; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.core.io.support.ResourcePatternResolver; -import org.springframework.core.io.support.ResourcePropertySource; - -import java.io.IOException; - -/** - * A lowest precedence {@link EnvironmentPostProcessor} implementation to append Nacos default {@link PropertySource} - * with lowest order in {@link Environment}. - * - * @author Mercy - * @since 0.2.2 - */ -@Deprecated -public class NacosDefaultPropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { - - /** - * The name of Nacos default {@link PropertySource}. - */ - public static final String PROPERTY_SOURCE_NAME = "nacos-default"; - - /** - * The resource location pattern of Nacos default {@link PropertySource}. - * - * @see ResourcePatternResolver#CLASSPATH_ALL_URL_PREFIX - */ - public static final String RESOURCE_LOCATION_PATTERN = - ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "META-INF/nacos-default.properties"; - - private static final String FILE_ENCODING = "UTF-8"; - - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { - - ResourceLoader resourceLoader = getResourceLoader(application); - - processPropertySource(environment, resourceLoader); - - } - - private ResourceLoader getResourceLoader(SpringApplication application) { - - ResourceLoader resourceLoader = application.getResourceLoader(); - - if (resourceLoader == null) { - resourceLoader = new DefaultResourceLoader(application.getClassLoader()); - } - - return resourceLoader; - } - - private void processPropertySource(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { - - try { - PropertySource nacosDefaultPropertySource = buildPropertySource(resourceLoader); - MutablePropertySources propertySources = environment.getPropertySources(); - // append nacosDefaultPropertySource as last one in order to be overrided by higher order - propertySources.addLast(nacosDefaultPropertySource); - } catch (IOException e) { - throw new IllegalStateException(e.getMessage(), e); - } - } - - private PropertySource buildPropertySource(ResourceLoader resourceLoader) throws IOException { - CompositePropertySource propertySource = new CompositePropertySource(PROPERTY_SOURCE_NAME); - appendPropertySource(propertySource, resourceLoader); - return propertySource; - } - - private void appendPropertySource(CompositePropertySource propertySource, ResourceLoader resourceLoader) - throws IOException { - ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(resourceLoader); - Resource[] resources = resourcePatternResolver.getResources(RESOURCE_LOCATION_PATTERN); - for (Resource resource : resources) { - // Add if exists - if (resource.exists()) { - String internalName = String.valueOf(resource.getURL()); - propertySource.addPropertySource( - new ResourcePropertySource(internalName, new EncodedResource(resource, FILE_ENCODING))); - } - } - } - - @Override - public int getOrder() { - return Ordered.LOWEST_PRECEDENCE; - } -} diff --git a/sys/src/main/resources/META-INF/spring.factories b/sys/src/main/resources/META-INF/spring.factories index c5289f497..df7bb0712 100644 --- a/sys/src/main/resources/META-INF/spring.factories +++ b/sys/src/main/resources/META-INF/spring.factories @@ -1,6 +1,3 @@ -# EnvironmentPostProcessor -org.springframework.boot.env.EnvironmentPostProcessor=\ -com.alibaba.nacos.sys.env.NacosDefaultPropertySourceEnvironmentPostProcessor # ApplicationContextInitializer org.springframework.context.ApplicationContextInitializer=\ - com.alibaba.nacos.sys.utils.ApplicationUtils \ No newline at end of file + com.alibaba.nacos.sys.utils.ApplicationUtils diff --git a/sys/src/test/java/com/alibaba/nacos/sys/env/NacosDefaultPropertySourceEnvironmentPostProcessorTest.java b/sys/src/test/java/com/alibaba/nacos/sys/env/NacosDefaultPropertySourceEnvironmentPostProcessorTest.java deleted file mode 100644 index 2223cf864..000000000 --- a/sys/src/test/java/com/alibaba/nacos/sys/env/NacosDefaultPropertySourceEnvironmentPostProcessorTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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.nacos.sys.env; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.core.env.CompositePropertySource; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertySource; -import org.springframework.test.context.junit4.SpringRunner; - -import java.util.HashSet; - -import static com.alibaba.nacos.sys.env.NacosDefaultPropertySourceEnvironmentPostProcessor.PROPERTY_SOURCE_NAME; -import static com.alibaba.nacos.sys.env.NacosDefaultPropertySourceEnvironmentPostProcessor.RESOURCE_LOCATION_PATTERN; -import static java.util.Arrays.asList; - -/** - * {@link NacosDefaultPropertySourceEnvironmentPostProcessor} Test. - * - * @author Mercy - * @since 0.2.2 - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = NacosDefaultPropertySourceEnvironmentPostProcessorTest.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) -public class NacosDefaultPropertySourceEnvironmentPostProcessorTest { - - @Autowired - private ConfigurableEnvironment environment; - - @Test - public void testNacosDefaultPropertySourcePresent() { - MutablePropertySources propertySources = environment.getPropertySources(); - // "nacos-default" must be present - Assert.assertTrue(propertySources.contains("nacos-default")); - // Get PropertySource via PROPERTY_SOURCE_NAME - PropertySource propertySource = getNacosDefaultPropertySource(); - // "nacos-default" must be present - Assert.assertNotNull(propertySource); - // make sure propertySource is last one - Assert.assertEquals(propertySources.size() - 1, propertySources.precedenceOf(propertySource)); - } - - @Test - public void testDefaultProperties() { - - // Web Server - assertPropertyEquals("server.port", "8848"); - assertPropertyEquals("server.tomcat.uri-encoding", "UTF-8"); - - // HTTP Encoding - assertPropertyEquals("spring.http.encoding.force", "true"); - assertPropertyEquals("spring.http.encoding.enabled", "true"); - - // i18n - assertPropertyEquals("spring.messages.encoding", "UTF-8"); - } - - @Test - public void testDefaultPropertyNames() { - - assertPropertyNames("nacos.version", "server.servlet.contextPath", "server.port", "server.tomcat.uri-encoding", - "spring.http.encoding.force", "spring.http.encoding.enabled", "spring.messages.encoding", - "spring.autoconfigure.exclude"); - } - - private void assertPropertyNames(String... propertyNames) { - - CompositePropertySource propertySource = getNacosDefaultPropertySource(); - - Assert.assertEquals("Please Properties from resources[" + RESOURCE_LOCATION_PATTERN + "]", - new HashSet(asList(propertyNames)), - new HashSet(asList(propertySource.getPropertyNames()))); - } - - private void assertPropertyEquals(String propertyName, String expectedValue) { - PropertySource propertySource = getNacosDefaultPropertySource(); - Assert.assertEquals(expectedValue, propertySource.getProperty(propertyName)); - } - - private CompositePropertySource getNacosDefaultPropertySource() { - MutablePropertySources propertySources = environment.getPropertySources(); - // Get PropertySource via PROPERTY_SOURCE_NAME - CompositePropertySource propertySource = (CompositePropertySource) propertySources.get(PROPERTY_SOURCE_NAME); - return propertySource; - } -} diff --git a/test/core-test/src/test/java/com/alibaba/nacos/test/core/auth/AuthBase.java b/test/core-test/src/test/java/com/alibaba/nacos/test/core/auth/AuthBase.java index dcc1294ee..219cacbe5 100644 --- a/test/core-test/src/test/java/com/alibaba/nacos/test/core/auth/AuthBase.java +++ b/test/core-test/src/test/java/com/alibaba/nacos/test/core/auth/AuthBase.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.alibaba.nacos.test.core.auth; import com.alibaba.nacos.api.PropertyKeyConst; @@ -36,42 +37,46 @@ import java.util.concurrent.TimeUnit; * @since 1.2.0 */ public class AuthBase extends HttpClient4Test { - + protected String accessToken; - + protected String username1 = "username1"; + protected String password1 = "password1"; - + protected String username2 = "username2"; + protected String password2 = "password2"; - + protected String username3 = "username3"; + protected String password3 = "password3"; - + protected String role1 = "role1"; + protected String role2 = "role2"; + protected String role3 = "role3"; - + protected Properties properties; - + protected String namespace1 = "namespace1"; - - public String login() { - + + public String login(String username, String password) { ResponseEntity response = request("/nacos/v1/auth/users/login", - Params.newParams() - .appendParam("username", "nacos") - .appendParam("password", "nacos") - .done(), - String.class, - HttpMethod.POST); - + Params.newParams().appendParam("username", username).appendParam("password", password).done(), + String.class, HttpMethod.POST); + Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); JsonNode json = JacksonUtils.toObj(response.getBody()); Assert.assertTrue(json.has("accessToken")); return json.get("accessToken").textValue(); } - + + public String login() { + return login("nacos", "nacos"); + } + protected void init(int port) throws Exception { AuthConfigs.setCachingEnabled(false); TimeUnit.SECONDS.sleep(5L); @@ -79,240 +84,150 @@ public class AuthBase extends HttpClient4Test { System.setProperty("nacos.core.auth.enabled", "true"); this.base = new URL(url); accessToken = login(); - + // Create a user: ResponseEntity response = request("/nacos/v1/auth/users", - Params.newParams() - .appendParam("username", username1) - .appendParam("password", password1) - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.POST); + Params.newParams().appendParam("username", username1).appendParam("password", password1) + .appendParam("accessToken", accessToken).done(), String.class, HttpMethod.POST); System.out.println(response); Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Create a user: response = request("/nacos/v1/auth/users", - Params.newParams() - .appendParam("username", username2) - .appendParam("password", password2) - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.POST); - + Params.newParams().appendParam("username", username2).appendParam("password", password2) + .appendParam("accessToken", accessToken).done(), String.class, HttpMethod.POST); + System.out.println(response); Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Create a user: response = request("/nacos/v1/auth/users", - Params.newParams() - .appendParam("username", username3) - .appendParam("password", password3) - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.POST); - + Params.newParams().appendParam("username", username3).appendParam("password", password3) + .appendParam("accessToken", accessToken).done(), String.class, HttpMethod.POST); + System.out.println(response); Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Create a role: response = request("/nacos/v1/auth/roles", - Params.newParams() - .appendParam("role", role1) - .appendParam("username", username1) - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.POST); - + Params.newParams().appendParam("role", role1).appendParam("username", username1) + .appendParam("accessToken", accessToken).done(), String.class, HttpMethod.POST); + System.out.println(response); Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Create a role: response = request("/nacos/v1/auth/roles", - Params.newParams() - .appendParam("role", role2) - .appendParam("username", username2) - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.POST); + Params.newParams().appendParam("role", role2).appendParam("username", username2) + .appendParam("accessToken", accessToken).done(), String.class, HttpMethod.POST); System.out.println(response); Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Create a role: response = request("/nacos/v1/auth/roles", - Params.newParams() - .appendParam("role", role3) - .appendParam("username", username3) - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.POST); + Params.newParams().appendParam("role", role3).appendParam("username", username3) + .appendParam("accessToken", accessToken).done(), String.class, HttpMethod.POST); System.out.println(response); Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Add read permission of namespace1 to role1: response = request("/nacos/v1/auth/permissions", - Params.newParams() - .appendParam("role", role1) - .appendParam("resource", namespace1 + ":*:*") - .appendParam("action", "r") - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.POST); + Params.newParams().appendParam("role", role1).appendParam("resource", namespace1 + ":*:*") + .appendParam("action", "r").appendParam("accessToken", accessToken).done(), String.class, + HttpMethod.POST); System.out.println(response); Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Add write permission of namespace1 to role2: response = request("/nacos/v1/auth/permissions", - Params.newParams() - .appendParam("role", role2) - .appendParam("resource", namespace1 + ":*:*") - .appendParam("action", "w") - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.POST); + Params.newParams().appendParam("role", role2).appendParam("resource", namespace1 + ":*:*") + .appendParam("action", "w").appendParam("accessToken", accessToken).done(), String.class, + HttpMethod.POST); System.out.println(response); Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Add read/write permission of namespace1 to role3: response = request("/nacos/v1/auth/permissions", - Params.newParams() - .appendParam("role", role3) - .appendParam("resource", namespace1 + ":*:*") - .appendParam("action", "rw") - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.POST); + Params.newParams().appendParam("role", role3).appendParam("resource", namespace1 + ":*:*") + .appendParam("action", "rw").appendParam("accessToken", accessToken).done(), String.class, + HttpMethod.POST); System.out.println(response); Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Init properties: properties = new Properties(); properties.put(PropertyKeyConst.NAMESPACE, namespace1); properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1" + ":" + port); } - + protected void destroy() { - + // Delete permission: ResponseEntity response = request("/nacos/v1/auth/permissions", - Params.newParams() - .appendParam("role", role1) - .appendParam("resource", namespace1 + ":*:*") - .appendParam("action", "r") - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.DELETE); - + Params.newParams().appendParam("role", role1).appendParam("resource", namespace1 + ":*:*") + .appendParam("action", "r").appendParam("accessToken", accessToken).done(), String.class, + HttpMethod.DELETE); + Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Delete permission: response = request("/nacos/v1/auth/permissions", - Params.newParams() - .appendParam("role", role2) - .appendParam("resource", namespace1 + ":*:*") - .appendParam("action", "w") - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.DELETE); - + Params.newParams().appendParam("role", role2).appendParam("resource", namespace1 + ":*:*") + .appendParam("action", "w").appendParam("accessToken", accessToken).done(), String.class, + HttpMethod.DELETE); + Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Delete permission: response = request("/nacos/v1/auth/permissions", - Params.newParams() - .appendParam("role", role3) - .appendParam("resource", namespace1 + ":*:*") - .appendParam("action", "rw") - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.DELETE); - + Params.newParams().appendParam("role", role3).appendParam("resource", namespace1 + ":*:*") + .appendParam("action", "rw").appendParam("accessToken", accessToken).done(), String.class, + HttpMethod.DELETE); + Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Delete a role: response = request("/nacos/v1/auth/roles", - Params.newParams() - .appendParam("role", role1) - .appendParam("username", username1) - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.DELETE); - + Params.newParams().appendParam("role", role1).appendParam("username", username1) + .appendParam("accessToken", accessToken).done(), String.class, HttpMethod.DELETE); + Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Delete a role: response = request("/nacos/v1/auth/roles", - Params.newParams() - .appendParam("role", role2) - .appendParam("username", username2) - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.DELETE); - + Params.newParams().appendParam("role", role2).appendParam("username", username2) + .appendParam("accessToken", accessToken).done(), String.class, HttpMethod.DELETE); + Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Delete a role: response = request("/nacos/v1/auth/roles", - Params.newParams() - .appendParam("role", role3) - .appendParam("username", username3) - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.DELETE); - + Params.newParams().appendParam("role", role3).appendParam("username", username3) + .appendParam("accessToken", accessToken).done(), String.class, HttpMethod.DELETE); + Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Delete a user: response = request("/nacos/v1/auth/users", - Params.newParams() - .appendParam("username", username1) - .appendParam("password", password1) - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.DELETE); - + Params.newParams().appendParam("username", username1).appendParam("password", password1) + .appendParam("accessToken", accessToken).done(), String.class, HttpMethod.DELETE); + Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Delete a user: response = request("/nacos/v1/auth/users", - Params.newParams() - .appendParam("username", username2) - .appendParam("password", password2) - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.DELETE); - + Params.newParams().appendParam("username", username2).appendParam("password", password2) + .appendParam("accessToken", accessToken).done(), String.class, HttpMethod.DELETE); + Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + // Delete a user: response = request("/nacos/v1/auth/users", - Params.newParams() - .appendParam("username", username3) - .appendParam("password", password3) - .appendParam("accessToken", accessToken) - .done(), - String.class, - HttpMethod.DELETE); - + Params.newParams().appendParam("username", username3).appendParam("password", password3) + .appendParam("accessToken", accessToken).done(), String.class, HttpMethod.DELETE); + Assert.assertTrue(response.getStatusCode().is2xxSuccessful()); - + System.setProperty("nacos.core.auth.enabled", "false"); } } diff --git a/test/core-test/src/test/java/com/alibaba/nacos/test/core/auth/LdapAuth_ITCase.java b/test/core-test/src/test/java/com/alibaba/nacos/test/core/auth/LdapAuth_ITCase.java new file mode 100644 index 000000000..bb842ad1d --- /dev/null +++ b/test/core-test/src/test/java/com/alibaba/nacos/test/core/auth/LdapAuth_ITCase.java @@ -0,0 +1,82 @@ +/* + * 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.nacos.test.core.auth; + +import com.alibaba.nacos.Nacos; +import com.alibaba.nacos.auth.config.AuthConfigs; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.mockito.Mockito; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.test.context.junit4.SpringRunner; + +import java.net.URL; +import java.util.concurrent.TimeUnit; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ LdapAuth_ITCase.NonTlsTest.class,LdapAuth_ITCase.TlsTest.class}) +public class LdapAuth_ITCase extends AuthBase { + + @LocalServerPort + private int port; + + private String filterPrefix = "uid"; + + @MockBean + private LdapTemplate ldapTemplate; + + @Before + public void init() throws Exception { + Mockito.when(ldapTemplate.authenticate("", "(" + filterPrefix + "=" + "karson" + ")", "karson")) + .thenReturn(true); + AuthConfigs.setCachingEnabled(false); + TimeUnit.SECONDS.sleep(5L); + String url = String.format("http://localhost:%d/", port); + System.setProperty("nacos.core.auth.enabled", "true"); + this.base = new URL(url); + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = Nacos.class, properties = {"server.servlet.context-path=/nacos", + "nacos.core.auth.system.type=ldap"}, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) + public static class NonTlsTest extends LdapAuth_ITCase { + + @Test + public void testLdapAuth() throws Exception { + super.login("karson", "karson"); + } + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = Nacos.class, properties = {"server.servlet.context-path=/nacos", + "nacos.core.auth.system.type=ldap", + "nacos.core.auth.ldap.url=ldaps://localhost:636"}, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) + public static class TlsTest extends LdapAuth_ITCase { + + @Test + public void testLdapAuth() throws Exception { + super.login("karson", "karson"); + } + } + + +} diff --git a/test/core-test/src/test/resources/application.properties b/test/core-test/src/test/resources/application.properties index e2c7f8e3d..b433da9b2 100644 --- a/test/core-test/src/test/resources/application.properties +++ b/test/core-test/src/test/resources/application.properties @@ -51,5 +51,5 @@ nacos.core.auth.default.token.expire.seconds=18000 ### The default token: nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789 - +nacos.core.auth.plugin.nacos.token.secret.key=${nacos.core.auth.default.token.secret.key} tldSkipPatterns=derbyLocale_*.jar,jaxb-api.jar,jsr173_1.0_api.jar,jaxb1-impl.jar,activation.jar diff --git a/test/naming-test/src/test/java/com/alibaba/nacos/test/naming/InstanceOperate_With_RootContextPath_ITCase.java b/test/naming-test/src/test/java/com/alibaba/nacos/test/naming/InstanceOperate_With_RootContextPath_ITCase.java deleted file mode 100644 index 19bc8d7f0..000000000 --- a/test/naming-test/src/test/java/com/alibaba/nacos/test/naming/InstanceOperate_With_RootContextPath_ITCase.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.nacos.test.naming; - -import com.alibaba.nacos.Nacos; -import com.alibaba.nacos.sys.env.EnvUtil; -import com.alibaba.nacos.test.base.ConfigCleanUtils; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -import java.io.IOException; - -/** - * Test context path is '/'. - * - * @see #4171 - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = Nacos.class, properties = {"server.servlet.context-path=/nacos", - "server.port=8948"}, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) -@Ignore("Nacos 2.0 use gRPC, not use http open API, so ignore it and will removed") -@Deprecated -public class InstanceOperate_With_RootContextPath_ITCase extends AbstractInstanceOperate_ITCase { - - @BeforeClass - public static void beforeClass() throws IOException { - ConfigCleanUtils.changeToNewTestNacosHome(InstanceOperate_With_RootContextPath_ITCase.class.getSimpleName()); - ConfigCleanUtils.cleanClientCache(); - EnvUtil.setPort(8948); - - } - - @AfterClass - public static void cleanClientCache() throws Exception { - ConfigCleanUtils.cleanClientCache(); - EnvUtil.setPort(8848); - } -} diff --git a/test/naming-test/src/test/java/com/alibaba/nacos/test/naming/NamingHttpClientProxy_ITCase.java b/test/naming-test/src/test/java/com/alibaba/nacos/test/naming/NamingHttpClientProxy_ITCase.java deleted file mode 100644 index 2fab1eca5..000000000 --- a/test/naming-test/src/test/java/com/alibaba/nacos/test/naming/NamingHttpClientProxy_ITCase.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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.nacos.test.naming; - -import com.alibaba.nacos.api.PropertyKeyConst; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.NamingFactory; -import com.alibaba.nacos.api.naming.NamingService; -import com.alibaba.nacos.api.naming.utils.NamingUtils; -import com.alibaba.nacos.naming.NamingApp; -import com.alibaba.nacos.naming.cluster.transport.Serializer; -import com.alibaba.nacos.naming.consistency.Datum; -import com.alibaba.nacos.naming.consistency.KeyBuilder; -import com.alibaba.nacos.naming.consistency.ephemeral.distro.DataStore; -import com.alibaba.nacos.naming.core.Instance; -import com.alibaba.nacos.naming.core.Instances; -import com.alibaba.nacos.naming.misc.NamingProxy; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.test.context.junit4.SpringRunner; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.TimeUnit; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = NamingApp.class, properties = {"server.servlet.context-path=/nacos"}, - webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) -@Ignore("Http sync will stop for 2.0 server, and will be removed") -public class NamingHttpClientProxy_ITCase { - @LocalServerPort - private int port; - @Autowired - private Serializer serializer; - private final DataStore dataStore = new DataStore(); - private NamingService namingService; - private final String namespaceId = "dev"; - private final String serviceName = "biz"; - private final String groupName = "DEFAULT_GROUP"; - - @Before - public void init() throws NacosException { - Properties properties = new Properties(); - properties.put(PropertyKeyConst.NAMESPACE, namespaceId); - properties.put(PropertyKeyConst.SERVER_ADDR, "localhost:" + port); - namingService = NamingFactory.createNamingService(properties); - } - - @Test - public void testSyncData() throws NacosException, InterruptedException { - // write data to DataStore - String groupedName = NamingUtils.getGroupedName(serviceName, groupName); - Instances instances = new Instances(); - Instance instance = new Instance(); - instance.setIp("1.2.3.4"); - instance.setPort(8888); - instance.setEphemeral(true); - instance.setServiceName(groupedName); - List instanceList = new ArrayList(1); - instanceList.add(instance); - instances.setInstanceList(instanceList); - String key = KeyBuilder.buildInstanceListKey(namespaceId, instance.getServiceName(), true); - - Datum datum = new Datum<>(); - datum.value = instances; - datum.key = key; - datum.timestamp.incrementAndGet(); - this.dataStore.put(key, datum); - - // sync data to server - Map dataMap = dataStore.getDataMap(); - byte[] content = serializer.serialize(dataMap); - boolean result = NamingProxy.syncData(content, "localhost:" + port); - if (!result) { - Assert.fail("NamingProxy.syncData error"); - } - - // query instance by api - List allInstances = namingService.getAllInstances(serviceName, false); - for (int i = 0; i < 3 && allInstances.isEmpty(); i++) { - // wait for async op - TimeUnit.SECONDS.sleep(100); - allInstances = namingService.getAllInstances(serviceName, false); - } - if (allInstances.isEmpty()) { - Assert.fail("instance is empty"); - } - - com.alibaba.nacos.api.naming.pojo.Instance dst = allInstances.get(0); - Assert.assertEquals(instance.getIp(), dst.getIp()); - Assert.assertEquals(instance.getPort(), dst.getPort()); - Assert.assertEquals(instance.getServiceName(), dst.getServiceName()); - } - -} diff --git a/test/naming-test/src/test/java/com/alibaba/nacos/test/naming/RestAPI_ITCase.java b/test/naming-test/src/test/java/com/alibaba/nacos/test/naming/RestAPI_ITCase.java index bc3442877..62a7db7e1 100644 --- a/test/naming-test/src/test/java/com/alibaba/nacos/test/naming/RestAPI_ITCase.java +++ b/test/naming-test/src/test/java/com/alibaba/nacos/test/naming/RestAPI_ITCase.java @@ -65,8 +65,12 @@ public class RestAPI_ITCase extends NamingBase { JsonNode json = JacksonUtils.toObj(response.getBody()); Assert.assertNotNull(json.get("serviceCount")); Assert.assertNotNull(json.get("instanceCount")); - Assert.assertNotNull(json.get("responsibleServiceCount")); Assert.assertNotNull(json.get("responsibleInstanceCount")); + Assert.assertNotNull(json.get("clientCount")); + Assert.assertNotNull(json.get("connectionBasedClientCount")); + Assert.assertNotNull(json.get("ephemeralIpPortClientCount")); + Assert.assertNotNull(json.get("persistentIpPortClientCount")); + Assert.assertNotNull(json.get("responsibleClientCount")); } /**