Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(openapi): Add a new API to load items with pagination #4468

Merged
merged 7 commits into from
Jul 24, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ 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)
------------------
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 ([email protected])
*/
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;
mghio marked this conversation as resolved.
Show resolved Hide resolved
}

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);
}

mghio marked this conversation as resolved.
Show resolved Hide resolved
@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