未验证 提交 2fb34799 编写于 作者: L lepdou 提交者: GitHub

support search by item (#3977)

上级 debf749a
......@@ -15,6 +15,7 @@ Apollo 1.10.0
* [Fix issue: ingress syntax](https://github.com/apolloconfig/apollo/pull/3933)
* [refactor: let open api more easier to use and development](https://github.com/apolloconfig/apollo/pull/3943)
* [feat(scripts): use bash to call openapi](https://github.com/apolloconfig/apollo/pull/3980)
* [Support search by item](https://github.com/apolloconfig/apollo/pull/3977)
------------------
All issues and pull requests are [here](https://github.com/ctripcorp/apollo/milestone/8?closed=1)
......@@ -19,9 +19,13 @@ package com.ctrip.framework.apollo.adminservice.controller;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.service.NamespaceService;
import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import com.ctrip.framework.apollo.common.dto.PageDTO;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.exception.NotFoundException;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
......@@ -87,6 +91,18 @@ public class NamespaceController {
return BeanUtils.transform(NamespaceDTO.class, namespace);
}
/**
* the returned content's size is not fixed. so please carefully used.
*/
@GetMapping("/namespaces/find-by-item")
public PageDTO<NamespaceDTO> findByItem(@RequestParam String itemKey, Pageable pageable) {
Page<Namespace> namespacePage = namespaceService.findByItem(itemKey, pageable);
List<NamespaceDTO> namespaceDTOS = BeanUtils.batchTransform(NamespaceDTO.class, namespacePage.getContent());
return new PageDTO<>(namespaceDTOS, pageable, namespacePage.getTotalElements());
}
@GetMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName:.+}")
public NamespaceDTO get(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
......
......@@ -18,6 +18,8 @@ package com.ctrip.framework.apollo.biz.repository;
import com.ctrip.framework.apollo.biz.entity.Item;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
......@@ -35,6 +37,8 @@ public interface ItemRepository extends PagingAndSortingRepository<Item, Long> {
List<Item> findByNamespaceIdAndDataChangeLastModifiedTimeGreaterThan(Long namespaceId, Date date);
Page<Item> findByKey(String key, Pageable pageable);
Item findFirst1ByNamespaceIdOrderByLineNumDesc(Long namespaceId);
@Modifying
......
......@@ -24,6 +24,7 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List;
import java.util.Set;
public interface NamespaceRepository extends PagingAndSortingRepository<Namespace, Long> {
......@@ -39,6 +40,8 @@ public interface NamespaceRepository extends PagingAndSortingRepository<Namespac
List<Namespace> findByNamespaceName(String namespaceName, Pageable page);
List<Namespace> findByIdIn(Set<Long> namespaceIds);
int countByNamespaceNameAndAppIdNot(String namespaceName, String appId);
}
......@@ -27,6 +27,8 @@ import com.ctrip.framework.apollo.common.exception.NotFoundException;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -138,6 +140,10 @@ public class ItemService {
return itemRepository.findByNamespaceIdAndDataChangeLastModifiedTimeGreaterThan(namespaceId, date);
}
public Page<Item> findItemsByKey(String key, Pageable pageable) {
return itemRepository.findByKey(key, pageable);
}
@Transactional
public Item save(Item entity) {
checkItemKeyLength(entity.getKey());
......
......@@ -35,6 +35,8 @@ import com.ctrip.framework.apollo.core.ConfigConsts;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -104,6 +106,21 @@ public class NamespaceService {
namespaceName);
}
/**
* the returned content's size is not fixed. so please carefully used.
*/
public Page<Namespace> findByItem(String itemKey, Pageable pageable) {
Page<Item> items = itemService.findItemsByKey(itemKey, pageable);
if (!items.hasContent()) {
return Page.empty();
}
Set<Long> namespaceIds = BeanUtils.toPropertySet("namespaceId", items.getContent());
return new PageImpl<>(namespaceRepository.findByIdIn(namespaceIds));
}
public Namespace findPublicNamespaceForAssociatedNamespace(String clusterName, String namespaceName) {
AppNamespace appNamespace = appNamespaceService.findPublicNamespaceByName(namespaceName);
if (appNamespace == null) {
......
......@@ -16,7 +16,6 @@
*/
package com.ctrip.framework.apollo.core.utils;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import org.junit.Assert;
import org.junit.Test;
......
......@@ -68,6 +68,10 @@ public class AdminServiceAPI {
@Service
public static class NamespaceAPI extends API {
private ParameterizedTypeReference<PageDTO<NamespaceDTO>>
namespacePageDTO = new ParameterizedTypeReference<PageDTO<NamespaceDTO>>() {
};
private ParameterizedTypeReference<Map<String, Boolean>>
typeReference = new ParameterizedTypeReference<Map<String, Boolean>>() {
};
......@@ -79,6 +83,14 @@ public class AdminServiceAPI {
return Arrays.asList(namespaceDTOs);
}
public PageDTO<NamespaceDTO> findByItem(Env env, String itemKey, int page, int size) {
ResponseEntity<PageDTO<NamespaceDTO>>
entity =
restTemplate.get(env, "/namespaces/find-by-item?itemKey={itemKey}&page={page}&size={size}",
namespacePageDTO, itemKey, page, size);
return entity.getBody();
}
public NamespaceDTO loadNamespace(String appId, Env env, String clusterName,
String namespaceName) {
return
......
......@@ -270,4 +270,7 @@ public class PortalConfig extends RefreshableConfig {
return getArrayProperty("config.release.webhook.service.url", null);
}
public boolean supportSearchByItem() {
return getBooleanProperty("searchByItem.switch", true);
}
}
......@@ -18,7 +18,6 @@ package com.ctrip.framework.apollo.portal.controller;
import com.ctrip.framework.apollo.common.dto.AppDTO;
import com.ctrip.framework.apollo.common.dto.PageDTO;
import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.http.MultiResponseEntity;
......@@ -103,15 +102,6 @@ public class AppController {
return appService.findByAppIds(Sets.newHashSet(appIds.split(",")));
}
@GetMapping("/search/by-appid-or-name")
public PageDTO<App> searchByAppIdOrAppName(@RequestParam(value = "query", required = false) String query,
Pageable pageable) {
if (Strings.isNullOrEmpty(query)) {
return appService.findAll(pageable);
}
return appService.searchByAppIdOrAppName(query, pageable);
}
@GetMapping("/by-owner")
public List<App> findAppsByOwner(@RequestParam("owner") String owner, Pageable page) {
Set<String> appIds = Sets.newHashSet();
......@@ -184,7 +174,7 @@ public class AppController {
public ResponseEntity<Void> create(@PathVariable String env, @Valid @RequestBody App app) {
appService.createAppInRemote(Env.valueOf(env), app);
roleInitializationService.initNamespaceSpecificEnvRoles(app.getAppId(), ConfigConsts.NAMESPACE_APPLICATION,
roleInitializationService.initNamespaceSpecificEnvRoles(app.getAppId(), ConfigConsts.NAMESPACE_APPLICATION,
env, userInfoHolder.getUser().getUserId());
return ResponseEntity.ok().build();
......
/*
* Copyright 2021 Apollo Authors
*
* 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.ctrip.framework.apollo.portal.controller;
import com.google.common.collect.Lists;
import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import com.ctrip.framework.apollo.common.dto.PageDTO;
import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.portal.component.PortalSettings;
import com.ctrip.framework.apollo.portal.component.config.PortalConfig;
import com.ctrip.framework.apollo.portal.environment.Env;
import com.ctrip.framework.apollo.portal.service.AppService;
import com.ctrip.framework.apollo.portal.service.NamespaceService;
import org.springframework.data.domain.Pageable;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author lepdou 2021-09-13
*/
@RestController("/app")
public class SearchController {
private AppService appService;
private PortalSettings portalSettings;
private NamespaceService namespaceService;
private PortalConfig portalConfig;
public SearchController(final AppService appService,
final PortalSettings portalSettings,
final PortalConfig portalConfig,
final NamespaceService namespaceService) {
this.appService = appService;
this.portalConfig = portalConfig;
this.portalSettings = portalSettings;
this.namespaceService = namespaceService;
}
@GetMapping("/apps/search/by-appid-or-name")
public PageDTO<App> search(@RequestParam(value = "query", required = false) String query, Pageable pageable) {
if (StringUtils.isEmpty(query)) {
return appService.findAll(pageable);
}
//search app
PageDTO<App> appPageDTO = appService.searchByAppIdOrAppName(query, pageable);
if (appPageDTO.hasContent()) {
return appPageDTO;
}
if (!portalConfig.supportSearchByItem()) {
return new PageDTO<>(Lists.newLinkedList(), pageable, 0);
}
//search item
return searchByItem(query, pageable);
}
private PageDTO<App> searchByItem(String itemKey, Pageable pageable) {
List<App> result = Lists.newLinkedList();
if (StringUtils.isEmpty(itemKey)) {
return new PageDTO<>(result, pageable, 0);
}
//use the env witch has the most namespace as page index.
final AtomicLong maxTotal = new AtomicLong(0);
List<Env> activeEnvs = portalSettings.getActiveEnvs();
activeEnvs.forEach(env -> {
PageDTO<NamespaceDTO> namespacePage = namespaceService.findNamespacesByItem(env, itemKey, pageable);
if (!namespacePage.hasContent()) {
return;
}
long currentEnvNSTotal = namespacePage.getTotal();
if (currentEnvNSTotal > maxTotal.get()) {
maxTotal.set(namespacePage.getTotal());
}
List<NamespaceDTO> namespaceDTOS = namespacePage.getContent();
namespaceDTOS.forEach(namespaceDTO -> {
String cluster = namespaceDTO.getClusterName();
String namespaceName = namespaceDTO.getNamespaceName();
App app = new App();
app.setAppId(namespaceDTO.getAppId());
app.setName(env.getName() + " / " + cluster + " / " + namespaceName);
app.setOrgId(env.getName() + "+" + cluster + "+" + namespaceName);
app.setOrgName("SearchByItem" + "+" + itemKey);
result.add(app);
});
});
return new PageDTO<>(result, pageable, maxTotal.get());
}
}
......@@ -36,7 +36,6 @@ import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.List;
import java.util.Set;
......
......@@ -19,6 +19,7 @@ package com.ctrip.framework.apollo.portal.service;
import com.ctrip.framework.apollo.common.constants.GsonType;
import com.ctrip.framework.apollo.common.dto.ItemDTO;
import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import com.ctrip.framework.apollo.common.dto.PageDTO;
import com.ctrip.framework.apollo.common.dto.ReleaseDTO;
import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
......@@ -48,6 +49,7 @@ import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -181,6 +183,13 @@ public class NamespaceService {
return namespaceAPI.findNamespaceByCluster(appId, env, clusterName);
}
/**
* the returned content's size is not fixed. so please carefully used.
*/
public PageDTO<NamespaceDTO> findNamespacesByItem(Env env, String itemKey, Pageable pageable) {
return namespaceAPI.findByItem(env, itemKey, pageable.getPageNumber(), pageable.getPageSize());
}
public List<NamespaceDTO> getPublicAppNamespaceAllNamespaces(Env env, String publicNamespaceName,
int page,
int size) {
......
......@@ -729,7 +729,7 @@
"Valdr.Release.ReleaseName.Required": "Release Name cannot be empty",
"Valdr.Release.Comment.Size": "Comment length should not exceed 256 characters",
"ApolloConfirmDialog.DefaultConfirmBtnName": "OK",
"ApolloConfirmDialog.SearchPlaceHolder": "Search items (App Id, App Name)",
"ApolloConfirmDialog.SearchPlaceHolder": "Search Apps by appId, appName, configuration key",
"RulesModal.ChooseInstances": "Select from the list of instances",
"RulesModal.InvalidIp": "Illegal IP Address: '{{ip}}'",
"RulesModal.GrayscaleAppIdCanNotBeNull": "Grayscale AppId cannot be empty",
......@@ -764,4 +764,4 @@
"Rollback.RollbackFailed": "Failed to Rollback",
"Revoke.RevokeFailed": "Failed to Revoke",
"Revoke.RevokeSuccessfully": "Revoke Successfully"
}
\ No newline at end of file
}
......@@ -729,7 +729,7 @@
"Valdr.Release.ReleaseName.Required": "Release Name不能为空",
"Valdr.Release.Comment.Size": "备注长度不能多于256个字符",
"ApolloConfirmDialog.DefaultConfirmBtnName": "确认",
"ApolloConfirmDialog.SearchPlaceHolder": "搜索应用(AppId、应用名)",
"ApolloConfirmDialog.SearchPlaceHolder": "搜索应用Id、应用名、配置项Key",
"RulesModal.ChooseInstances": "从实例列表中选择",
"RulesModal.InvalidIp": "不合法的IP地址: '{{ip}}'",
"RulesModal.GrayscaleAppIdCanNotBeNull": "灰度的AppId不能为空",
......
......@@ -45,7 +45,9 @@ function ConfigBaseInfoController($rootScope, $scope, $window, $location, $trans
$rootScope.pageContext = {
appId: appId,
env: urlParams.env ? urlParams.env : (scene ? scene.env : ''),
clusterName: urlParams.cluster ? urlParams.cluster : (scene ? scene.cluster : 'default')
clusterName: urlParams.cluster ? urlParams.cluster : (scene ? scene.cluster : 'default'),
namespaceName: urlParams.namespace,
item: urlParams.item,
};
//storage page context to session storage
......@@ -201,7 +203,7 @@ function ConfigBaseInfoController($rootScope, $scope, $window, $location, $trans
$rootScope.pageContext.env = nodes[0].env;
}
EventManager.emit(EventManager.EventType.REFRESH_NAMESPACE);
EventManager.emit(EventManager.EventType.REFRESH_NAMESPACE, {firstLoad: true});
nodes.forEach(function (env) {
if (!env.clusters || env.clusters.length == 0) {
......
......@@ -107,12 +107,12 @@ function controller($rootScope, $scope, $translate, toastr, AppUtil, EventManage
if (context.namespace) {
refreshSingleNamespace(context.namespace);
} else {
refreshAllNamespaces();
refreshAllNamespaces(context);
}
});
function refreshAllNamespaces() {
function refreshAllNamespaces(context) {
if ($rootScope.pageContext.env == '') {
return;
}
......@@ -126,6 +126,19 @@ function controller($rootScope, $scope, $translate, toastr, AppUtil, EventManage
$('.config-item-container').removeClass('hide');
initPublishInfo();
//If there is a namespace parameter in the URL, expand the corresponding namespace directly
if (context && context.firstLoad && $rootScope.pageContext.namespaceName) {
refreshSingleNamespace({
baseInfo: {
namespaceName: $rootScope.pageContext.namespaceName
},
searchInfo: {
showSearchInput: true,
searchItemKey: $rootScope.pageContext.item,
}
});
}
}, function (result) {
toastr.error(AppUtil.errorMsg(result), $translate.instant('Config.LoadingAllNamespaceError'));
});
......@@ -136,6 +149,9 @@ function controller($rootScope, $scope, $translate, toastr, AppUtil, EventManage
return;
}
const showSearchItemInput = namespace.searchInfo ? namespace.searchInfo.showSearchInput : false;
const searchItemKey = namespace.searchInfo ? namespace.searchInfo.searchItemKey : '';
ConfigService.load_namespace($rootScope.pageContext.appId,
$rootScope.pageContext.env,
$rootScope.pageContext.clusterName,
......@@ -143,11 +159,13 @@ function controller($rootScope, $scope, $translate, toastr, AppUtil, EventManage
function (result) {
$scope.namespaces.forEach(function (namespace, index) {
if (namespace.baseInfo.namespaceName == result.baseInfo.namespaceName) {
if (namespace.baseInfo.namespaceName === result.baseInfo.namespaceName) {
result.showNamespaceBody = true;
result.initialized = true;
result.show = namespace.show;
$scope.namespaces[index] = result;
result.showSearchItemInput = showSearchItemInput;
result.searchItemKey = searchItemKey;
}
});
......
......@@ -56,7 +56,9 @@ directive_module.directive('apollonav',
data.content.forEach(function (app) {
result.push({
id: app.appId,
text: app.appId + ' / ' + app.name
text: app.appId + ' / ' + app.name,
orgId: app.orgId,
orgName: app.orgName
})
});
return {
......@@ -81,17 +83,40 @@ directive_module.directive('apollonav',
$('#app-search-list').on('select2:select', function () {
var selected = $('#app-search-list').select2('data');
if (selected && selected.length) {
jumpToConfigPage(selected[0].id)
jumpToConfigPage(selected[0].id, selected[0].name, selected[0].orgName, selected[0].orgId)
}
});
});
function jumpToConfigPage(selectedAppId) {
function jumpToConfigPage(selectedAppId, name, type, namespaceInfo) {
if ($window.location.href.indexOf("config.html") > -1) {
$window.location.hash = "appid=" + selectedAppId;
if (type && type.startsWith('SearchByItem')) {
var namespaceInfos = namespaceInfo.split('+');
var env = namespaceInfos[0];
var cluster = namespaceInfos[1];
var namespaceName = namespaceInfos[2];
var searchKey = type.split("+")[1];
$window.location.hash =
"appid=" + selectedAppId + "&env=" + env + "&cluster=" + cluster + "&namespace="
+ namespaceName + "&item=" + searchKey;
} else {
$window.location.hash = "appid=" + selectedAppId;
}
$window.location.reload();
} else {
$window.location.href = AppUtil.prefixPath() + '/config.html?#appid=' + selectedAppId;
if (type && type.startsWith('SearchByItem')) {
var namespaceInfos = namespaceInfo.split('+');
var env = namespaceInfos[0];
var cluster = namespaceInfos[1];
var namespaceName = namespaceInfos[2];
var searchKey = type.split("+")[1];
$window.location.href =
AppUtil.prefixPath() + '/config.html?#appid=' + selectedAppId + "&env=" + env
+ "&cluster=" + cluster + "&namespace=" + namespaceName + "&item=" + searchKey;
} else {
$window.location.href = AppUtil.prefixPath() + '/config.html?#appid=' + selectedAppId;
}
}
};
......
......@@ -66,6 +66,7 @@ function directive($window, $translate, toastr, AppUtil, EventManager, Permissio
scope.toggleItemSearchInput = toggleItemSearchInput;
scope.toggleHistorySearchInput = toggleHistorySearchInput;
scope.searchItems = searchItems;
scope.resetSearchItems = resetSearchItems;
scope.searchHistory = searchHistory;
scope.loadCommitHistory = loadCommitHistory;
scope.toggleTextEditStatus = toggleTextEditStatus;
......@@ -130,7 +131,8 @@ function directive($window, $translate, toastr, AppUtil, EventManager, Permissio
namespace.isBranch = false;
namespace.displayControl = {
currentOperateBranch: 'master',
showSearchInput: false,
showSearchInput: namespace.showSearchItemInput,
searchItemKey: namespace.searchItemKey,
showHistorySearchInput: false,
show: scope.showBody
};
......@@ -153,6 +155,7 @@ function directive($window, $translate, toastr, AppUtil, EventManager, Permissio
initPermission(namespace);
initLinkedNamespace(namespace);
loadInstanceInfo(namespace);
initSearchItemInput(namespace);
function initNamespaceBranch(namespace) {
NamespaceBranchService.findNamespaceBranch(scope.appId, scope.env,
......@@ -398,6 +401,12 @@ function directive($window, $translate, toastr, AppUtil, EventManager, Permissio
}
function initSearchItemInput(namespace) {
if (namespace.displayControl.searchItemKey) {
namespace.searchKey = namespace.displayControl.searchItemKey;
searchItems(namespace);
}
}
}
function initNamespaceInstancesCount(namespace) {
......@@ -863,6 +872,11 @@ function directive($window, $translate, toastr, AppUtil, EventManager, Permissio
namespace.viewItems = items;
}
function resetSearchItems(namespace) {
namespace.searchKey = '';
searchItems(namespace);
}
function toggleHistorySearchInput(namespace) {
namespace.displayControl.showHistorySearchInput = !namespace.displayControl.showHistorySearchInput;
}
......
......@@ -451,7 +451,7 @@ table th {
}
.namespace-panel .namespace-view-table .search-input input {
width: 350px;
width: 500px;
}
.namespace-panel .no-config-panel {
......@@ -836,4 +836,4 @@ table th {
.app-search-list .select2-container .select2-selection .select2-selection__rendered {
line-height: 34px;
font-size: 14px;
}
\ No newline at end of file
}
......@@ -253,9 +253,14 @@
<!--not link namespace-->
<div ng-if="!namespace.isLinkedNamespace">
<div class="search-input" ng-show="namespace.displayControl.showSearchInput">
<input type="text" class="form-control"
placeholder="{{'Component.Namespace.Master.Items.Body.FilterByKey' | translate }}"
ng-model="namespace.searchKey" ng-change="searchItems(namespace)">
<form class="form-inline">
<div class="form-group">
<input type="text" class="form-control" id="searchItemKeyInput"
placeholder="{{'Component.Namespace.Master.Items.Body.FilterByKey' | translate }}"
ng-model="namespace.searchKey" ng-change="searchItems(namespace)">
</div>
<button type="button" class="btn btn-default" ng-click="resetSearchItems(namespace)">{{'Config.Sync.Rest' | translate }}</button>
</form>
</div>
<table class="table table-bordered table-striped table-hover">
......@@ -1140,4 +1145,4 @@
</section>
</div>
</section>
\ No newline at end of file
</section>
/*
* Copyright 2021 Apollo Authors
*
* 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.ctrip.framework.apollo.portal.controller;
import com.google.common.collect.Lists;
import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import com.ctrip.framework.apollo.common.dto.PageDTO;
import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.portal.component.PortalSettings;
import com.ctrip.framework.apollo.portal.component.config.PortalConfig;
import com.ctrip.framework.apollo.portal.environment.Env;
import com.ctrip.framework.apollo.portal.service.AppService;
import com.ctrip.framework.apollo.portal.service.NamespaceService;
import org.junit.Assert;
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.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author lepdou 2021-09-13
*/
@RunWith(MockitoJUnitRunner.class)
public class SearchControllerTest {
@Mock
private AppService appService;
@Mock
private NamespaceService namespaceService;
@Mock
private PortalSettings portalSettings;
@Mock
private PortalConfig portalConfig;
@InjectMocks
private SearchController searchController;
@Test
public void testSearchByEmptyKey() {
PageRequest request = PageRequest.of(0, 20);
searchController.search("", request);
verify(appService, times(1)).findAll(request);
}
@Test
public void testSearchApp() {
String query = "timeout";
PageRequest request = PageRequest.of(0, 20);
PageDTO<App> apps = genPageApp(10, request, 100);
when(appService.searchByAppIdOrAppName(query, request)).thenReturn(apps);
searchController.search(query, request);
verify(appService, times(0)).findAll(request);
verify(appService, times(1)).searchByAppIdOrAppName(query, request);
}
@Test
public void testSearchItemSwitch() {
String query = "timeout";
PageRequest request = PageRequest.of(0, 20);
PageDTO<App> apps = new PageDTO<>(Lists.newLinkedList(), request, 0);
when(appService.searchByAppIdOrAppName(query, request)).thenReturn(apps);
when(portalConfig.supportSearchByItem()).thenReturn(false);
PageDTO<App> result = searchController.search(query, request);
Assert.assertFalse(result.hasContent());
verify(appService, times(0)).findAll(request);
verify(appService, times(1)).searchByAppIdOrAppName(query, request);
}
@Test
public void testSearchItem() {
String query = "timeout";
PageRequest request = PageRequest.of(0, 20);
PageDTO<App> apps = new PageDTO<>(Lists.newLinkedList(), request, 0);
PageDTO<NamespaceDTO> devNamespaces = genPageNamespace(10, request, 20);
PageDTO<NamespaceDTO> fatNamespaces = genPageNamespace(15, request, 30);
when(appService.searchByAppIdOrAppName(query, request)).thenReturn(apps);
when(portalConfig.supportSearchByItem()).thenReturn(true);
when(portalSettings.getActiveEnvs()).thenReturn(Lists.newArrayList(Env.DEV, Env.FAT));
when(namespaceService.findNamespacesByItem(Env.DEV, query, request)).thenReturn(devNamespaces);
when(namespaceService.findNamespacesByItem(Env.FAT, query, request)).thenReturn(fatNamespaces);
PageDTO<App> result = searchController.search(query, request);
Assert.assertTrue(result.hasContent());
Assert.assertEquals(25, result.getContent().size());
Assert.assertEquals(30, result.getTotal());
verify(appService, times(0)).findAll(request);
verify(appService, times(1)).searchByAppIdOrAppName(query, request);
verify(namespaceService).findNamespacesByItem(Env.DEV, query, request);
verify(namespaceService).findNamespacesByItem(Env.FAT, query, request);
}
private PageDTO<App> genPageApp(int size, Pageable pageable, int total) {
List<App> result = Lists.newLinkedList();
for (int i = 0; i < size; i++) {
App app = new App();
result.add(app);
}
return new PageDTO<>(result, pageable, total);
}
private PageDTO<NamespaceDTO> genPageNamespace(int size, Pageable pageable, int total) {
List<NamespaceDTO> result = Lists.newLinkedList();
for (int i = 0; i < size; i++) {
NamespaceDTO namespaceDTO = new NamespaceDTO();
result.add(namespaceDTO);
}
return new PageDTO<>(result, pageable, total);
}
}
......@@ -1152,6 +1152,12 @@ portal上“帮助”链接的地址,默认是Apollo github的wiki首页,可
### 3.1.12 admin-service.access.tokens - 设置apollo-portal访问各环境apollo-adminservice所需的access token
### 3.1.13 searchByItem.switch - 控制台搜索框是否支持按配置项搜索
默认为 true,可以方便的按配置项快速搜索配置
如果设置为 false,则关闭此功能
> 适用于1.7.1及以上版本
如果对应环境的apollo-adminservice开启了[访问控制](#_326-admin-serviceaccesscontrolenabled-配置apollo-adminservice是否开启访问控制),那么需要在此配置apollo-portal访问该环境apollo-adminservice所需的access token,否则会访问失败
......
--
-- Copyright 2021 Apollo Authors
--
-- 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.
--
# delta schema to upgrade apollo config db from v1.9.0 to v1.10.0
Use ApolloConfigDB;
ALTER TABLE Item ADD INDEX IX_key (`Key`);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册