Skip to content

Commit

Permalink
feature(openapi): Add a new API to load items with pagination (#4468)
Browse files Browse the repository at this point in the history
  • Loading branch information
mghio authored Jul 24, 2022
1 parent 0ca7eb0 commit aaa3561
Show file tree
Hide file tree
Showing 17 changed files with 347 additions and 34 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Apollo 2.1.0
* [Allow users to associate multiple public namespaces at a time](https://github.com/apolloconfig/apollo/pull/4437)
* [Move apollo-demo, scripts/docker-quick-start and scripts/apollo-on-kubernetes out of main repository](https://github.com/apolloconfig/apollo/pull/4440)
* [Add search key when comparing Configuration items](https://github.com/apolloconfig/apollo/pull/4459)
* [Add a new API to load items with pagination](https://github.com/apolloconfig/apollo/pull/4468)
* [fix(#4474):'openjdk:8-jre-alpine' potentially causing wrong number of cpu cores](https://github.com/apolloconfig/apollo/pull/4475)
------------------
All issues and pull requests are [here](https://github.com/apolloconfig/apollo/milestone/11?closed=1)
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.ctrip.framework.apollo.biz.service.ReleaseService;
import com.ctrip.framework.apollo.biz.utils.ConfigChangeContentBuilder;
import com.ctrip.framework.apollo.common.dto.ItemDTO;
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;
Expand All @@ -37,6 +38,9 @@
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

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 @@ -236,4 +240,15 @@ public ItemDTO get(@PathVariable("appId") String appId,
return BeanUtils.transform(ItemDTO.class, item);
}

@GetMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items-with-page")
public PageDTO<ItemDTO> findItemsByNamespace(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName,
Pageable pageable) {
Page<Item> itemPage = itemService.findItemsByNamespace(appId, clusterName, namespaceName, pageable);

List<ItemDTO> itemDTOS = BeanUtils.batchTransform(ItemDTO.class, itemPage.getContent());
return new PageDTO<>(itemDTOS, pageable, itemPage.getTotalElements());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public interface ItemRepository extends PagingAndSortingRepository<Item, Long> {
List<Item> findByNamespaceIdAndDataChangeLastModifiedTimeGreaterThan(Long namespaceId, Date date);

Page<Item> findByKey(String key, Pageable pageable);

Page<Item> findByNamespaceId(Long namespaceId, Pageable pageable);

Item findFirst1ByNamespaceIdOrderByLineNumDesc(Long namespaceId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,12 @@ public int batchDelete(long namespaceId, String operator) {
}

public Item findOne(String appId, String clusterName, String namespaceName, String key) {
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) {
throw new NotFoundException(
String.format("namespace not found for %s %s %s", appId, clusterName, namespaceName));
}
Namespace namespace = findNamespaceByAppIdAndClusterNameAndNamespaceName(appId, clusterName, namespaceName);
return itemRepository.findByNamespaceIdAndKey(namespace.getId(), key);
}

public Item findLastOne(String appId, String clusterName, String namespaceName) {
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) {
throw new NotFoundException(
String.format("namespace not found for %s %s %s", appId, clusterName, namespaceName));
}
Namespace namespace = findNamespaceByAppIdAndClusterNameAndNamespaceName(appId, clusterName, namespaceName);
return findLastOne(namespace.getId());
}

Expand Down Expand Up @@ -145,6 +137,11 @@ public Page<Item> findItemsByKey(String key, Pageable pageable) {
return itemRepository.findByKey(key, pageable);
}

public Page<Item> findItemsByNamespace(String appId, String clusterName, String namespaceName, Pageable pageable) {
Namespace namespace = findNamespaceByAppIdAndClusterNameAndNamespaceName(appId, clusterName, namespaceName);
return itemRepository.findByNamespaceId(namespace.getId(), pageable);
}

@Transactional
public Item save(Item entity) {
checkItemKeyLength(entity.getKey());
Expand Down Expand Up @@ -222,4 +219,15 @@ private int getItemValueLengthLimit(long namespaceId) {
return bizConfig.itemValueLengthLimit();
}

private Namespace findNamespaceByAppIdAndClusterNameAndNamespaceName(String appId,
String clusterName,
String namespaceName) {
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) {
throw new NotFoundException(String.format("namespace not found for appId:%s clusterName:%s namespaceName:%s",
appId, clusterName, namespaceName));
}
return namespace;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ public PageDTO(List<T> content, Pageable pageable, long total) {
this.size = pageable.getPageSize();
}


public long getTotal() {
return total;
}
Expand All @@ -54,7 +53,7 @@ public int getSize() {
return size;
}

public boolean hasContent(){
public boolean hasContent() {
return content != null && content.size() > 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.ctrip.framework.apollo.openapi.api;

import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenPageDTO;

/**
* @author wxq
Expand All @@ -37,4 +38,8 @@ void createOrUpdateItem(String appId, String env, String clusterName, String nam

void removeItem(String appId, String env, String clusterName, String namespaceName, String key,
String operator);

OpenPageDTO<OpenItemDTO> findItemsByNamespace(String appId, String env, String clusterName,
String namespaceName, int page, int size);

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,7 @@
import com.ctrip.framework.apollo.openapi.client.service.ItemOpenApiService;
import com.ctrip.framework.apollo.openapi.client.service.NamespaceOpenApiService;
import com.ctrip.framework.apollo.openapi.client.service.ReleaseOpenApiService;
import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenAppDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenAppNamespaceDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenClusterDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenEnvClusterDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceLockDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenReleaseDTO;
import com.ctrip.framework.apollo.openapi.dto.*;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
Expand Down Expand Up @@ -160,6 +152,14 @@ public OpenItemDTO getItem(String appId, String env, String clusterName, String
return itemService.getItem(appId, env, clusterName, namespaceName, key);
}

/**
* Paging get configs
*/
public OpenPageDTO<OpenItemDTO> findItemsByNamespace(String appId, String env, String clusterName,
String namespaceName, int page, int size) {
return itemService.findItemsByNamespace(appId, env, clusterName, namespaceName, page, size);
}

/**
* Add config
* @return the created config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,12 @@ protected void checkNotEmpty(String value, String name) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(value), name + " should not be null or empty");
}

protected void checkPage(int page) {
Preconditions.checkArgument(page >= 0, "page should be positive or 0");
}

protected void checkSize(int size) {
Preconditions.checkArgument(size > 0, "size should be positive number");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,22 @@
import com.ctrip.framework.apollo.openapi.client.exception.ApolloOpenApiException;
import com.ctrip.framework.apollo.openapi.client.url.OpenApiPathBuilder;
import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenPageDTO;
import com.google.common.base.Strings;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import java.lang.reflect.Type;

public class ItemOpenApiService extends AbstractOpenApiService implements
com.ctrip.framework.apollo.openapi.api.ItemOpenApiService {

private static final Type OPEN_PAGE_DTO_OPEN_ITEM_DTO_TYPE_REFERENCE = new TypeToken<OpenPageDTO<OpenItemDTO>>() {
}.getType();

public ItemOpenApiService(CloseableHttpClient client, String baseUrl, Gson gson) {
super(client, baseUrl, gson);
}
Expand Down Expand Up @@ -189,4 +196,36 @@ public void removeItem(String appId, String env, String clusterName, String name
}

}

@Override
public OpenPageDTO<OpenItemDTO> findItemsByNamespace(String appId, String env, String clusterName,
String namespaceName, int page, int size) {
if (Strings.isNullOrEmpty(clusterName)) {
clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
}
if (Strings.isNullOrEmpty(namespaceName)) {
namespaceName = ConfigConsts.NAMESPACE_APPLICATION;
}

checkNotEmpty(appId, "App id");
checkNotEmpty(env, "Env");
checkPage(page);
checkSize(size);

OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder()
.envsPathVal(env)
.appsPathVal(appId)
.clustersPathVal(clusterName)
.namespacesPathVal(namespaceName)
.itemsPathVal("")
.addParam("page", page)
.addParam("size", size);

try (CloseableHttpResponse response = get(pathBuilder)) {
return gson.fromJson(EntityUtils.toString(response.getEntity()), OPEN_PAGE_DTO_OPEN_ITEM_DTO_TYPE_REFERENCE);
} catch (Throwable ex) {
throw new RuntimeException(String.format("Paging get items: appId: %s, cluster: %s, namespace: %s in env: %s failed",
appId, clusterName, namespaceName, env), ex);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2022 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.openapi.dto;

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

/**
* @author mghio (mghio.dev@gmail.com)
*/
public class OpenPageDTO<T> {

private final int page;
private final int size;
private final long total;
private final List<T> content;

public OpenPageDTO(int page, int size, long total, List<T> content) {
this.page = page;
this.size = size;
this.total = total;
this.content = Collections.unmodifiableList(content);
}

public int getPage() {
return page;
}

public int getSize() {
return size;
}

public long getTotal() {
return total;
}

public List<T> getContent() {
return content;
}

public boolean hasContent() {
return content != null && content.size() > 0;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -236,4 +236,55 @@ public void testRemoveItemWithError() throws Exception {

itemOpenApiService.removeItem(someAppId, someEnv, someCluster, someNamespace, someKey, someOperator);
}

@Test
public void testFindItemsByNamespace() throws Exception {
final int page = 0;
final int size = 50;
final ArgumentCaptor<HttpGet> request = ArgumentCaptor.forClass(HttpGet.class);

itemOpenApiService.findItemsByNamespace(someAppId, someEnv, someCluster, someNamespace, page, size);

verify(httpClient, times(1)).execute(request.capture());

HttpGet get = request.getValue();

assertEquals(String.format("%s/envs/%s/apps/%s/clusters/%s/namespaces/%s/items?size=%s&page=%s",
someBaseUrl, someEnv, someAppId, someCluster, someNamespace, size, page), get.getURI().toString());
}

@Test(expected = RuntimeException.class)
public void testFindItemsByNamespaceWithError() {
final int page = 0;
final int size = 50;

when(statusLine.getStatusCode()).thenReturn(400);

itemOpenApiService.findItemsByNamespace(someAppId, someEnv, someCluster, someNamespace, page, size);
}

@Test(expected = IllegalArgumentException.class)
public void testFindItemsByNamespaceWithPageNegativeError() {
final int page = -1;
final int size = 50;

itemOpenApiService.findItemsByNamespace(someAppId, someEnv, someCluster, someNamespace, page, size);
}

@Test(expected = IllegalArgumentException.class)
public void testFindItemsByNamespaceWithSizeNegativeError() {
final int page = 0;
final int size = -50;

itemOpenApiService.findItemsByNamespace(someAppId, someEnv, someCluster, someNamespace, page, size);
}

@Test(expected = IllegalArgumentException.class)
public void testFindItemsByNamespaceWithPageAndSizeAllNegativeError() {
final int page = -1;
final int size = -50;

itemOpenApiService.findItemsByNamespace(someAppId, someEnv, someCluster, someNamespace, page, size);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
package com.ctrip.framework.apollo.openapi.server.service;

import com.ctrip.framework.apollo.common.dto.ItemDTO;
import com.ctrip.framework.apollo.common.dto.PageDTO;
import com.ctrip.framework.apollo.openapi.api.ItemOpenApiService;
import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenPageDTO;
import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils;
import com.ctrip.framework.apollo.portal.environment.Env;
import com.ctrip.framework.apollo.portal.service.ItemService;
Expand Down Expand Up @@ -99,4 +101,14 @@ public void removeItem(String appId, String env, String clusterName, String name
ItemDTO toDeleteItem = this.itemService.loadItem(Env.valueOf(env), appId, clusterName, namespaceName, key);
this.itemService.deleteItem(Env.valueOf(env), toDeleteItem.getId(), operator);
}

@Override
public OpenPageDTO<OpenItemDTO> findItemsByNamespace(String appId, String env, String clusterName,
String namespaceName, int page, int size) {
PageDTO<OpenItemDTO> commonOpenItemDTOPage =
this.itemService.findItemsByNamespace(appId, Env.valueOf(env), clusterName, namespaceName, page, size);

return new OpenPageDTO<>(commonOpenItemDTOPage.getPage(), commonOpenItemDTOPage.getSize(),
commonOpenItemDTOPage.getTotal(), commonOpenItemDTOPage.getContent());
}
}
Loading

1 comment on commit aaa3561

@ksice
Copy link
Contributor

@ksice ksice commented on aaa3561 Jul 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are great!!!

Please sign in to comment.