Skip to content

Commit

Permalink
support search by item (#3977)
Browse files Browse the repository at this point in the history
  • Loading branch information
lepdou authored Sep 18, 2021
1 parent debf749 commit 2fb3479
Show file tree
Hide file tree
Showing 24 changed files with 451 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@
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;
Expand Down Expand Up @@ -87,6 +91,18 @@ public NamespaceDTO get(@PathVariable("namespaceId") Long namespaceId) {
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

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;
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.springframework.data.repository.PagingAndSortingRepository;

import java.util.List;
import java.util.Set;

public interface NamespaceRepository extends PagingAndSortingRepository<Namespace, Long> {

Expand All @@ -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);

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
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;

Expand Down Expand Up @@ -138,6 +140,10 @@ public List<Item> findItemsModifiedAfterDate(long namespaceId, Date date) {
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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
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;
Expand Down Expand Up @@ -104,6 +106,21 @@ public Namespace findOne(String appId, String clusterName, String namespaceName)
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ public void deleteApp(Env env, String appId, String operator) {
@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>>() {
};
Expand All @@ -79,6 +83,14 @@ public List<NamespaceDTO> findNamespaceByCluster(String appId, Env env, String c
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,4 +270,7 @@ public String[] webHookUrls() {
return getArrayProperty("config.release.webhook.service.url", null);
}

public boolean supportSearchByItem() {
return getBooleanProperty("searchByItem.switch", true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@


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;
Expand Down Expand Up @@ -103,15 +102,6 @@ public List<App> findApps(@RequestParam(value = "appIds", required = false) Stri
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();
Expand Down Expand Up @@ -184,7 +174,7 @@ public MultiResponseEntity<EnvClusterInfo> nav(@PathVariable String appId) {
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();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* 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());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.Set;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
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;
Expand Down Expand Up @@ -48,6 +49,7 @@
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;

Expand Down Expand Up @@ -181,6 +183,13 @@ public List<NamespaceDTO> findNamespaces(String appId, Env env, String clusterNa
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) {
Expand Down
4 changes: 2 additions & 2 deletions apollo-portal/src/main/resources/static/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -764,4 +764,4 @@
"Rollback.RollbackFailed": "Failed to Rollback",
"Revoke.RevokeFailed": "Failed to Revoke",
"Revoke.RevokeSuccessfully": "Revoke Successfully"
}
}
2 changes: 1 addition & 1 deletion apollo-portal/src/main/resources/static/i18n/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -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不能为空",
Expand Down
Loading

0 comments on commit 2fb3479

Please sign in to comment.