diff --git a/.gitattributes b/.gitattributes index af19312d146..c2a655ad66c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ -text=auto \ No newline at end of file +text=auto +*.sh text eol=lf diff --git a/CHANGES.md b/CHANGES.md index 6f1726199d8..987a18af0b2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,9 @@ Apollo 1.10.0 * [remove ctrip profile](https://github.com/ctripcorp/apollo/pull/3920) * [Remove spring dependencies from internal code](https://github.com/apolloconfig/apollo/pull/3937) * [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) * [Support export/import configs](https://github.com/apolloconfig/apollo/pull/3947) diff --git a/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/controller/NamespaceController.java b/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/controller/NamespaceController.java index 70c44779730..2823ce5c082 100644 --- a/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/controller/NamespaceController.java +++ b/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/controller/NamespaceController.java @@ -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; @@ -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 findByItem(@RequestParam String itemKey, Pageable pageable) { + Page namespacePage = namespaceService.findByItem(itemKey, pageable); + + List 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, diff --git a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/repository/ItemRepository.java b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/repository/ItemRepository.java index 572569e2e7c..f8ff6894139 100644 --- a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/repository/ItemRepository.java +++ b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/repository/ItemRepository.java @@ -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; @@ -35,6 +37,8 @@ public interface ItemRepository extends PagingAndSortingRepository { List findByNamespaceIdAndDataChangeLastModifiedTimeGreaterThan(Long namespaceId, Date date); + Page findByKey(String key, Pageable pageable); + Item findFirst1ByNamespaceIdOrderByLineNumDesc(Long namespaceId); @Modifying diff --git a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/repository/NamespaceRepository.java b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/repository/NamespaceRepository.java index 29755bb636d..331d8cf31ef 100644 --- a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/repository/NamespaceRepository.java +++ b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/repository/NamespaceRepository.java @@ -24,6 +24,7 @@ import org.springframework.data.repository.PagingAndSortingRepository; import java.util.List; +import java.util.Set; public interface NamespaceRepository extends PagingAndSortingRepository { @@ -39,6 +40,8 @@ public interface NamespaceRepository extends PagingAndSortingRepository findByNamespaceName(String namespaceName, Pageable page); + List findByIdIn(Set namespaceIds); + int countByNamespaceNameAndAppIdNot(String namespaceName, String appId); } diff --git a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/ItemService.java b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/ItemService.java index 127f14e0c4d..39ade331f0c 100644 --- a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/ItemService.java +++ b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/ItemService.java @@ -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; @@ -138,6 +140,10 @@ public List findItemsModifiedAfterDate(long namespaceId, Date date) { return itemRepository.findByNamespaceIdAndDataChangeLastModifiedTimeGreaterThan(namespaceId, date); } + public Page findItemsByKey(String key, Pageable pageable) { + return itemRepository.findByKey(key, pageable); + } + @Transactional public Item save(Item entity) { checkItemKeyLength(entity.getKey()); diff --git a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/NamespaceService.java b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/NamespaceService.java index ba1f8e1f685..54e5d805799 100644 --- a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/NamespaceService.java +++ b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/NamespaceService.java @@ -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; @@ -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 findByItem(String itemKey, Pageable pageable) { + Page items = itemService.findItemsByKey(itemKey, pageable); + + if (!items.hasContent()) { + return Page.empty(); + } + + Set 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) { diff --git a/apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/StringUtils.java b/apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/StringUtils.java index 34e28bdf5d7..927c97396fd 100644 --- a/apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/StringUtils.java +++ b/apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/StringUtils.java @@ -344,44 +344,4 @@ public interface StringFormatter { String format(T obj); } - public static String join(Collection collection, String separator) { - return join(collection, separator, new StringFormatter() { - @Override - public String format(T obj) { - return obj.toString(); - } - }); - } - - public static String join(Collection collection, String separator, - StringFormatter formatter) { - Iterator iterator = collection.iterator(); - // handle null, zero and one elements before building a buffer - if (iterator == null) { - return null; - } - if (!iterator.hasNext()) { - return EMPTY; - } - T first = iterator.next(); - if (!iterator.hasNext()) { - return first == null ? "" : formatter.format(first); - } - - // two or more elements - StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small - if (first != null) { - buf.append(formatter.format(first)); - } - - while (iterator.hasNext()) { - buf.append(separator); - T obj = iterator.next(); - if (obj != null) { - buf.append(formatter.format(obj)); - } - } - - return buf.toString(); - } } diff --git a/apollo-core/src/test/java/com/ctrip/framework/apollo/core/utils/StringUtilsTest.java b/apollo-core/src/test/java/com/ctrip/framework/apollo/core/utils/StringUtilsTest.java index 8d9903a5479..5bcd7748c56 100644 --- a/apollo-core/src/test/java/com/ctrip/framework/apollo/core/utils/StringUtilsTest.java +++ b/apollo-core/src/test/java/com/ctrip/framework/apollo/core/utils/StringUtilsTest.java @@ -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,19 +67,6 @@ public void testIsNumeric() { Assert.assertTrue(StringUtils.isNumeric("1")); } - @Test - public void testJoin() { - Assert.assertEquals("", StringUtils.join(new ArrayList(), "1a 2b 3c")); - - ArrayList collection = new ArrayList(); - collection.add(null); - Assert.assertEquals("", StringUtils.join(collection, "1a 2b 3c")); - - collection = new ArrayList(); - collection.add(-2_147_483_648); - Assert.assertEquals("-2147483648", StringUtils.join(collection, "1a 2b 3c")); - } - @Test public void testStartsWithIgnoreCase() { Assert.assertFalse(StringUtils.startsWithIgnoreCase("A1B2C3", "BAZ")); diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/AppOpenApiService.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/AppOpenApiService.java new file mode 100644 index 00000000000..d6af21c7051 --- /dev/null +++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/AppOpenApiService.java @@ -0,0 +1,35 @@ +/* + * 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.openapi.api; + +import com.ctrip.framework.apollo.openapi.dto.OpenAppDTO; +import com.ctrip.framework.apollo.openapi.dto.OpenEnvClusterDTO; +import java.util.List; + +/** + * @author wxq + */ +public interface AppOpenApiService { + + List getEnvClusterInfo(String appId); + + List getAllApps(); + + List getAppsInfo(List appIds); + + List getAuthorizedApps(); +} diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/ClusterOpenApiService.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/ClusterOpenApiService.java new file mode 100644 index 00000000000..0bf0c8f37ea --- /dev/null +++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/ClusterOpenApiService.java @@ -0,0 +1,29 @@ +/* + * 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.openapi.api; + +import com.ctrip.framework.apollo.openapi.dto.OpenClusterDTO; + +/** + * @author wxq + */ +public interface ClusterOpenApiService { + + OpenClusterDTO getCluster(String appId, String env, String clusterName); + + OpenClusterDTO createCluster(String env, OpenClusterDTO openClusterDTO); +} diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/ItemOpenApiService.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/ItemOpenApiService.java new file mode 100644 index 00000000000..7c627e6d549 --- /dev/null +++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/ItemOpenApiService.java @@ -0,0 +1,40 @@ +/* + * 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.openapi.api; + +import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO; + +/** + * @author wxq + */ +public interface ItemOpenApiService { + + OpenItemDTO getItem(String appId, String env, String clusterName, String namespaceName, + String key); + + OpenItemDTO createItem(String appId, String env, String clusterName, String namespaceName, + OpenItemDTO itemDTO); + + void updateItem(String appId, String env, String clusterName, String namespaceName, + OpenItemDTO itemDTO); + + void createOrUpdateItem(String appId, String env, String clusterName, String namespaceName, + OpenItemDTO itemDTO); + + void removeItem(String appId, String env, String clusterName, String namespaceName, String key, + String operator); +} diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/NamespaceOpenApiService.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/NamespaceOpenApiService.java new file mode 100644 index 00000000000..99ee03cff3f --- /dev/null +++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/NamespaceOpenApiService.java @@ -0,0 +1,37 @@ +/* + * 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.openapi.api; + +import com.ctrip.framework.apollo.openapi.dto.OpenAppNamespaceDTO; +import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO; +import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceLockDTO; +import java.util.List; + +/** + * @author wxq + */ +public interface NamespaceOpenApiService { + + OpenNamespaceDTO getNamespace(String appId, String env, String clusterName, String namespaceName); + + List getNamespaces(String appId, String env, String clusterName); + + OpenAppNamespaceDTO createAppNamespace(OpenAppNamespaceDTO appNamespaceDTO); + + OpenNamespaceLockDTO getNamespaceLock(String appId, String env, String clusterName, + String namespaceName); +} diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/ReleaseOpenApiService.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/ReleaseOpenApiService.java new file mode 100644 index 00000000000..bb646cdcba8 --- /dev/null +++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/api/ReleaseOpenApiService.java @@ -0,0 +1,35 @@ +/* + * 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.openapi.api; + +import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO; +import com.ctrip.framework.apollo.openapi.dto.OpenReleaseDTO; + +/** + * @author wxq + */ +public interface ReleaseOpenApiService { + + OpenReleaseDTO publishNamespace(String appId, String env, String clusterName, + String namespaceName, + NamespaceReleaseDTO releaseDTO); + + OpenReleaseDTO getLatestActiveRelease(String appId, String env, String clusterName, + String namespaceName); + + void rollbackRelease(String env, long releaseId, String operator); +} diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/ApolloOpenApiClient.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/ApolloOpenApiClient.java index 05a6c3a9945..7a32047c368 100644 --- a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/ApolloOpenApiClient.java +++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/ApolloOpenApiClient.java @@ -84,7 +84,7 @@ public List getEnvClusterInfo(String appId) { * Get all App information */ public List getAllApps() { - return appService.getAppsInfo(null); + return appService.getAllApps(); } /** diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/exception/ApolloOpenApiException.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/exception/ApolloOpenApiException.java index 0f89855175a..a5f343fea15 100644 --- a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/exception/ApolloOpenApiException.java +++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/exception/ApolloOpenApiException.java @@ -17,8 +17,7 @@ package com.ctrip.framework.apollo.openapi.client.exception; public class ApolloOpenApiException extends RuntimeException { - - private int status; + private final int status; public ApolloOpenApiException(int status, String reason, String message) { super(String.format("Request to apollo open api failed, status code: %d, reason: %s, message: %s", status, reason, diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/AbstractOpenApiService.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/AbstractOpenApiService.java index 889a23f3e8e..96114f31035 100644 --- a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/AbstractOpenApiService.java +++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/AbstractOpenApiService.java @@ -17,30 +17,21 @@ package com.ctrip.framework.apollo.openapi.client.service; import com.ctrip.framework.apollo.openapi.client.exception.ApolloOpenApiException; +import com.ctrip.framework.apollo.openapi.client.url.OpenApiPathBuilder; import com.google.common.base.Preconditions; import com.google.common.base.Strings; -import com.google.common.escape.Escaper; -import com.google.common.net.UrlEscapers; import com.google.gson.Gson; -import java.io.IOException; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.methods.*; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; -abstract class AbstractOpenApiService { - private static final Escaper pathEscaper = UrlEscapers.urlPathSegmentEscaper(); - private static final Escaper queryParamEscaper = UrlEscapers.urlFormParameterEscaper(); +import java.io.IOException; +abstract class AbstractOpenApiService { private final String baseUrl; protected final CloseableHttpClient client; @@ -52,38 +43,30 @@ abstract class AbstractOpenApiService { this.gson = gson; } - protected CloseableHttpResponse get(String path) throws IOException { - HttpGet get = new HttpGet(String.format("%s/%s", baseUrl, path)); + protected CloseableHttpResponse get(OpenApiPathBuilder path) throws IOException { + HttpGet get = new HttpGet(path.buildPath(baseUrl)); return execute(get); } - protected CloseableHttpResponse post(String path, Object entity) throws IOException { - HttpPost post = new HttpPost(String.format("%s/%s", baseUrl, path)); + protected CloseableHttpResponse post(OpenApiPathBuilder path, Object entity) throws IOException { + HttpPost post = new HttpPost(path.buildPath(baseUrl)); return execute(post, entity); } - protected CloseableHttpResponse put(String path, Object entity) throws IOException { - HttpPut put = new HttpPut(String.format("%s/%s", baseUrl, path)); + protected CloseableHttpResponse put(OpenApiPathBuilder path, Object entity) throws IOException { + HttpPut put = new HttpPut(path.buildPath(baseUrl)); return execute(put, entity); } - protected CloseableHttpResponse delete(String path) throws IOException { - HttpDelete delete = new HttpDelete(String.format("%s/%s", baseUrl, path)); + protected CloseableHttpResponse delete(OpenApiPathBuilder path) throws IOException { + HttpDelete delete = new HttpDelete(path.buildPath(baseUrl)); return execute(delete); } - protected String escapePath(String path) { - return pathEscaper.escape(path); - } - - protected String escapeParam(String param) { - return queryParamEscaper.escape(param); - } - private CloseableHttpResponse execute(HttpEntityEnclosingRequestBase requestBase, Object entity) throws IOException { requestBase.setEntity(new StringEntity(gson.toJson(entity), ContentType.APPLICATION_JSON)); @@ -98,7 +81,6 @@ private CloseableHttpResponse execute(HttpUriRequest request) throws IOException return response; } - private void checkHttpResponseStatus(HttpResponse response) { if (response.getStatusLine().getStatusCode() == 200) { return; diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/AppOpenApiService.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/AppOpenApiService.java index ae0848ce799..72aa45fc99e 100644 --- a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/AppOpenApiService.java +++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/AppOpenApiService.java @@ -16,6 +16,7 @@ */ package com.ctrip.framework.apollo.openapi.client.service; +import com.ctrip.framework.apollo.openapi.client.url.OpenApiPathBuilder; import com.ctrip.framework.apollo.openapi.dto.OpenAppDTO; import com.ctrip.framework.apollo.openapi.dto.OpenEnvClusterDTO; import com.google.common.base.Joiner; @@ -27,7 +28,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; -public class AppOpenApiService extends AbstractOpenApiService { +public class AppOpenApiService extends AbstractOpenApiService implements + com.ctrip.framework.apollo.openapi.api.AppOpenApiService { private static final Type OPEN_ENV_CLUSTER_DTO_LIST_TYPE = new TypeToken>() { }.getType(); private static final Type OPEN_APP_DTO_LIST_TYPE = new TypeToken>() { @@ -37,36 +39,49 @@ public AppOpenApiService(CloseableHttpClient client, String baseUrl, Gson gson) super(client, baseUrl, gson); } + @Override public List getEnvClusterInfo(String appId) { checkNotEmpty(appId, "App id"); - String path = String.format("apps/%s/envclusters", escapePath(appId)); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .appsPathVal(appId) + .customResource("envclusters"); - try (CloseableHttpResponse response = get(path)) { + try (CloseableHttpResponse response = get(pathBuilder)) { return gson.fromJson(EntityUtils.toString(response.getEntity()), OPEN_ENV_CLUSTER_DTO_LIST_TYPE); } catch (Throwable ex) { throw new RuntimeException(String.format("Load env cluster information for appId: %s failed", appId), ex); } } + @Override + public List getAllApps() { + return this.getAppsInfo(null); + } + + @Override public List getAppsInfo(List appIds) { - String path = "apps"; + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .customResource("apps"); if (appIds != null && !appIds.isEmpty()) { String param = Joiner.on(",").join(appIds); - path = String.format("apps?appIds=%s", escapeParam(param)); + pathBuilder.addParam("appIds", param); } - try (CloseableHttpResponse response = get(path)) { + try (CloseableHttpResponse response = get(pathBuilder)) { return gson.fromJson(EntityUtils.toString(response.getEntity()), OPEN_APP_DTO_LIST_TYPE); } catch (Throwable ex) { throw new RuntimeException(String.format("Load app information for appIds: %s failed", appIds), ex); } } + @Override public List getAuthorizedApps() { - String path = "apps/authorized"; - try(CloseableHttpResponse response = this.get(path)) { + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .customResource("apps/authorized"); + + try(CloseableHttpResponse response = this.get(pathBuilder)) { return gson.fromJson(EntityUtils.toString(response.getEntity()), OPEN_APP_DTO_LIST_TYPE); } catch (Throwable ex) { throw new RuntimeException("Load authorized apps failed", ex); diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/ClusterOpenApiService.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/ClusterOpenApiService.java index 02695f9345c..17a5d619027 100644 --- a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/ClusterOpenApiService.java +++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/ClusterOpenApiService.java @@ -17,6 +17,7 @@ package com.ctrip.framework.apollo.openapi.client.service; import com.ctrip.framework.apollo.core.ConfigConsts; +import com.ctrip.framework.apollo.openapi.client.url.OpenApiPathBuilder; import com.ctrip.framework.apollo.openapi.dto.OpenClusterDTO; import com.google.common.base.Strings; import com.google.gson.Gson; @@ -24,12 +25,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; -public class ClusterOpenApiService extends AbstractOpenApiService { +public class ClusterOpenApiService extends AbstractOpenApiService implements + com.ctrip.framework.apollo.openapi.api.ClusterOpenApiService { public ClusterOpenApiService(CloseableHttpClient client, String baseUrl, Gson gson) { super(client, baseUrl, gson); } + @Override public OpenClusterDTO getCluster(String appId, String env, String clusterName) { checkNotEmpty(appId, "App id"); checkNotEmpty(env, "Env"); @@ -38,10 +41,12 @@ public OpenClusterDTO getCluster(String appId, String env, String clusterName) { clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT; } - String path = String.format("envs/%s/apps/%s/clusters/%s", escapePath(env), escapePath(appId), - escapePath(clusterName)); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName); - try (CloseableHttpResponse response = get(path)) { + try (CloseableHttpResponse response = get(pathBuilder)) { return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenClusterDTO.class); } catch (Throwable ex) { throw new RuntimeException(String @@ -49,15 +54,19 @@ public OpenClusterDTO getCluster(String appId, String env, String clusterName) { } } + @Override public OpenClusterDTO createCluster(String env, OpenClusterDTO openClusterDTO) { checkNotEmpty(openClusterDTO.getAppId(), "App id"); checkNotEmpty(env, "Env"); checkNotEmpty(openClusterDTO.getName(), "Cluster name"); checkNotEmpty(openClusterDTO.getDataChangeCreatedBy(), "Created by"); - String path = String.format("envs/%s/apps/%s/clusters", escapePath(env), escapePath(openClusterDTO.getAppId())); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(openClusterDTO.getAppId()) + .customResource("clusters"); - try (CloseableHttpResponse response = post(path, openClusterDTO)) { + try (CloseableHttpResponse response = post(pathBuilder, openClusterDTO)) { return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenClusterDTO.class); } catch (Throwable ex) { throw new RuntimeException(String diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/ItemOpenApiService.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/ItemOpenApiService.java index 37a378e11d4..02d22b9cb27 100644 --- a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/ItemOpenApiService.java +++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/ItemOpenApiService.java @@ -18,6 +18,7 @@ import com.ctrip.framework.apollo.core.ConfigConsts; 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.google.common.base.Strings; import com.google.gson.Gson; @@ -25,12 +26,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; -public class ItemOpenApiService extends AbstractOpenApiService { +public class ItemOpenApiService extends AbstractOpenApiService implements + com.ctrip.framework.apollo.openapi.api.ItemOpenApiService { public ItemOpenApiService(CloseableHttpClient client, String baseUrl, Gson gson) { super(client, baseUrl, gson); } + @Override public OpenItemDTO getItem(String appId, String env, String clusterName, String namespaceName, String key) { if (Strings.isNullOrEmpty(clusterName)) { clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT; @@ -43,10 +46,14 @@ public OpenItemDTO getItem(String appId, String env, String clusterName, String checkNotEmpty(env, "Env"); checkNotEmpty(key, "Item key"); - String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/items/%s", - escapePath(env), escapePath(appId), escapePath(clusterName), escapePath(namespaceName), escapePath(key)); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .itemsPathVal(key); - try (CloseableHttpResponse response = get(path)) { + try (CloseableHttpResponse response = get(pathBuilder)) { return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenItemDTO.class); } catch (Throwable ex) { // return null if item doesn't exist @@ -59,6 +66,7 @@ public OpenItemDTO getItem(String appId, String env, String clusterName, String } } + @Override public OpenItemDTO createItem(String appId, String env, String clusterName, String namespaceName, OpenItemDTO itemDTO) { if (Strings.isNullOrEmpty(clusterName)) { clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT; @@ -72,10 +80,14 @@ public OpenItemDTO createItem(String appId, String env, String clusterName, Stri checkNotEmpty(itemDTO.getKey(), "Item key"); checkNotEmpty(itemDTO.getDataChangeCreatedBy(), "Item created by"); - String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/items", - escapePath(env), escapePath(appId), escapePath(clusterName), escapePath(namespaceName)); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .customResource("items"); - try (CloseableHttpResponse response = post(path, itemDTO)) { + try (CloseableHttpResponse response = post(pathBuilder, itemDTO)) { return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenItemDTO.class); } catch (Throwable ex) { throw new RuntimeException(String @@ -84,6 +96,7 @@ public OpenItemDTO createItem(String appId, String env, String clusterName, Stri } } + @Override public void updateItem(String appId, String env, String clusterName, String namespaceName, OpenItemDTO itemDTO) { if (Strings.isNullOrEmpty(clusterName)) { clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT; @@ -97,11 +110,14 @@ public void updateItem(String appId, String env, String clusterName, String name checkNotEmpty(itemDTO.getKey(), "Item key"); checkNotEmpty(itemDTO.getDataChangeLastModifiedBy(), "Item modified by"); - String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/items/%s", - escapePath(env), escapePath(appId), escapePath(clusterName), escapePath(namespaceName), - escapePath(itemDTO.getKey())); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .itemsPathVal(itemDTO.getKey()); - try (CloseableHttpResponse ignored = put(path, itemDTO)) { + try (CloseableHttpResponse ignored = put(pathBuilder, itemDTO)) { } catch (Throwable ex) { throw new RuntimeException(String .format("Update item: %s for appId: %s, cluster: %s, namespace: %s in env: %s failed", itemDTO.getKey(), @@ -109,6 +125,7 @@ public void updateItem(String appId, String env, String clusterName, String name } } + @Override public void createOrUpdateItem(String appId, String env, String clusterName, String namespaceName, OpenItemDTO itemDTO) { if (Strings.isNullOrEmpty(clusterName)) { clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT; @@ -126,11 +143,15 @@ public void createOrUpdateItem(String appId, String env, String clusterName, Str itemDTO.setDataChangeLastModifiedBy(itemDTO.getDataChangeCreatedBy()); } - String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/items/%s?createIfNotExists=true", - escapePath(env), escapePath(appId), escapePath(clusterName), escapePath(namespaceName), - escapePath(itemDTO.getKey())); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .itemsPathVal(itemDTO.getKey()) + .addParam("createIfNotExists", "true"); - try (CloseableHttpResponse ignored = put(path, itemDTO)) { + try (CloseableHttpResponse ignored = put(pathBuilder, itemDTO)) { } catch (Throwable ex) { throw new RuntimeException(String .format("CreateOrUpdate item: %s for appId: %s, cluster: %s, namespace: %s in env: %s failed", itemDTO.getKey(), @@ -138,6 +159,7 @@ public void createOrUpdateItem(String appId, String env, String clusterName, Str } } + @Override public void removeItem(String appId, String env, String clusterName, String namespaceName, String key, String operator) { if (Strings.isNullOrEmpty(clusterName)) { clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT; @@ -151,11 +173,15 @@ public void removeItem(String appId, String env, String clusterName, String name checkNotEmpty(key, "Item key"); checkNotEmpty(operator, "Operator"); - String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/items/%s?operator=%s", - escapePath(env), escapePath(appId), escapePath(clusterName), escapePath(namespaceName), escapePath(key), - escapeParam(operator)); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .itemsPathVal(key) + .addParam("operator", operator); - try (CloseableHttpResponse ignored = delete(path)) { + try (CloseableHttpResponse ignored = delete(pathBuilder)) { } catch (Throwable ex) { throw new RuntimeException(String .format("Remove item: %s for appId: %s, cluster: %s, namespace: %s in env: %s failed", key, appId, diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/NamespaceOpenApiService.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/NamespaceOpenApiService.java index e823f065089..640de69e9d1 100644 --- a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/NamespaceOpenApiService.java +++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/NamespaceOpenApiService.java @@ -18,6 +18,7 @@ import com.ctrip.framework.apollo.core.ConfigConsts; import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; +import com.ctrip.framework.apollo.openapi.client.url.OpenApiPathBuilder; import com.ctrip.framework.apollo.openapi.dto.OpenAppNamespaceDTO; import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO; import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceLockDTO; @@ -30,7 +31,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; -public class NamespaceOpenApiService extends AbstractOpenApiService { +public class NamespaceOpenApiService extends AbstractOpenApiService implements + com.ctrip.framework.apollo.openapi.api.NamespaceOpenApiService { private static final Type OPEN_NAMESPACE_DTO_LIST_TYPE = new TypeToken>() { }.getType(); @@ -38,6 +40,7 @@ public NamespaceOpenApiService(CloseableHttpClient client, String baseUrl, Gson super(client, baseUrl, gson); } + @Override public OpenNamespaceDTO getNamespace(String appId, String env, String clusterName, String namespaceName) { if (Strings.isNullOrEmpty(clusterName)) { clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT; @@ -49,10 +52,13 @@ public OpenNamespaceDTO getNamespace(String appId, String env, String clusterNam checkNotEmpty(appId, "App id"); checkNotEmpty(env, "Env"); - String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s", escapePath(env), escapePath(appId), - escapePath(clusterName), escapePath(namespaceName)); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName); - try (CloseableHttpResponse response = get(path)) { + try (CloseableHttpResponse response = get(pathBuilder)) { return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenNamespaceDTO.class); } catch (Throwable ex) { throw new RuntimeException(String @@ -61,6 +67,7 @@ public OpenNamespaceDTO getNamespace(String appId, String env, String clusterNam } } + @Override public List getNamespaces(String appId, String env, String clusterName) { if (Strings.isNullOrEmpty(clusterName)) { clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT; @@ -69,10 +76,13 @@ public List getNamespaces(String appId, String env, String clu checkNotEmpty(appId, "App id"); checkNotEmpty(env, "Env"); - String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces", escapePath(env), escapePath(appId), - escapePath(clusterName)); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .customResource("namespaces"); - try (CloseableHttpResponse response = get(path)) { + try (CloseableHttpResponse response = get(pathBuilder)) { return gson.fromJson(EntityUtils.toString(response.getEntity()), OPEN_NAMESPACE_DTO_LIST_TYPE); } catch (Throwable ex) { throw new RuntimeException(String @@ -80,6 +90,7 @@ public List getNamespaces(String appId, String env, String clu } } + @Override public OpenAppNamespaceDTO createAppNamespace(OpenAppNamespaceDTO appNamespaceDTO) { checkNotEmpty(appNamespaceDTO.getAppId(), "App id"); checkNotEmpty(appNamespaceDTO.getName(), "Name"); @@ -89,9 +100,11 @@ public OpenAppNamespaceDTO createAppNamespace(OpenAppNamespaceDTO appNamespaceDT appNamespaceDTO.setFormat(ConfigFileFormat.Properties.getValue()); } - String path = String.format("apps/%s/appnamespaces", escapePath(appNamespaceDTO.getAppId())); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .appsPathVal(appNamespaceDTO.getAppId()) + .customResource("appnamespaces"); - try (CloseableHttpResponse response = post(path, appNamespaceDTO)) { + try (CloseableHttpResponse response = post(pathBuilder, appNamespaceDTO)) { return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenAppNamespaceDTO.class); } catch (Throwable ex) { throw new RuntimeException(String @@ -100,6 +113,7 @@ public OpenAppNamespaceDTO createAppNamespace(OpenAppNamespaceDTO appNamespaceDT } } + @Override public OpenNamespaceLockDTO getNamespaceLock(String appId, String env, String clusterName, String namespaceName) { if (Strings.isNullOrEmpty(clusterName)) { clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT; @@ -111,10 +125,14 @@ public OpenNamespaceLockDTO getNamespaceLock(String appId, String env, String cl checkNotEmpty(appId, "App id"); checkNotEmpty(env, "Env"); - String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/lock", escapePath(env), escapePath(appId), - escapePath(clusterName), escapePath(namespaceName)); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .customResource("lock"); - try (CloseableHttpResponse response = get(path)) { + try (CloseableHttpResponse response = get(pathBuilder)) { return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenNamespaceLockDTO.class); } catch (Throwable ex) { throw new RuntimeException(String diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/ReleaseOpenApiService.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/ReleaseOpenApiService.java index 5b24321a468..eb58996d44f 100644 --- a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/ReleaseOpenApiService.java +++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/ReleaseOpenApiService.java @@ -17,6 +17,7 @@ package com.ctrip.framework.apollo.openapi.client.service; import com.ctrip.framework.apollo.core.ConfigConsts; +import com.ctrip.framework.apollo.openapi.client.url.OpenApiPathBuilder; import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO; import com.ctrip.framework.apollo.openapi.dto.OpenReleaseDTO; import com.google.common.base.Strings; @@ -25,12 +26,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; -public class ReleaseOpenApiService extends AbstractOpenApiService { +public class ReleaseOpenApiService extends AbstractOpenApiService implements + com.ctrip.framework.apollo.openapi.api.ReleaseOpenApiService { public ReleaseOpenApiService(CloseableHttpClient client, String baseUrl, Gson gson) { super(client, baseUrl, gson); } + @Override public OpenReleaseDTO publishNamespace(String appId, String env, String clusterName, String namespaceName, NamespaceReleaseDTO releaseDTO) { if (Strings.isNullOrEmpty(clusterName)) { @@ -45,10 +48,14 @@ public OpenReleaseDTO publishNamespace(String appId, String env, String clusterN checkNotEmpty(releaseDTO.getReleaseTitle(), "Release title"); checkNotEmpty(releaseDTO.getReleasedBy(), "Released by"); - String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/releases", - escapePath(env), escapePath(appId), escapePath(clusterName), escapePath(namespaceName)); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .customResource("releases"); - try (CloseableHttpResponse response = post(path, releaseDTO)) { + try (CloseableHttpResponse response = post(pathBuilder, releaseDTO)) { return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenReleaseDTO.class); } catch (Throwable ex) { throw new RuntimeException(String @@ -57,6 +64,7 @@ public OpenReleaseDTO publishNamespace(String appId, String env, String clusterN } } + @Override public OpenReleaseDTO getLatestActiveRelease(String appId, String env, String clusterName, String namespaceName) { if (Strings.isNullOrEmpty(clusterName)) { clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT; @@ -68,10 +76,14 @@ public OpenReleaseDTO getLatestActiveRelease(String appId, String env, String cl checkNotEmpty(appId, "App id"); checkNotEmpty(env, "Env"); - String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/releases/latest", - escapePath(env), escapePath(appId), escapePath(clusterName), escapePath(namespaceName)); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .releasesPathVal("latest"); - try (CloseableHttpResponse response = get(path)) { + try (CloseableHttpResponse response = get(pathBuilder)) { return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenReleaseDTO.class); } catch (Throwable ex) { throw new RuntimeException(String @@ -80,14 +92,18 @@ public OpenReleaseDTO getLatestActiveRelease(String appId, String env, String cl } } + @Override public void rollbackRelease(String env, long releaseId, String operator) { checkNotEmpty(env, "Env"); checkNotEmpty(operator, "Operator"); - String path = String.format("envs/%s/releases/%s/rollback?operator=%s", escapePath(env), releaseId, - escapeParam(operator)); + OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .releasesPathVal(String.valueOf(releaseId)) + .customResource("rollback") + .addParam("operator", operator); - try (CloseableHttpResponse ignored = put(path, null)) { + try (CloseableHttpResponse ignored = put(pathBuilder, null)) { } catch (Throwable ex) { throw new RuntimeException(String.format("Rollback release: %s in env: %s failed", releaseId, env), ex); } diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/url/OpenApiPathBuilder.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/url/OpenApiPathBuilder.java new file mode 100644 index 00000000000..f457e4fe52c --- /dev/null +++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/url/OpenApiPathBuilder.java @@ -0,0 +1,153 @@ +/* + * 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.openapi.client.url; + +import com.google.common.base.Joiner; +import com.google.common.base.Strings; +import com.google.common.escape.Escaper; +import com.google.common.net.UrlEscapers; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class OpenApiPathBuilder { + + private static final String ENV_PATH = "env"; + private static final String ENVS_PATH = "envs"; + private static final String APPS_PATH = "apps"; + private static final String CLUSTERS_PATH = "clusters"; + private static final String NAMESPACES_PATH = "namespaces"; + private static final String ITEMS_PATH = "items"; + private static final String RELEASE_PATH = "releases"; + + private final static List SORTED_PATH_KEYS = Arrays.asList(ENVS_PATH, ENV_PATH, APPS_PATH, + CLUSTERS_PATH, + NAMESPACES_PATH, ITEMS_PATH, RELEASE_PATH); + + private static final Escaper PATH_ESCAPER = UrlEscapers.urlPathSegmentEscaper(); + private static final Escaper QUERY_PARAM_ESCAPER = UrlEscapers.urlFormParameterEscaper(); + private static final Joiner PATH_JOIN = Joiner.on("/"); + + private final Map pathVariable; + private final Map params; + + private String customResource; + + public static OpenApiPathBuilder newBuilder() { + return new OpenApiPathBuilder(); + } + + private OpenApiPathBuilder() { + this.pathVariable = new HashMap<>(); + this.params = new HashMap<>(); + } + + public OpenApiPathBuilder envPathVal(String env) { + pathVariable.put(ENV_PATH, escapePath(env)); + return this; + } + + public OpenApiPathBuilder envsPathVal(String envs) { + pathVariable.put(ENVS_PATH, escapePath(envs)); + return this; + } + + public OpenApiPathBuilder appsPathVal(String apps) { + pathVariable.put(APPS_PATH, escapePath(apps)); + return this; + } + + public OpenApiPathBuilder clustersPathVal(String clusters) { + pathVariable.put(CLUSTERS_PATH, escapePath(clusters)); + return this; + } + + public OpenApiPathBuilder namespacesPathVal(String namespaces) { + pathVariable.put(NAMESPACES_PATH, escapePath(namespaces)); + return this; + } + + public OpenApiPathBuilder itemsPathVal(String items) { + pathVariable.put(ITEMS_PATH, escapePath(items)); + return this; + } + + public OpenApiPathBuilder releasesPathVal(String releases) { + pathVariable.put(RELEASE_PATH, escapePath(releases)); + return this; + } + + public OpenApiPathBuilder customResource(String customResource) { + this.customResource = customResource; + return this; + } + + public OpenApiPathBuilder addParam(String key, Object value) { + if (Strings.isNullOrEmpty(key)) { + throw new IllegalArgumentException("Param key should not be null or empty"); + } + this.params.put(key, escapeParam(String.valueOf(value))); + return this; + } + + public String buildPath(String baseUrl) { + if (Strings.isNullOrEmpty(baseUrl)) { + throw new IllegalArgumentException("Base url should not be null or empty"); + } + List parts = new ArrayList<>(); + parts.add(baseUrl); + + for (String k : SORTED_PATH_KEYS) { + if (pathVariable.containsKey(k)) { + parts.add(k); + String v = pathVariable.get(k); + if (!Strings.isNullOrEmpty(v)) { + parts.add(v); + } + } + } + + if (!Strings.isNullOrEmpty(this.customResource)) { + parts.add(this.customResource); + } + + String path = PATH_JOIN.join(parts); + + if (!params.isEmpty()) { + StringBuilder sb = new StringBuilder(); + for (Map.Entry kv : params.entrySet()) { + if (sb.length() > 0) { + sb.append("&"); + } + sb.append(kv.getKey()).append("=").append(kv.getValue()); + } + path += "?" + sb; + } + return path; + } + + protected String escapePath(String path) { + return PATH_ESCAPER.escape(path); + } + + protected String escapeParam(String param) { + return QUERY_PARAM_ESCAPER.escape(param); + } + +} diff --git a/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/url/OpenApiPathBuilderTest.java b/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/url/OpenApiPathBuilderTest.java new file mode 100644 index 00000000000..adad05aa545 --- /dev/null +++ b/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/url/OpenApiPathBuilderTest.java @@ -0,0 +1,238 @@ +/* + * 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.openapi.client.url; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class OpenApiPathBuilderTest { + + @Test + public void testBuildPath() { + String baseURL = "http://localhost"; + OpenApiPathBuilder tools = OpenApiPathBuilder.newBuilder(); + String path, expected, actual; + String env = "test"; + String appId = "appid-1001"; + String clusterName = "cluster-1001"; + String namespaceName = "application.yml"; + String key = "spring.profile"; + String operator = "junit"; + long releaseId = 1L; + + // AppOpenApiService path check + + path = String.format("apps/%s/envclusters", tools.escapePath(appId)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .appsPathVal(appId) + .customResource("envclusters") + .buildPath(baseURL); + assertEquals(expected, actual); + + String param = "1,2,3"; + path = String.format("apps?appIds=%s", tools.escapeParam(param)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .customResource("apps") + .addParam("appIds", param) + .buildPath(baseURL); + assertEquals(expected, actual); + + path = "apps/authorized"; + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .customResource("apps/authorized") + .buildPath(baseURL); + assertEquals(expected, actual); + + // ClusterOpenApiService path check + + path = String.format("envs/%s/apps/%s/clusters/%s", tools.escapePath(env), + tools.escapePath(appId), + tools.escapePath(clusterName)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .buildPath(baseURL); + assertEquals(expected, actual); + + path = String.format("envs/%s/apps/%s/clusters", tools.escapePath(env), + tools.escapePath(appId)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .customResource("clusters") + .buildPath(baseURL); + assertEquals(expected, actual); + + // ItemOpenApiService path check + + path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/items/%s", + tools.escapePath(env), tools.escapePath(appId), tools.escapePath(clusterName), + tools.escapePath(namespaceName), tools.escapePath(key)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .itemsPathVal(key) + .buildPath(baseURL); + assertEquals(expected, actual); + + path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/items", + tools.escapePath(env), tools.escapePath(appId), tools.escapePath(clusterName), + tools.escapePath(namespaceName)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .customResource("items") + .buildPath(baseURL); + assertEquals(expected, actual); + + path = String.format( + "envs/%s/apps/%s/clusters/%s/namespaces/%s/items/%s?createIfNotExists=true", + tools.escapePath(env), tools.escapePath(appId), tools.escapePath(clusterName), + tools.escapePath(namespaceName), tools.escapePath(key)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .itemsPathVal(key) + .addParam("createIfNotExists", "true") + .buildPath(baseURL); + assertEquals(expected, actual); + + path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/items/%s?operator=%s", + tools.escapePath(env), tools.escapePath(appId), tools.escapePath(clusterName), + tools.escapePath(namespaceName), tools.escapePath(key), tools.escapeParam(operator)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .itemsPathVal(key) + .addParam("operator", operator) + .buildPath(baseURL); + assertEquals(expected, actual); + + // NamespaceOpenApiService path check + + path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s", tools.escapePath(env), + tools.escapePath(appId), tools.escapePath(clusterName), tools.escapePath(namespaceName)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .buildPath(baseURL); + assertEquals(expected, actual); + + path = String.format("envs/%s/apps/%s/clusters/%s/namespaces", tools.escapePath(env), + tools.escapePath(appId), tools.escapePath(clusterName)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .customResource("namespaces") + .buildPath(baseURL); + assertEquals(expected, actual); + + path = String.format("apps/%s/appnamespaces", tools.escapePath(appId)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .appsPathVal(appId) + .customResource("appnamespaces") + .buildPath(baseURL); + assertEquals(expected, actual); + + path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/lock", tools.escapePath(env), + tools.escapePath(appId), tools.escapePath(clusterName), tools.escapePath(namespaceName)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .customResource("lock") + .buildPath(baseURL); + assertEquals(expected, actual); + + // ReleaseOpenApiService path check + + path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/releases", + tools.escapePath(env), tools.escapePath(appId), tools.escapePath(clusterName), + tools.escapePath(namespaceName)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .customResource("releases") + .buildPath(baseURL); + assertEquals(expected, actual); + + path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/releases/latest", + tools.escapePath(env), tools.escapePath(appId), tools.escapePath(clusterName), + tools.escapePath(namespaceName)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .appsPathVal(appId) + .clustersPathVal(clusterName) + .namespacesPathVal(namespaceName) + .releasesPathVal("latest") + .buildPath(baseURL); + assertEquals(expected, actual); + + path = String.format("envs/%s/releases/%s/rollback?operator=%s", tools.escapePath(env), + releaseId, + tools.escapeParam(operator)); + expected = String.format("%s/%s", baseURL, path); + actual = OpenApiPathBuilder.newBuilder() + .envsPathVal(env) + .releasesPathVal(String.valueOf(releaseId)) + .customResource("rollback") + .addParam("operator", operator) + .buildPath(baseURL); + assertEquals(expected, actual); + } + + @Test(expected = IllegalArgumentException.class) + public void testAddParamKeyEmpty() { + OpenApiPathBuilder.newBuilder().addParam("", ""); + } + + @Test(expected = IllegalArgumentException.class) + public void testBuildPathURLEmpty() { + OpenApiPathBuilder.newBuilder().buildPath(""); + } +} \ No newline at end of file diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerAppOpenApiService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerAppOpenApiService.java new file mode 100644 index 00000000000..4ade02433d2 --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerAppOpenApiService.java @@ -0,0 +1,87 @@ +/* + * 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.openapi.server.service; + +import com.ctrip.framework.apollo.common.dto.ClusterDTO; +import com.ctrip.framework.apollo.common.entity.App; +import com.ctrip.framework.apollo.common.utils.BeanUtils; +import com.ctrip.framework.apollo.openapi.api.AppOpenApiService; +import com.ctrip.framework.apollo.openapi.dto.OpenAppDTO; +import com.ctrip.framework.apollo.openapi.dto.OpenEnvClusterDTO; +import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils; +import com.ctrip.framework.apollo.portal.component.PortalSettings; +import com.ctrip.framework.apollo.portal.environment.Env; +import com.ctrip.framework.apollo.portal.service.AppService; +import com.ctrip.framework.apollo.portal.service.ClusterService; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import org.springframework.stereotype.Service; + +/** + * @author wxq + */ +@Service +public class ServerAppOpenApiService implements AppOpenApiService { + private final PortalSettings portalSettings; + private final ClusterService clusterService; + private final AppService appService; + + public ServerAppOpenApiService( + PortalSettings portalSettings, + ClusterService clusterService, + AppService appService) { + this.portalSettings = portalSettings; + this.clusterService = clusterService; + this.appService = appService; + } + + @Override + public List getEnvClusterInfo(String appId) { + List envClusters = new LinkedList<>(); + + List envs = portalSettings.getActiveEnvs(); + for (Env env : envs) { + OpenEnvClusterDTO envCluster = new OpenEnvClusterDTO(); + + envCluster.setEnv(env.getName()); + List clusterDTOs = clusterService.findClusters(env, appId); + envCluster.setClusters(BeanUtils.toPropertySet("name", clusterDTOs)); + + envClusters.add(envCluster); + } + + return envClusters; + } + + @Override + public List getAllApps() { + final List apps = this.appService.findAll(); + return OpenApiBeanUtils.transformFromApps(apps); + } + + @Override + public List getAppsInfo(List appIds) { + final List apps = this.appService.findByAppIds(new HashSet<>(appIds)); + return OpenApiBeanUtils.transformFromApps(apps); + } + + @Override + public List getAuthorizedApps() { + throw new UnsupportedOperationException(); + } +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerClusterOpenApiService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerClusterOpenApiService.java new file mode 100644 index 00000000000..c5884b7f31f --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerClusterOpenApiService.java @@ -0,0 +1,51 @@ +/* + * 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.openapi.server.service; + +import com.ctrip.framework.apollo.common.dto.ClusterDTO; +import com.ctrip.framework.apollo.openapi.api.ClusterOpenApiService; +import com.ctrip.framework.apollo.openapi.dto.OpenClusterDTO; +import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils; +import com.ctrip.framework.apollo.portal.environment.Env; +import com.ctrip.framework.apollo.portal.service.ClusterService; +import org.springframework.stereotype.Service; + +/** + * @author wxq + */ +@Service +public class ServerClusterOpenApiService implements ClusterOpenApiService { + + private final ClusterService clusterService; + + public ServerClusterOpenApiService(ClusterService clusterService) { + this.clusterService = clusterService; + } + + @Override + public OpenClusterDTO getCluster(String appId, String env, String clusterName) { + ClusterDTO clusterDTO = clusterService.loadCluster(appId, Env.valueOf(env), clusterName); + return clusterDTO == null ? null : OpenApiBeanUtils.transformFromClusterDTO(clusterDTO); + } + + @Override + public OpenClusterDTO createCluster(String env, OpenClusterDTO openClusterDTO) { + ClusterDTO toCreate = OpenApiBeanUtils.transformToClusterDTO(openClusterDTO); + ClusterDTO createdClusterDTO = clusterService.createCluster(Env.valueOf(env), toCreate); + return OpenApiBeanUtils.transformFromClusterDTO(createdClusterDTO); + } +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerItemOpenApiService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerItemOpenApiService.java new file mode 100644 index 00000000000..541a4e58193 --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerItemOpenApiService.java @@ -0,0 +1,102 @@ +/* + * 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.openapi.server.service; + +import com.ctrip.framework.apollo.common.dto.ItemDTO; +import com.ctrip.framework.apollo.openapi.api.ItemOpenApiService; +import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO; +import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils; +import com.ctrip.framework.apollo.portal.environment.Env; +import com.ctrip.framework.apollo.portal.service.ItemService; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpStatusCodeException; + +/** + * @author wxq + */ +@Service +public class ServerItemOpenApiService implements ItemOpenApiService { + + private final ItemService itemService; + + public ServerItemOpenApiService(ItemService itemService) { + this.itemService = itemService; + } + + @Override + public OpenItemDTO getItem(String appId, String env, String clusterName, String namespaceName, + String key) { + ItemDTO itemDTO = itemService.loadItem(Env.valueOf(env), appId, clusterName, namespaceName, key); + return itemDTO == null ? null : OpenApiBeanUtils.transformFromItemDTO(itemDTO); + } + + @Override + public OpenItemDTO createItem(String appId, String env, String clusterName, String namespaceName, + OpenItemDTO itemDTO) { + + ItemDTO toCreate = OpenApiBeanUtils.transformToItemDTO(itemDTO); + + //protect + toCreate.setLineNum(0); + toCreate.setId(0); + toCreate.setDataChangeLastModifiedBy(toCreate.getDataChangeCreatedBy()); + toCreate.setDataChangeLastModifiedTime(null); + toCreate.setDataChangeCreatedTime(null); + + ItemDTO createdItem = itemService.createItem(appId, Env.valueOf(env), + clusterName, namespaceName, toCreate); + return OpenApiBeanUtils.transformFromItemDTO(createdItem); + } + + @Override + public void updateItem(String appId, String env, String clusterName, String namespaceName, + OpenItemDTO itemDTO) { + ItemDTO toUpdateItem = itemService + .loadItem(Env.valueOf(env), appId, clusterName, namespaceName, itemDTO.getKey()); + //protect. only value,comment,lastModifiedBy can be modified + toUpdateItem.setComment(itemDTO.getComment()); + toUpdateItem.setValue(itemDTO.getValue()); + toUpdateItem.setDataChangeLastModifiedBy(itemDTO.getDataChangeLastModifiedBy()); + + itemService.updateItem(appId, Env.valueOf(env), clusterName, namespaceName, toUpdateItem); + } + + @Override + public void createOrUpdateItem(String appId, String env, String clusterName, String namespaceName, + OpenItemDTO itemDTO) { + try { + this.updateItem(appId, env, clusterName, namespaceName, itemDTO); + } catch (Throwable ex) { + if (ex instanceof HttpStatusCodeException) { + // check createIfNotExists + if (((HttpStatusCodeException) ex).getStatusCode().equals(HttpStatus.NOT_FOUND)) { + this.createItem(appId, env, clusterName, namespaceName, itemDTO); + return; + } + } + throw ex; + } + } + + @Override + public void removeItem(String appId, String env, String clusterName, String namespaceName, + String key, String operator) { + ItemDTO toDeleteItem = this.itemService.loadItem(Env.valueOf(env), appId, clusterName, namespaceName, key); + this.itemService.deleteItem(Env.valueOf(env), toDeleteItem.getId(), operator); + } +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerNamespaceOpenApiService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerNamespaceOpenApiService.java new file mode 100644 index 00000000000..b50bd5946f9 --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerNamespaceOpenApiService.java @@ -0,0 +1,96 @@ +/* + * 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.openapi.server.service; + +import com.ctrip.framework.apollo.common.dto.NamespaceDTO; +import com.ctrip.framework.apollo.common.dto.NamespaceLockDTO; +import com.ctrip.framework.apollo.common.entity.AppNamespace; +import com.ctrip.framework.apollo.openapi.api.NamespaceOpenApiService; +import com.ctrip.framework.apollo.openapi.dto.OpenAppNamespaceDTO; +import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO; +import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceLockDTO; +import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils; +import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO; +import com.ctrip.framework.apollo.portal.environment.Env; +import com.ctrip.framework.apollo.portal.listener.AppNamespaceCreationEvent; +import com.ctrip.framework.apollo.portal.service.AppNamespaceService; +import com.ctrip.framework.apollo.portal.service.NamespaceLockService; +import com.ctrip.framework.apollo.portal.service.NamespaceService; +import java.util.List; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; + +/** + * @author wxq + */ +@Service +public class ServerNamespaceOpenApiService implements NamespaceOpenApiService { + + private final AppNamespaceService appNamespaceService; + private final ApplicationEventPublisher publisher; + private final NamespaceService namespaceService; + private final NamespaceLockService namespaceLockService; + + public ServerNamespaceOpenApiService( + AppNamespaceService appNamespaceService, + ApplicationEventPublisher publisher, + NamespaceService namespaceService, + NamespaceLockService namespaceLockService) { + this.appNamespaceService = appNamespaceService; + this.publisher = publisher; + this.namespaceService = namespaceService; + this.namespaceLockService = namespaceLockService; + } + + @Override + public OpenNamespaceDTO getNamespace(String appId, String env, String clusterName, + String namespaceName) { + NamespaceBO namespaceBO = namespaceService.loadNamespaceBO(appId, Env.valueOf + (env), clusterName, namespaceName); + if (namespaceBO == null) { + return null; + } + return OpenApiBeanUtils.transformFromNamespaceBO(namespaceBO); + } + + @Override + public List getNamespaces(String appId, String env, String clusterName) { + return OpenApiBeanUtils + .batchTransformFromNamespaceBOs(namespaceService.findNamespaceBOs(appId, Env + .valueOf(env), clusterName)); + } + + @Override + public OpenAppNamespaceDTO createAppNamespace(OpenAppNamespaceDTO appNamespaceDTO) { + AppNamespace appNamespace = OpenApiBeanUtils.transformToAppNamespace(appNamespaceDTO); + AppNamespace createdAppNamespace = appNamespaceService.createAppNamespaceInLocal(appNamespace, appNamespaceDTO.isAppendNamespacePrefix()); + + publisher.publishEvent(new AppNamespaceCreationEvent(createdAppNamespace)); + + return OpenApiBeanUtils.transformToOpenAppNamespaceDTO(createdAppNamespace); + } + + @Override + public OpenNamespaceLockDTO getNamespaceLock(String appId, String env, String clusterName, + String namespaceName) { + NamespaceDTO namespace = namespaceService.loadNamespaceBaseInfo(appId, Env + .valueOf(env), clusterName, namespaceName); + NamespaceLockDTO lockDTO = namespaceLockService.getNamespaceLock(appId, Env + .valueOf(env), clusterName, namespaceName); + return OpenApiBeanUtils.transformFromNamespaceLockDTO(namespace.getNamespaceName(), lockDTO); + } +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerReleaseOpenApiService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerReleaseOpenApiService.java new file mode 100644 index 00000000000..11fae767094 --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerReleaseOpenApiService.java @@ -0,0 +1,71 @@ +/* + * 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.openapi.server.service; + +import com.ctrip.framework.apollo.common.dto.ReleaseDTO; +import com.ctrip.framework.apollo.common.utils.BeanUtils; +import com.ctrip.framework.apollo.openapi.api.ReleaseOpenApiService; +import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO; +import com.ctrip.framework.apollo.openapi.dto.OpenReleaseDTO; +import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils; +import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel; +import com.ctrip.framework.apollo.portal.environment.Env; +import com.ctrip.framework.apollo.portal.service.ReleaseService; +import org.springframework.stereotype.Service; + +/** + * @author wxq + */ +@Service +public class ServerReleaseOpenApiService implements ReleaseOpenApiService { + private final ReleaseService releaseService; + + public ServerReleaseOpenApiService( + ReleaseService releaseService) { + this.releaseService = releaseService; + } + + @Override + public OpenReleaseDTO publishNamespace(String appId, String env, String clusterName, + String namespaceName, NamespaceReleaseDTO releaseDTO) { + NamespaceReleaseModel releaseModel = BeanUtils.transform(NamespaceReleaseModel.class, releaseDTO); + + releaseModel.setAppId(appId); + releaseModel.setEnv(Env.valueOf(env).toString()); + releaseModel.setClusterName(clusterName); + releaseModel.setNamespaceName(namespaceName); + + return OpenApiBeanUtils.transformFromReleaseDTO(releaseService.publish(releaseModel)); + } + + @Override + public OpenReleaseDTO getLatestActiveRelease(String appId, String env, String clusterName, + String namespaceName) { + ReleaseDTO releaseDTO = releaseService.loadLatestRelease(appId, Env.valueOf + (env), clusterName, namespaceName); + if (releaseDTO == null) { + return null; + } + + return OpenApiBeanUtils.transformFromReleaseDTO(releaseDTO); + } + + @Override + public void rollbackRelease(String env, long releaseId, String operator) { + releaseService.rollback(Env.valueOf(env), releaseId, operator); + } +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/AppController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/AppController.java index bb9207543ab..c158496c2b8 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/AppController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/AppController.java @@ -16,80 +16,49 @@ */ package com.ctrip.framework.apollo.openapi.v1.controller; -import com.ctrip.framework.apollo.common.dto.ClusterDTO; -import com.ctrip.framework.apollo.common.entity.App; -import com.ctrip.framework.apollo.common.utils.BeanUtils; -import com.ctrip.framework.apollo.openapi.entity.ConsumerRole; +import com.ctrip.framework.apollo.openapi.api.AppOpenApiService; import com.ctrip.framework.apollo.openapi.service.ConsumerService; import com.ctrip.framework.apollo.openapi.util.ConsumerAuthUtil; -import com.ctrip.framework.apollo.portal.environment.Env; import com.ctrip.framework.apollo.openapi.dto.OpenAppDTO; import com.ctrip.framework.apollo.openapi.dto.OpenEnvClusterDTO; -import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils; -import com.ctrip.framework.apollo.portal.component.PortalSettings; -import com.ctrip.framework.apollo.portal.service.AppService; -import com.ctrip.framework.apollo.portal.service.ClusterService; -import com.google.common.collect.Sets; +import java.util.Arrays; import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; @RestController("openapiAppController") @RequestMapping("/openapi/v1") public class AppController { - private final PortalSettings portalSettings; - private final ClusterService clusterService; - private final AppService appService; private final ConsumerAuthUtil consumerAuthUtil; private final ConsumerService consumerService; + private final AppOpenApiService appOpenApiService; - public AppController(final PortalSettings portalSettings, - final ClusterService clusterService, - final AppService appService, + public AppController( final ConsumerAuthUtil consumerAuthUtil, - final ConsumerService consumerService) { - this.portalSettings = portalSettings; - this.clusterService = clusterService; - this.appService = appService; + final ConsumerService consumerService, + AppOpenApiService appOpenApiService) { this.consumerAuthUtil = consumerAuthUtil; this.consumerService = consumerService; + this.appOpenApiService = appOpenApiService; } @GetMapping(value = "/apps/{appId}/envclusters") - public List loadEnvClusterInfo(@PathVariable String appId){ - - List envClusters = new LinkedList<>(); - - List envs = portalSettings.getActiveEnvs(); - for (Env env : envs) { - OpenEnvClusterDTO envCluster = new OpenEnvClusterDTO(); - - envCluster.setEnv(env.getName()); - List clusterDTOs = clusterService.findClusters(env, appId); - envCluster.setClusters(BeanUtils.toPropertySet("name", clusterDTOs)); - - envClusters.add(envCluster); - } - - return envClusters; - + public List getEnvClusterInfo(@PathVariable String appId){ + return this.appOpenApiService.getEnvClusterInfo(appId); } @GetMapping("/apps") public List findApps(@RequestParam(value = "appIds", required = false) String appIds) { - final List apps = new ArrayList<>(); - if (!StringUtils.hasLength(appIds)) { - apps.addAll(appService.findAll()); + if (StringUtils.hasText(appIds)) { + return this.appOpenApiService.getAppsInfo(Arrays.asList(appIds.split(","))); } else { - apps.addAll(appService.findByAppIds(Sets.newHashSet(appIds.split(",")))); + return this.appOpenApiService.getAllApps(); } - return OpenApiBeanUtils.transformFromApps(apps); } /** @@ -101,8 +70,7 @@ public List findAppsAuthorized(HttpServletRequest request) { Set appIds = this.consumerService.findAppIdsAuthorizedByConsumerId(consumerId); - List apps = this.appService.findByAppIds(appIds); - return OpenApiBeanUtils.transformFromApps(apps); + return this.appOpenApiService.getAppsInfo(new ArrayList<>(appIds)); } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java index 75523629e89..c8eb6d03ae2 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java @@ -16,6 +16,8 @@ */ package com.ctrip.framework.apollo.openapi.v1.controller; +import com.ctrip.framework.apollo.openapi.api.ClusterOpenApiService; +import com.ctrip.framework.apollo.portal.spi.UserService; import java.util.Objects; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; @@ -26,35 +28,30 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.ctrip.framework.apollo.common.dto.ClusterDTO; import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.common.utils.InputValidator; import com.ctrip.framework.apollo.common.utils.RequestPrecondition; -import com.ctrip.framework.apollo.portal.environment.Env; import com.ctrip.framework.apollo.core.utils.StringUtils; import com.ctrip.framework.apollo.openapi.dto.OpenClusterDTO; -import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils; -import com.ctrip.framework.apollo.portal.service.ClusterService; -import com.ctrip.framework.apollo.portal.spi.UserService; @RestController("openapiClusterController") @RequestMapping("/openapi/v1/envs/{env}") public class ClusterController { - private final ClusterService clusterService; private final UserService userService; + private final ClusterOpenApiService clusterOpenApiService; - public ClusterController(final ClusterService clusterService, final UserService userService) { - this.clusterService = clusterService; + public ClusterController( + UserService userService, + ClusterOpenApiService clusterOpenApiService) { this.userService = userService; + this.clusterOpenApiService = clusterOpenApiService; } @GetMapping(value = "apps/{appId}/clusters/{clusterName:.+}") - public OpenClusterDTO loadCluster(@PathVariable("appId") String appId, @PathVariable String env, + public OpenClusterDTO getCluster(@PathVariable("appId") String appId, @PathVariable String env, @PathVariable("clusterName") String clusterName) { - - ClusterDTO clusterDTO = clusterService.loadCluster(appId, Env.valueOf(env), clusterName); - return clusterDTO == null ? null : OpenApiBeanUtils.transformFromClusterDTO(clusterDTO); + return this.clusterOpenApiService.getCluster(appId, env, clusterName); } @PreAuthorize(value = "@consumerPermissionValidator.hasCreateClusterPermission(#request, #appId)") @@ -82,10 +79,7 @@ public OpenClusterDTO createCluster(@PathVariable String appId, @PathVariable St throw new BadRequestException("User " + operator + " doesn't exist!"); } - ClusterDTO toCreate = OpenApiBeanUtils.transformToClusterDTO(cluster); - ClusterDTO createdClusterDTO = clusterService.createCluster(Env.valueOf(env), toCreate); - - return OpenApiBeanUtils.transformFromClusterDTO(createdClusterDTO); + return this.clusterOpenApiService.createCluster(env, cluster); } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ItemController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ItemController.java index b2ead890db4..2caa29d45c2 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ItemController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ItemController.java @@ -19,13 +19,12 @@ import com.ctrip.framework.apollo.common.dto.ItemDTO; import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.common.utils.RequestPrecondition; +import com.ctrip.framework.apollo.openapi.api.ItemOpenApiService; import com.ctrip.framework.apollo.portal.environment.Env; import com.ctrip.framework.apollo.core.utils.StringUtils; import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO; -import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils; import com.ctrip.framework.apollo.portal.service.ItemService; import com.ctrip.framework.apollo.portal.spi.UserService; -import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -36,7 +35,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.client.HttpStatusCodeException; import javax.servlet.http.HttpServletRequest; @@ -47,19 +45,19 @@ public class ItemController { private final ItemService itemService; private final UserService userService; + private final ItemOpenApiService itemOpenApiService; - public ItemController(final ItemService itemService, final UserService userService) { + public ItemController(final ItemService itemService, final UserService userService, + ItemOpenApiService itemOpenApiService) { this.itemService = itemService; this.userService = userService; + this.itemOpenApiService = itemOpenApiService; } @GetMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items/{key:.+}") public OpenItemDTO getItem(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @PathVariable String key) { - - ItemDTO itemDTO = itemService.loadItem(Env.valueOf(env), appId, clusterName, namespaceName, key); - - return itemDTO == null ? null : OpenApiBeanUtils.transformFromItemDTO(itemDTO); + return this.itemOpenApiService.getItem(appId, env, clusterName, namespaceName, key); } @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#request, #appId, #namespaceName, #env)") @@ -80,18 +78,7 @@ public OpenItemDTO createItem(@PathVariable String appId, @PathVariable String e throw new BadRequestException("Comment length should not exceed 256 characters"); } - ItemDTO toCreate = OpenApiBeanUtils.transformToItemDTO(item); - - //protect - toCreate.setLineNum(0); - toCreate.setId(0); - toCreate.setDataChangeLastModifiedBy(toCreate.getDataChangeCreatedBy()); - toCreate.setDataChangeLastModifiedTime(null); - toCreate.setDataChangeCreatedTime(null); - - ItemDTO createdItem = itemService.createItem(appId, Env.valueOf(env), - clusterName, namespaceName, toCreate); - return OpenApiBeanUtils.transformFromItemDTO(createdItem); + return this.itemOpenApiService.createItem(appId, env, clusterName, namespaceName, item); } @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#request, #appId, #namespaceName, #env)") @@ -117,24 +104,10 @@ public void updateItem(@PathVariable String appId, @PathVariable String env, throw new BadRequestException("Comment length should not exceed 256 characters"); } - try { - ItemDTO toUpdateItem = itemService - .loadItem(Env.valueOf(env), appId, clusterName, namespaceName, item.getKey()); - //protect. only value,comment,lastModifiedBy can be modified - toUpdateItem.setComment(item.getComment()); - toUpdateItem.setValue(item.getValue()); - toUpdateItem.setDataChangeLastModifiedBy(item.getDataChangeLastModifiedBy()); - - itemService.updateItem(appId, Env.valueOf(env), clusterName, namespaceName, toUpdateItem); - } catch (Throwable ex) { - if (ex instanceof HttpStatusCodeException) { - // check createIfNotExists - if (((HttpStatusCodeException) ex).getStatusCode().equals(HttpStatus.NOT_FOUND) && createIfNotExists) { - createItem(appId, env, clusterName, namespaceName, item, request); - return; - } - } - throw ex; + if (createIfNotExists) { + this.itemOpenApiService.createOrUpdateItem(appId, env, clusterName, namespaceName, item); + } else { + this.itemOpenApiService.updateItem(appId, env, clusterName, namespaceName, item); } } @@ -155,7 +128,7 @@ public void deleteItem(@PathVariable String appId, @PathVariable String env, throw new BadRequestException("item not exists"); } - itemService.deleteItem(Env.valueOf(env), toDeleteItem.getId(), operator); + this.itemOpenApiService.removeItem(appId, env, clusterName, namespaceName, key, operator); } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceController.java index 41e726c67d4..ee691f146cb 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceController.java @@ -16,26 +16,15 @@ */ package com.ctrip.framework.apollo.openapi.v1.controller; - -import com.ctrip.framework.apollo.common.dto.NamespaceDTO; -import com.ctrip.framework.apollo.common.dto.NamespaceLockDTO; -import com.ctrip.framework.apollo.common.entity.AppNamespace; import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.common.utils.InputValidator; import com.ctrip.framework.apollo.common.utils.RequestPrecondition; import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; -import com.ctrip.framework.apollo.portal.environment.Env; +import com.ctrip.framework.apollo.openapi.api.NamespaceOpenApiService; import com.ctrip.framework.apollo.openapi.dto.OpenAppNamespaceDTO; import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO; import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceLockDTO; -import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils; -import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO; -import com.ctrip.framework.apollo.portal.listener.AppNamespaceCreationEvent; -import com.ctrip.framework.apollo.portal.service.AppNamespaceService; -import com.ctrip.framework.apollo.portal.service.NamespaceLockService; -import com.ctrip.framework.apollo.portal.service.NamespaceService; import com.ctrip.framework.apollo.portal.spi.UserService; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -50,26 +39,16 @@ @RestController("openapiNamespaceController") public class NamespaceController { - private final NamespaceLockService namespaceLockService; - private final NamespaceService namespaceService; - private final AppNamespaceService appNamespaceService; - private final ApplicationEventPublisher publisher; private final UserService userService; + private final NamespaceOpenApiService namespaceOpenApiService; public NamespaceController( - final NamespaceLockService namespaceLockService, - final NamespaceService namespaceService, - final AppNamespaceService appNamespaceService, - final ApplicationEventPublisher publisher, - final UserService userService) { - this.namespaceLockService = namespaceLockService; - this.namespaceService = namespaceService; - this.appNamespaceService = appNamespaceService; - this.publisher = publisher; + final UserService userService, + NamespaceOpenApiService namespaceOpenApiService) { this.userService = userService; + this.namespaceOpenApiService = namespaceOpenApiService; } - @PreAuthorize(value = "@consumerPermissionValidator.hasCreateNamespacePermission(#request, #appId)") @PostMapping(value = "/openapi/v1/apps/{appId}/appnamespaces") public OpenAppNamespaceDTO createNamespace(@PathVariable String appId, @@ -98,45 +77,27 @@ public OpenAppNamespaceDTO createNamespace(@PathVariable String appId, throw new BadRequestException(String.format("Illegal user. user = %s", operator)); } - AppNamespace appNamespace = OpenApiBeanUtils.transformToAppNamespace(appNamespaceDTO); - AppNamespace createdAppNamespace = appNamespaceService.createAppNamespaceInLocal(appNamespace, appNamespaceDTO.isAppendNamespacePrefix()); - - publisher.publishEvent(new AppNamespaceCreationEvent(createdAppNamespace)); - - return OpenApiBeanUtils.transformToOpenAppNamespaceDTO(createdAppNamespace); + return this.namespaceOpenApiService.createAppNamespace(appNamespaceDTO); } @GetMapping(value = "/openapi/v1/envs/{env}/apps/{appId}/clusters/{clusterName}/namespaces") public List findNamespaces(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName) { - - return OpenApiBeanUtils - .batchTransformFromNamespaceBOs(namespaceService.findNamespaceBOs(appId, Env - .valueOf(env), clusterName)); + return this.namespaceOpenApiService.getNamespaces(appId, env, clusterName); } @GetMapping(value = "/openapi/v1/envs/{env}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName:.+}") public OpenNamespaceDTO loadNamespace(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName) { - NamespaceBO namespaceBO = namespaceService.loadNamespaceBO(appId, Env.valueOf - (env), clusterName, namespaceName); - if (namespaceBO == null) { - return null; - } - return OpenApiBeanUtils.transformFromNamespaceBO(namespaceBO); + return this.namespaceOpenApiService.getNamespace(appId, env, clusterName, namespaceName); } @GetMapping(value = "/openapi/v1/envs/{env}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/lock") public OpenNamespaceLockDTO getNamespaceLock(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName) { - - NamespaceDTO namespace = namespaceService.loadNamespaceBaseInfo(appId, Env - .valueOf(env), clusterName, namespaceName); - NamespaceLockDTO lockDTO = namespaceLockService.getNamespaceLock(appId, Env - .valueOf(env), clusterName, namespaceName); - return OpenApiBeanUtils.transformFromNamespaceLockDTO(namespace.getNamespaceName(), lockDTO); + return this.namespaceOpenApiService.getNamespaceLock(appId, env, clusterName, namespaceName); } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ReleaseController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ReleaseController.java index c88005113b2..021a538e3f1 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ReleaseController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ReleaseController.java @@ -20,6 +20,7 @@ import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.common.utils.BeanUtils; import com.ctrip.framework.apollo.common.utils.RequestPrecondition; +import com.ctrip.framework.apollo.openapi.api.ReleaseOpenApiService; import com.ctrip.framework.apollo.portal.environment.Env; import com.ctrip.framework.apollo.core.utils.StringUtils; import com.ctrip.framework.apollo.openapi.auth.ConsumerPermissionValidator; @@ -52,16 +53,19 @@ public class ReleaseController { private final UserService userService; private final NamespaceBranchService namespaceBranchService; private final ConsumerPermissionValidator consumerPermissionValidator; + private final ReleaseOpenApiService releaseOpenApiService; public ReleaseController( final ReleaseService releaseService, final UserService userService, final NamespaceBranchService namespaceBranchService, - final ConsumerPermissionValidator consumerPermissionValidator) { + final ConsumerPermissionValidator consumerPermissionValidator, + ReleaseOpenApiService releaseOpenApiService) { this.releaseService = releaseService; this.userService = userService; this.namespaceBranchService = namespaceBranchService; this.consumerPermissionValidator = consumerPermissionValidator; + this.releaseOpenApiService = releaseOpenApiService; } @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#request, #appId, #namespaceName, #env)") @@ -79,27 +83,14 @@ public OpenReleaseDTO createRelease(@PathVariable String appId, @PathVariable St throw new BadRequestException("user(releaseBy) not exists"); } - NamespaceReleaseModel releaseModel = BeanUtils.transform(NamespaceReleaseModel.class, model); - - releaseModel.setAppId(appId); - releaseModel.setEnv(Env.valueOf(env).toString()); - releaseModel.setClusterName(clusterName); - releaseModel.setNamespaceName(namespaceName); - - return OpenApiBeanUtils.transformFromReleaseDTO(releaseService.publish(releaseModel)); + return this.releaseOpenApiService.publishNamespace(appId, env, clusterName, namespaceName, model); } @GetMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/latest") public OpenReleaseDTO loadLatestActiveRelease(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName) { - ReleaseDTO releaseDTO = releaseService.loadLatestRelease(appId, Env.valueOf - (env), clusterName, namespaceName); - if (releaseDTO == null) { - return null; - } - - return OpenApiBeanUtils.transformFromReleaseDTO(releaseDTO); + return this.releaseOpenApiService.getLatestActiveRelease(appId, env, clusterName, namespaceName); } @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#request, #appId, #namespaceName, #env)") @@ -194,8 +185,7 @@ public void rollback(@PathVariable String env, throw new AccessDeniedException("Forbidden operation. you don't have release permission"); } - releaseService.rollback(Env.valueOf(env), releaseId, operator); - + this.releaseOpenApiService.rollbackRelease(env, releaseId, operator); } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/AdminServiceAPI.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/AdminServiceAPI.java index b149501dba0..3409162a333 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/AdminServiceAPI.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/AdminServiceAPI.java @@ -68,6 +68,10 @@ public void deleteApp(Env env, String appId, String operator) { @Service public static class NamespaceAPI extends API { + private ParameterizedTypeReference> + namespacePageDTO = new ParameterizedTypeReference>() { + }; + private ParameterizedTypeReference> typeReference = new ParameterizedTypeReference>() { }; @@ -79,6 +83,14 @@ public List findNamespaceByCluster(String appId, Env env, String c return Arrays.asList(namespaceDTOs); } + public PageDTO findByItem(Env env, String itemKey, int page, int size) { + ResponseEntity> + 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 diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/config/PortalConfig.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/config/PortalConfig.java index 251c001fed1..c1162892565 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/config/PortalConfig.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/config/PortalConfig.java @@ -270,4 +270,7 @@ public String[] webHookUrls() { return getArrayProperty("config.release.webhook.service.url", null); } + public boolean supportSearchByItem() { + return getBooleanProperty("searchByItem.switch", true); + } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AppController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AppController.java index 09b4cf67030..421a78491f3 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AppController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AppController.java @@ -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; @@ -103,15 +102,6 @@ public List findApps(@RequestParam(value = "appIds", required = false) Stri return appService.findByAppIds(Sets.newHashSet(appIds.split(","))); } - @GetMapping("/search/by-appid-or-name") - public PageDTO 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 findAppsByOwner(@RequestParam("owner") String owner, Pageable page) { Set appIds = Sets.newHashSet(); @@ -184,7 +174,7 @@ public MultiResponseEntity nav(@PathVariable String appId) { public ResponseEntity 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(); diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/SearchController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/SearchController.java new file mode 100644 index 00000000000..6d216cb7d30 --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/SearchController.java @@ -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 search(@RequestParam(value = "query", required = false) String query, Pageable pageable) { + if (StringUtils.isEmpty(query)) { + return appService.findAll(pageable); + } + + //search app + PageDTO 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 searchByItem(String itemKey, Pageable pageable) { + List 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 activeEnvs = portalSettings.getActiveEnvs(); + + activeEnvs.forEach(env -> { + PageDTO namespacePage = namespaceService.findNamespacesByItem(env, itemKey, pageable); + if (!namespacePage.hasContent()) { + return; + } + + long currentEnvNSTotal = namespacePage.getTotal(); + if (currentEnvNSTotal > maxTotal.get()) { + maxTotal.set(namespacePage.getTotal()); + } + + List 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()); + } + +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/environment/Env.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/environment/Env.java index 0d8feb1fc89..7ddf13bc1ff 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/environment/Env.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/environment/Env.java @@ -198,7 +198,7 @@ public String toString() { /** * Backward compatibility with enum's name method - * @return + * @Deprecated please use {@link #getName()} instead of */ @Deprecated public String name() { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppService.java index 094cea24002..1438e000cc7 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppService.java @@ -37,7 +37,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.Collections; import java.util.List; import java.util.Set; diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/NamespaceService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/NamespaceService.java index 813fda892a8..99000ac4592 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/NamespaceService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/NamespaceService.java @@ -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; @@ -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; @@ -184,6 +186,13 @@ public List 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 findNamespacesByItem(Env env, String itemKey, Pageable pageable) { + return namespaceAPI.findByItem(env, itemKey, pageable.getPageNumber(), pageable.getPageSize()); + } + public List getPublicAppNamespaceAllNamespaces(Env env, String publicNamespaceName, int page, int size) { diff --git a/apollo-portal/src/main/resources/static/i18n/en.json b/apollo-portal/src/main/resources/static/i18n/en.json index bba41ee7483..08d27f9be9e 100644 --- a/apollo-portal/src/main/resources/static/i18n/en.json +++ b/apollo-portal/src/main/resources/static/i18n/en.json @@ -744,7 +744,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", diff --git a/apollo-portal/src/main/resources/static/i18n/zh-CN.json b/apollo-portal/src/main/resources/static/i18n/zh-CN.json index c486a94f62e..7d9a354ed4b 100644 --- a/apollo-portal/src/main/resources/static/i18n/zh-CN.json +++ b/apollo-portal/src/main/resources/static/i18n/zh-CN.json @@ -744,7 +744,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不能为空", diff --git a/apollo-portal/src/main/resources/static/scripts/controller/config/ConfigBaseInfoController.js b/apollo-portal/src/main/resources/static/scripts/controller/config/ConfigBaseInfoController.js index a5008d92737..213a480ffd2 100644 --- a/apollo-portal/src/main/resources/static/scripts/controller/config/ConfigBaseInfoController.js +++ b/apollo-portal/src/main/resources/static/scripts/controller/config/ConfigBaseInfoController.js @@ -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) { diff --git a/apollo-portal/src/main/resources/static/scripts/controller/config/ConfigNamespaceController.js b/apollo-portal/src/main/resources/static/scripts/controller/config/ConfigNamespaceController.js index 99825eb1b25..06c6f0f9c82 100644 --- a/apollo-portal/src/main/resources/static/scripts/controller/config/ConfigNamespaceController.js +++ b/apollo-portal/src/main/resources/static/scripts/controller/config/ConfigNamespaceController.js @@ -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; } }); diff --git a/apollo-portal/src/main/resources/static/scripts/directive/directive.js b/apollo-portal/src/main/resources/static/scripts/directive/directive.js index f95e68bebe6..48dee6c94bd 100644 --- a/apollo-portal/src/main/resources/static/scripts/directive/directive.js +++ b/apollo-portal/src/main/resources/static/scripts/directive/directive.js @@ -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; + } } }; diff --git a/apollo-portal/src/main/resources/static/scripts/directive/namespace-panel-directive.js b/apollo-portal/src/main/resources/static/scripts/directive/namespace-panel-directive.js index 59935e8a777..47d4f632809 100644 --- a/apollo-portal/src/main/resources/static/scripts/directive/namespace-panel-directive.js +++ b/apollo-portal/src/main/resources/static/scripts/directive/namespace-panel-directive.js @@ -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; } diff --git a/apollo-portal/src/main/resources/static/styles/common-style.css b/apollo-portal/src/main/resources/static/styles/common-style.css index f7b20df41aa..99161f733af 100644 --- a/apollo-portal/src/main/resources/static/styles/common-style.css +++ b/apollo-portal/src/main/resources/static/styles/common-style.css @@ -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 +} diff --git a/apollo-portal/src/main/resources/static/views/component/namespace-panel-master-tab.html b/apollo-portal/src/main/resources/static/views/component/namespace-panel-master-tab.html index 5d374db50c3..08424c8d4b1 100644 --- a/apollo-portal/src/main/resources/static/views/component/namespace-panel-master-tab.html +++ b/apollo-portal/src/main/resources/static/views/component/namespace-panel-master-tab.html @@ -253,9 +253,14 @@
- +
+
+ +
+ +
@@ -1140,4 +1145,4 @@
{{'Component.Namespace.Master.Items.Body.NoPublished.Title' | translate }} - \ No newline at end of file + diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerTest.java index 3b04c338b25..e53d3900613 100644 --- a/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerTest.java +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerTest.java @@ -23,6 +23,7 @@ import com.ctrip.framework.apollo.openapi.repository.ConsumerRepository; import com.ctrip.framework.apollo.openapi.repository.ConsumerRoleRepository; import com.ctrip.framework.apollo.openapi.repository.ConsumerTokenRepository; +import com.ctrip.framework.apollo.openapi.server.service.ServerAppOpenApiService; import com.ctrip.framework.apollo.openapi.service.ConsumerService; import com.ctrip.framework.apollo.openapi.util.ConsumerAuthUtil; import com.ctrip.framework.apollo.portal.component.PortalSettings; @@ -60,7 +61,7 @@ */ @RunWith(SpringRunner.class) @WebMvcTest(controllers = AppController.class) -@Import(ConsumerService.class) +@Import({ConsumerService.class, ServerAppOpenApiService.class}) public class AppControllerTest { @Autowired diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceControllerWithAuthorizationTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceControllerWithAuthorizationTest.java new file mode 100644 index 00000000000..4c256d4a46a --- /dev/null +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceControllerWithAuthorizationTest.java @@ -0,0 +1,188 @@ +/* + * 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.openapi.v1.controller; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.ctrip.framework.apollo.common.utils.InputValidator; +import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; +import com.ctrip.framework.apollo.openapi.dto.OpenAppNamespaceDTO; +import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO; +import java.util.Arrays; +import java.util.UUID; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.web.client.HttpClientErrorException; + +/** + * @author wxq + */ +public class NamespaceControllerWithAuthorizationTest extends AbstractControllerTest { + + static final HttpHeaders HTTP_HEADERS_WITH_TOKEN = new HttpHeaders() {{ + set(HttpHeaders.AUTHORIZATION, "3c16bf5b1f44b465179253442460e8c0ad845289"); + }}; + + /** + * test method {@link NamespaceController#createAppNamespace(String, OpenAppNamespaceDTO)}. + */ + @Ignore("need admin server for this case") + @Test + @Sql(scripts = "/sql/openapi/NamespaceControllerTest.testCreateAppNamespace.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) + public void testCreateAppNamespace() { + final String appId = "consumer-test-app-id-0"; + final String namespaceName = "create-app-namespace-success"; + + // query + { + ResponseEntity responseEntity = + restTemplate.exchange( + url("/openapi/v1/envs/{env}/apps/{appId}/clusters/{clusterName}/namespaces"), + HttpMethod.GET, new HttpEntity<>(HTTP_HEADERS_WITH_TOKEN), String.class, "DEV", appId, + "default"); + String responseEntityBody = responseEntity.getBody(); + assertNotNull(responseEntityBody); + assertFalse(responseEntityBody.contains(namespaceName)); + } + + // create it + final OpenAppNamespaceDTO dto = new OpenAppNamespaceDTO(); + dto.setAppId(appId); + + dto.setName(namespaceName); + dto.setFormat(ConfigFileFormat.Properties.getValue()); + dto.setDataChangeCreatedBy("apollo"); + + restTemplate.exchange(this.url("/openapi/v1/apps/{appId}/appnamespaces"), HttpMethod.POST, + new HttpEntity<>(dto, HTTP_HEADERS_WITH_TOKEN), OpenAppNamespaceDTO.class, dto.getAppId()); + + // query again to confirm + { + ResponseEntity responseEntity = + restTemplate + .getForEntity("/openapi/v1/envs/{env}/apps/{appId}/clusters/{clusterName}/namespaces", + OpenNamespaceDTO[].class, "DEV", appId, "default"); + OpenNamespaceDTO[] openNamespaceDTOS = responseEntity.getBody(); + assertNotNull(openNamespaceDTOS); + assertEquals(1, Arrays.stream(openNamespaceDTOS) + .filter(openNamespaceDTO -> namespaceName.equals(openNamespaceDTO.getNamespaceName())) + .count()); + } + } + + /** + * test method {@link NamespaceController#createAppNamespace(String, OpenAppNamespaceDTO)}. + */ + @Test + @Sql(scripts = "/sql/openapi/NamespaceControllerTest.testCreateAppNamespace.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) + public void testCreateAppNamespaceUnauthorized() { + OpenAppNamespaceDTO dto = new OpenAppNamespaceDTO(); + dto.setAppId("consumer-test-app-id-0"); + dto.setName("namespace-0"); + dto.setFormat(ConfigFileFormat.Properties.getValue()); + dto.setDataChangeCreatedBy("apollo"); + try { + restTemplate.postForEntity( + url("/openapi/v1/apps/{appId}/appnamespaces"), + dto, OpenAppNamespaceDTO.class, dto.getAppId() + ); + Assert.fail("should throw"); + } catch (HttpClientErrorException e) { + assertEquals(HttpStatus.UNAUTHORIZED, e.getStatusCode()); + } + } + + /** + * test method {@link NamespaceController#createAppNamespace(String, OpenAppNamespaceDTO)}. Just + * for check Authorization is ok. + */ + @Test + @Sql(scripts = "/sql/openapi/NamespaceControllerTest.testCreateAppNamespace.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) + public void testCreateAppNamespaceInvalidNamespaceName() { + OpenAppNamespaceDTO dto = new OpenAppNamespaceDTO(); + dto.setAppId("consumer-test-app-id-0"); + dto.setName("invalid name"); + dto.setFormat(ConfigFileFormat.Properties.getValue()); + dto.setDataChangeCreatedBy("apollo"); + + try { + restTemplate.exchange(this.url("/openapi/v1/apps/{appId}/appnamespaces"), HttpMethod.POST, + new HttpEntity<>(dto, HTTP_HEADERS_WITH_TOKEN), OpenAppNamespaceDTO.class, + dto.getAppId()); + Assert.fail("should throw"); + } catch (HttpClientErrorException e) { + assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode()); + String result = e.getResponseBodyAsString(); + assertTrue(result.contains(InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE)); + assertTrue(result.contains(InputValidator.INVALID_NAMESPACE_NAMESPACE_MESSAGE)); + } + } + + /** + * test method {@link NamespaceController#createAppNamespace(String, OpenAppNamespaceDTO)} without + * authority. + */ + @Test + @Sql(scripts = "/sql/openapi/NamespaceControllerTest.testCreateAppNamespace.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) + public void testCreateAppNamespaceWithoutAuthority() { + final OpenAppNamespaceDTO dto = new OpenAppNamespaceDTO(); + dto.setAppId("consumer-test-app-id-1"); + dto.setName("create-app-namespace-fail"); + dto.setFormat(ConfigFileFormat.Properties.getValue()); + dto.setDataChangeCreatedBy("apollo"); + + try { + restTemplate.exchange(this.url("/openapi/v1/apps/{appId}/appnamespaces"), HttpMethod.POST, + new HttpEntity<>(dto, HTTP_HEADERS_WITH_TOKEN), OpenAppNamespaceDTO.class, + dto.getAppId()); + fail("should throw"); + } catch (HttpClientErrorException e) { + assertEquals(HttpStatus.FORBIDDEN, e.getStatusCode()); + String result = e.getResponseBodyAsString(); + assertTrue(result.contains("org.springframework.security.access.AccessDeniedException")); + } + + // random app id + dto.setAppId(UUID.randomUUID().toString()); + + try { + restTemplate.exchange(this.url("/openapi/v1/apps/{appId}/appnamespaces"), HttpMethod.POST, + new HttpEntity<>(dto, HTTP_HEADERS_WITH_TOKEN), OpenAppNamespaceDTO.class, + dto.getAppId()); + fail("should throw"); + } catch (HttpClientErrorException e) { + assertEquals(HttpStatus.FORBIDDEN, e.getStatusCode()); + String result = e.getResponseBodyAsString(); + assertTrue(result.contains("org.springframework.security.access.AccessDeniedException")); + } + } +} diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/SearchControllerTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/SearchControllerTest.java new file mode 100644 index 00000000000..77b4a181e21 --- /dev/null +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/SearchControllerTest.java @@ -0,0 +1,147 @@ +/* + * 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 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 apps = new PageDTO<>(Lists.newLinkedList(), request, 0); + + when(appService.searchByAppIdOrAppName(query, request)).thenReturn(apps); + when(portalConfig.supportSearchByItem()).thenReturn(false); + + PageDTO 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 apps = new PageDTO<>(Lists.newLinkedList(), request, 0); + PageDTO devNamespaces = genPageNamespace(10, request, 20); + PageDTO 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 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 genPageApp(int size, Pageable pageable, int total) { + List 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 genPageNamespace(int size, Pageable pageable, int total) { + List result = Lists.newLinkedList(); + for (int i = 0; i < size; i++) { + NamespaceDTO namespaceDTO = new NamespaceDTO(); + result.add(namespaceDTO); + } + return new PageDTO<>(result, pageable, total); + } +} diff --git a/apollo-portal/src/test/resources/sql/openapi/NamespaceControllerTest.testCreateAppNamespace.sql b/apollo-portal/src/test/resources/sql/openapi/NamespaceControllerTest.testCreateAppNamespace.sql new file mode 100644 index 00000000000..dcd14bf04ae --- /dev/null +++ b/apollo-portal/src/test/resources/sql/openapi/NamespaceControllerTest.testCreateAppNamespace.sql @@ -0,0 +1,223 @@ +-- +-- 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. +-- + +/* + This sql is dumped from a apollo portal database. + + The logic is as follows + + create app: + consumer-test-app-id-0 + consumer-test-app-id-1 + + create consumer: + consumer-test-app-role + + Authorization, let consumer-test-app-role manage: + consumer-test-app-id-0: + Authorization type: App + consumer-test-app-id-1: + Authorization type: Namespace + Managed Namespace: application +*/ + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET NAMES utf8 */; +/*!50503 SET NAMES utf8mb4 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +/*!40000 ALTER TABLE `App` DISABLE KEYS */; +INSERT INTO `App` (`Id`, `AppId`, `Name`, `OrgId`, `OrgName`, `OwnerName`, `OwnerEmail`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(1000, 'consumer-test-app-id-0', 'consumer-test-app-id-0', 'TEST1', '样例部门1', 'apollo', 'apollo@acme.com', 'apollo', 'apollo'); +INSERT INTO `App` (`Id`, `AppId`, `Name`, `OrgId`, `OrgName`, `OwnerName`, `OwnerEmail`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(2000, 'consumer-test-app-id-1', 'consumer-test-app-id-1', 'TEST2', '样例部门2', 'apollo', 'apollo@acme.com', 'apollo', 'apollo'); +/*!40000 ALTER TABLE `App` ENABLE KEYS */; + +/*!40000 ALTER TABLE `AppNamespace` DISABLE KEYS */; +INSERT INTO `AppNamespace` (`Id`, `Name`, `AppId`, `Format`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(1000, 'application', 'consumer-test-app-id-0', 'properties', 'default app namespace', 'apollo', 'apollo'); +INSERT INTO `AppNamespace` (`Id`, `Name`, `AppId`, `Format`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(2000, 'application', 'consumer-test-app-id-1', 'properties', 'default app namespace', 'apollo', 'apollo'); +/*!40000 ALTER TABLE `AppNamespace` ENABLE KEYS */; + +/*!40000 ALTER TABLE `Consumer` DISABLE KEYS */; +INSERT INTO `Consumer` (`Id`, `AppId`, `Name`, `OrgId`, `OrgName`, `OwnerName`, `OwnerEmail`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(1000, 'consumer-test-app-role', 'consumer-test-app-role', 'TEST2', '样例部门2', 'apollo', 'apollo@acme.com', 'apollo', 'apollo'); +/*!40000 ALTER TABLE `Consumer` ENABLE KEYS */; + +/*!40000 ALTER TABLE `ConsumerAudit` DISABLE KEYS */; +/*!40000 ALTER TABLE `ConsumerAudit` ENABLE KEYS */; + +/*!40000 ALTER TABLE `ConsumerRole` DISABLE KEYS */; +INSERT INTO `ConsumerRole` (`Id`, `ConsumerId`, `RoleId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(1000, 1000, 1000, 'apollo', 'apollo'); +INSERT INTO `ConsumerRole` (`Id`, `ConsumerId`, `RoleId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(2000, 1000, 11000, 'apollo', 'apollo'); +/*!40000 ALTER TABLE `ConsumerRole` ENABLE KEYS */; + +/*!40000 ALTER TABLE `ConsumerToken` DISABLE KEYS */; +INSERT INTO `ConsumerToken` (`Id`, `ConsumerId`, `Token`, `Expires`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(1000, 1000, '3c16bf5b1f44b465179253442460e8c0ad845289', '2098-12-31 10:00:00', 'apollo', 'apollo'); +/*!40000 ALTER TABLE `ConsumerToken` ENABLE KEYS */; + +/*!40000 ALTER TABLE `Favorite` DISABLE KEYS */; +/*!40000 ALTER TABLE `Favorite` ENABLE KEYS */; + +/*!40000 ALTER TABLE `Permission` DISABLE KEYS */; +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(1000, 'AssignRole', 'consumer-test-app-id-0', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(2000, 'CreateNamespace', 'consumer-test-app-id-0', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(3000, 'CreateCluster', 'consumer-test-app-id-0', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(4000, 'ManageAppMaster', 'consumer-test-app-id-0', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(5000, 'ModifyNamespace', 'consumer-test-app-id-0+application', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(6000, 'ReleaseNamespace', 'consumer-test-app-id-0+application', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(7000, 'ModifyNamespace', 'consumer-test-app-id-0+application+DEV', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(8000, 'ReleaseNamespace', 'consumer-test-app-id-0+application+DEV', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(9000, 'CreateNamespace', 'consumer-test-app-id-1', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(10000, 'AssignRole', 'consumer-test-app-id-1', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(11000, 'CreateCluster', 'consumer-test-app-id-1', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(12000, 'ManageAppMaster', 'consumer-test-app-id-1', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(13000, 'ModifyNamespace', 'consumer-test-app-id-1+application', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(14000, 'ReleaseNamespace', 'consumer-test-app-id-1+application', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(15000, 'ModifyNamespace', 'consumer-test-app-id-1+application+DEV', 'apollo', 'apollo'); +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(16000, 'ReleaseNamespace', 'consumer-test-app-id-1+application+DEV', 'apollo', 'apollo'); +/*!40000 ALTER TABLE `Permission` ENABLE KEYS */; + +/*!40000 ALTER TABLE `Role` DISABLE KEYS */; +INSERT INTO `Role` (`Id`, `RoleName`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(1000, 'Master+consumer-test-app-id-0', 'apollo', 'apollo'); +INSERT INTO `Role` (`Id`, `RoleName`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(2000, 'ManageAppMaster+consumer-test-app-id-0', 'apollo', 'apollo'); +INSERT INTO `Role` (`Id`, `RoleName`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(3000, 'ModifyNamespace+consumer-test-app-id-0+application', 'apollo', 'apollo'); +INSERT INTO `Role` (`Id`, `RoleName`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(4000, 'ReleaseNamespace+consumer-test-app-id-0+application', 'apollo', 'apollo'); +INSERT INTO `Role` (`Id`, `RoleName`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(5000, 'ModifyNamespace+consumer-test-app-id-0+application+DEV', 'apollo', 'apollo'); +INSERT INTO `Role` (`Id`, `RoleName`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(6000, 'ReleaseNamespace+consumer-test-app-id-0+application+DEV', 'apollo', 'apollo'); +INSERT INTO `Role` (`Id`, `RoleName`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(7000, 'Master+consumer-test-app-id-1', 'apollo', 'apollo'); +INSERT INTO `Role` (`Id`, `RoleName`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(8000, 'ManageAppMaster+consumer-test-app-id-1', 'apollo', 'apollo'); +INSERT INTO `Role` (`Id`, `RoleName`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(9000, 'ModifyNamespace+consumer-test-app-id-1+application', 'apollo', 'apollo'); +INSERT INTO `Role` (`Id`, `RoleName`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(10000, 'ReleaseNamespace+consumer-test-app-id-1+application', 'apollo', 'apollo'); +INSERT INTO `Role` (`Id`, `RoleName`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(11000, 'ModifyNamespace+consumer-test-app-id-1+application+DEV', 'apollo', 'apollo'); +INSERT INTO `Role` (`Id`, `RoleName`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(12000, 'ReleaseNamespace+consumer-test-app-id-1+application+DEV', 'apollo', 'apollo'); +/*!40000 ALTER TABLE `Role` ENABLE KEYS */; + +/*!40000 ALTER TABLE `RolePermission` DISABLE KEYS */; +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(1000, 1000, 1000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(2000, 1000, 2000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(3000, 1000, 3000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(4000, 2000, 4000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(5000, 3000, 5000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(6000, 4000, 6000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(7000, 5000, 7000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(8000, 6000, 8000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(9000, 7000, 9000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(10000, 7000, 10000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(11000, 7000, 11000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(12000, 8000, 12000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(13000, 9000, 13000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(14000, 10000, 14000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(15000, 11000, 15000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(16000, 12000, 16000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(17000, 13000, 17000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(18000, 13000, 18000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(19000, 13000, 19000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(20000, 14000, 20000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(21000, 15000, 21000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(22000, 16000, 22000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(23000, 17000, 23000, 'apollo', 'apollo'); +INSERT INTO `RolePermission` (`Id`, `RoleId`, `PermissionId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(24000, 18000, 24000, 'apollo', 'apollo'); +/*!40000 ALTER TABLE `RolePermission` ENABLE KEYS */; + +/*!40000 ALTER TABLE `UserRole` DISABLE KEYS */; +INSERT INTO `UserRole` (`Id`, `UserId`, `RoleId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(1000, 'apollo', 1000, 'apollo', 'apollo'); +INSERT INTO `UserRole` (`Id`, `UserId`, `RoleId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(2000, 'apollo', 3000, 'apollo', 'apollo'); +INSERT INTO `UserRole` (`Id`, `UserId`, `RoleId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(3000, 'apollo', 4000, 'apollo', 'apollo'); +INSERT INTO `UserRole` (`Id`, `UserId`, `RoleId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(4000, 'apollo', 7000, 'apollo', 'apollo'); +INSERT INTO `UserRole` (`Id`, `UserId`, `RoleId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(5000, 'apollo', 9000, 'apollo', 'apollo'); +INSERT INTO `UserRole` (`Id`, `UserId`, `RoleId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(6000, 'apollo', 10000, 'apollo', 'apollo'); +INSERT INTO `UserRole` (`Id`, `UserId`, `RoleId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(7000, 'apollo', 13000, 'apollo', 'apollo'); +INSERT INTO `UserRole` (`Id`, `UserId`, `RoleId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(8000, 'apollo', 15000, 'apollo', 'apollo'); +INSERT INTO `UserRole` (`Id`, `UserId`, `RoleId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES +(9000, 'apollo', 16000, 'apollo', 'apollo'); +/*!40000 ALTER TABLE `UserRole` ENABLE KEYS */; + +/*!40000 ALTER TABLE `Users` DISABLE KEYS */; +INSERT INTO `Users` (`Id`, `Username`, `Password`, `UserDisplayName`, `Email`, `Enabled`) VALUES +(1000, 'apollo', '$2a$10$7r20uS.BQ9uBpf3Baj3uQOZvMVvB1RN3PYoKE94gtz2.WAOuiiwXS', 'apollo', 'apollo@acme.com', 1); +/*!40000 ALTER TABLE `Users` ENABLE KEYS */; + +/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; +/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */; diff --git a/changes/changes-1.9.1.md b/changes/changes-1.9.1.md new file mode 100644 index 00000000000..dbeca07a728 --- /dev/null +++ b/changes/changes-1.9.1.md @@ -0,0 +1,12 @@ +Changes by Version +================== +Release Notes. + +Apollo 1.9.1 + +------------------ +* [Remove spring dependencies from internal code](https://github.com/apolloconfig/apollo/pull/3937) +* [Fix issue: ingress syntax](https://github.com/apolloconfig/apollo/pull/3933) + +------------------ +All issues and pull requests are [here](https://github.com/apolloconfig/apollo/milestone/9?closed=1) diff --git a/docs/charts/apollo-portal-0.3.1.tgz b/docs/charts/apollo-portal-0.3.1.tgz new file mode 100644 index 00000000000..98d5c7195ed Binary files /dev/null and b/docs/charts/apollo-portal-0.3.1.tgz differ diff --git a/docs/charts/apollo-service-0.3.1.tgz b/docs/charts/apollo-service-0.3.1.tgz new file mode 100644 index 00000000000..f57e39e832f Binary files /dev/null and b/docs/charts/apollo-service-0.3.1.tgz differ diff --git a/docs/charts/index.yaml b/docs/charts/index.yaml index 7d34169ff82..685e921c20b 100644 --- a/docs/charts/index.yaml +++ b/docs/charts/index.yaml @@ -15,6 +15,22 @@ apiVersion: v1 entries: apollo-portal: + - apiVersion: v2 + appVersion: 1.9.1 + created: "2021-09-09T09:24:18.945869+08:00" + description: A Helm chart for Apollo Portal + digest: 6e50025665de4179f3485aa58ef33f24556267764afc645eaaab98810716f7ab + home: https://github.com/ctripcorp/apollo + icon: https://raw.githubusercontent.com/ctripcorp/apollo/master/apollo-portal/src/main/resources/static/img/logo-simple.png + maintainers: + - email: nobodyiam@gmail.com + name: nobodyiam + url: https://github.com/nobodyiam + name: apollo-portal + type: application + urls: + - apollo-portal-0.3.1.tgz + version: 0.3.1 - apiVersion: v2 appVersion: 1.9.0 created: "2021-08-23T20:17:19.061588+08:00" @@ -128,6 +144,22 @@ entries: - apollo-portal-0.1.0.tgz version: 0.1.0 apollo-service: + - apiVersion: v2 + appVersion: 1.9.1 + created: "2021-09-09T09:24:18.953677+08:00" + description: A Helm chart for Apollo Config Service and Apollo Admin Service + digest: e444ef8db74d8e11b7b5cfebb31ac2e1138a40d036045f432898ebf98fc33f07 + home: https://github.com/ctripcorp/apollo + icon: https://raw.githubusercontent.com/ctripcorp/apollo/master/apollo-portal/src/main/resources/static/img/logo-simple.png + maintainers: + - email: nobodyiam@gmail.com + name: nobodyiam + url: https://github.com/nobodyiam + name: apollo-service + type: application + urls: + - apollo-service-0.3.1.tgz + version: 0.3.1 - apiVersion: v2 appVersion: 1.9.0 created: "2021-08-23T20:17:19.069986+08:00" diff --git a/docs/zh/deployment/distributed-deployment-guide.md b/docs/zh/deployment/distributed-deployment-guide.md index e228b7b53f3..1c32f786cd3 100644 --- a/docs/zh/deployment/distributed-deployment-guide.md +++ b/docs/zh/deployment/distributed-deployment-guide.md @@ -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,否则会访问失败 diff --git a/docs/zh/usage/apollo-open-api-platform.md b/docs/zh/usage/apollo-open-api-platform.md index 14733554c8f..f8a22a2c182 100644 --- a/docs/zh/usage/apollo-open-api-platform.md +++ b/docs/zh/usage/apollo-open-api-platform.md @@ -26,7 +26,7 @@ Apollo管理员在 http://{portal_address}/open/manage.html 创建第三方应 任何语言的第三方应用都可以调用Apollo的Open API,在调用接口时,需要设置注意以下两点: * Http Header中增加一个Authorization字段,字段值为申请的token * Http Header的Content-Type字段需要设置成application/json;charset=UTF-8 - + ##### 2.3.2 Java应用通过apollo-openapi调用Apollo Open API 从1.1.0版本开始,Apollo提供了[apollo-openapi](https://github.com/ctripcorp/apollo/tree/master/apollo-openapi)客户端,所以Java语言的第三方应用可以更方便地调用Apollo Open API。 @@ -55,17 +55,26 @@ ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder() .Net core也提供了open api的客户端,详见https://github.com/ctripcorp/apollo.net/pull/77 +##### 2.3.4 Shell Scripts调用Apollo Open API + +封装了bash的function,底层使用curl来发送HTTP请求 + +* bash函数:[openapi.sh](https://github.com/apolloconfig/apollo/blob/master/scripts/openapi/bash/openapi.sh) + +* 使用示例:[openapi-usage-example.sh](https://github.com/apolloconfig/apollo/blob/master/scripts/openapi/bash/openapi-usage-example.sh) +* 全部和openapi有关的shell脚本在文件夹 https://github.com/apolloconfig/apollo/tree/master/scripts/sql 下 + ### 三、 接口文档 #### 3.1 URL路径参数说明 - + 参数名 | 参数说明 --- | --- env | 所管理的配置环境 appId | 所管理的配置AppId clusterName | 所管理的配置集群名, 一般情况下传入 default 即可。如果是特殊集群,传入相应集群的名称即可 namespaceName | 所管理的Namespace的名称,如果是非properties格式,需要加上后缀名,如`sample.yml` - + #### 3.2 API接口列表 ##### 3.2.1 获取App的环境,集群信息 @@ -549,4 +558,3 @@ operator | true | String | 删除配置的操作者,域账号 #### 4.6 500 - Internal Server Error 其它类型的错误默认都会返回500,对这类错误如果应用无法根据提示信息找到原因的话,可以找Apollo研发团队一起排查问题。 - diff --git a/scripts/openapi/bash/openapi-usage-example.sh b/scripts/openapi/bash/openapi-usage-example.sh new file mode 100644 index 00000000000..96bdbc569fc --- /dev/null +++ b/scripts/openapi/bash/openapi-usage-example.sh @@ -0,0 +1,111 @@ +#!/bin/bash +# +# 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. +# + +# title openapi-usage-example.sh +# description show how to use openapi.sh +# author wxq +# date 2021-09-12 +# Chinese reference website https://www.apolloconfig.com/#/zh/usage/apollo-open-api-platform +# English reference website https://www.apolloconfig.com/#/en/usage/apollo-open-api-platform + +# export global varialbes +export APOLLO_PORTAL_ADDRESS=http://106.54.227.205 +export APOLLO_OPENAPI_TOKEN=284fe833cbaeecf2764801aa73965080b184fc88 +export CURL_OPTIONS="" +# load functions +source openapi.sh + +# set up global environment variable +APOLLO_APP_ID=openapi +APOLLO_ENV=DEV +APOLLO_CLUSTER=default +APOLLO_USER=apollo + +####################################### cluster ####################################### +# get cluster +printf "get cluster: env = '%s', app id = '%s', cluster = '%s'\n" ${APOLLO_ENV} ${APOLLO_APP_ID} ${APOLLO_CLUSTER} +cluster_get ${APOLLO_ENV} ${APOLLO_APP_ID} ${APOLLO_CLUSTER} +printf "\n\n" + +# create cluster. To forbid cluster xxx already exists, add timestamp to suffix +temp_apollo_cluster="cluster-$(date +%s)" +printf "create cluster: env = '%s', app id = '%s', cluster = '%s'\n" ${APOLLO_ENV} ${APOLLO_APP_ID} ${temp_apollo_cluster} +cluster_create ${APOLLO_ENV} ${APOLLO_APP_ID} ${temp_apollo_cluster} ${APOLLO_USER} +printf "\n\n" +####################################### end of cluster ####################################### + +####################################### namespace ####################################### +# create namespace +temp_namespace_name="application-123" +temp_namespace_format=yaml +echo "create namespace: namespace name = '${temp_namespace_name}', app id = '${APOLLO_APP_ID}', format = '${temp_namespace_format}'" +namespace_create ${APOLLO_APP_ID} ${temp_namespace_name} ${temp_format} false 'create by openapi, bash scripts' ${APOLLO_USER} +printf "\n\n" +####################################### end of namespace ####################################### + +####################################### item ####################################### +# create an item, i.e a key value pair +temp_item_key="openapi-usage-create-item-key-$(date +%s)" +temp_item_value="openapi-usage-create-item-value-$(date +%s)" +echo -e "create item: app id = ${APOLLO_APP_ID} env = ${APOLLO_ENV} key = ${temp_item_key} value = ${temp_item_value}" +item_create ${APOLLO_ENV} ${APOLLO_APP_ID} default application ${temp_item_key} ${temp_item_value} "openapi-create-item" ${APOLLO_USER} +printf "\n\n" + +# update an item +echo "show update failed when item key not exists" +sleep 1 +temp_item_key="openapi-usage-update-item-key-$(date +%s)" +temp_item_value="openapi-usage-update-item-value-$(date +%s)" +item_update ${APOLLO_ENV} ${APOLLO_APP_ID} default application ${temp_item_key} ${temp_item_value} "openapi-update-item" ${APOLLO_USER} +printf "\n\n" + +echo "show after created, update success" +item_create ${APOLLO_ENV} ${APOLLO_APP_ID} default application ${temp_item_key} ${temp_item_value} "openapi-create-item" ${APOLLO_USER} +temp_item_value="item-update-success" +printf "\n" +item_update ${APOLLO_ENV} ${APOLLO_APP_ID} default application ${temp_item_key} ${temp_item_value} "openapi-update-item" ${APOLLO_USER} +printf "\n\n" + +echo "show Update an item of a namespace, if item doesn's exist, create it" +sleep 1 +temp_item_key="openapi-usage-item_update_create_if_not_exists-key-$(date +%s)" +temp_item_value="openapi-usage-item_update_create_if_not_exists-value-$(date +%s)" +echo "create it, key = '${temp_item_key}' value = '${temp_item_value}'" +item_update_create_if_not_exists ${APOLLO_ENV} ${APOLLO_APP_ID} default application ${temp_item_key} ${temp_item_value} "openapi-update-item" ${APOLLO_USER} ${APOLLO_USER} +temp_item_value="openapi-value-of-item_update_create_if_not_exists" +echo "update it, key = '${temp_item_key}' value = '${temp_item_value}'" +item_update_create_if_not_exists ${APOLLO_ENV} ${APOLLO_APP_ID} default application ${temp_item_key} ${temp_item_value} "openapi-update-item" ${APOLLO_USER} ${APOLLO_USER} +printf "\n\n" + +echo "show delete item failed" +item_delete ${APOLLO_ENV} ${APOLLO_APP_ID} default application "key-be-deleted" ${APOLLO_USER} +printf "\nshow delete item success\n" +item_delete ${APOLLO_ENV} ${APOLLO_APP_ID} default application ${temp_item_key} ${APOLLO_USER} +printf "\n\n" +####################################### end of item ####################################### + +####################################### namespace release ####################################### +temp_namespace_name="application-$(date +%s)" +temp_namespace_format=properties +echo -e "create namespace: namespace name = '${temp_namespace_name}', app id = '${APOLLO_APP_ID}', format = '${temp_namespace_format}'" +namespace_create ${APOLLO_APP_ID} ${temp_namespace_name} ${temp_namespace_format} false 'create by openapi, bash scripts for release' ${APOLLO_USER} +echo -e "\ncreate or update an item '${temp_item_key}'='${temp_item_value}'" +item_update_create_if_not_exists ${APOLLO_ENV} ${APOLLO_APP_ID} default ${temp_namespace_name} ${temp_item_key} ${temp_item_value} "openapi-update-item" ${APOLLO_USER} ${APOLLO_USER} +echo -e "\nrelease namespace: '${temp_namespace_name}'" +namespace_release ${APOLLO_ENV} ${APOLLO_APP_ID} ${APOLLO_CLUSTER} ${temp_namespace_name} 'releaseTitle-openapi-2021-01-01' 'releaseComment-openapi' ${APOLLO_USER} +printf "\n\n" +####################################### end of namespace release ####################################### \ No newline at end of file diff --git a/scripts/openapi/bash/openapi.sh b/scripts/openapi/bash/openapi.sh new file mode 100644 index 00000000000..6a14d75fe47 --- /dev/null +++ b/scripts/openapi/bash/openapi.sh @@ -0,0 +1,343 @@ +#!/bin/bash +# +# 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. +# + +# title openapi.sh +# description functions to call openapi through http +# author wxq +# date 2021-09-12 +# Chinese reference website https://www.apolloconfig.com/#/zh/usage/apollo-open-api-platform +# English reference website https://www.apolloconfig.com/#/en/usage/apollo-open-api-platform + +####################################### Global variables ####################################### +# portal's address, just support 1 address without suffix '/' +# Don't use http://ip:port/ with suffix '/' or multiple address http://ip1:port1,http://ip2:port2 +APOLLO_PORTAL_ADDRESS=${APOLLO_PORTAL_ADDRESS:-http://ip:port} +APOLLO_OPENAPI_TOKEN=${APOLLO_OPENAPI_TOKEN:-please_change_me_by_environment_variable} +CURL_OPTIONS=${CURL_OPTIONS:-} + +echo "apollo portal address: ${APOLLO_PORTAL_ADDRESS}" +echo "curl options: ${CURL_OPTIONS}" +####################################### end of Global variables ####################################### + +####################################### basic http call ####################################### +####################################### +# Http get by curl. +# Globals: +# APOLLO_PORTAL_ADDRESS: portal's address +# APOLLO_OPENAPI_TOKEN: openapi's token +# CURL_OPTIONS: options in curl +# Arguments: +# url_suffix +####################################### +function openapi_get() { + local url_suffix=$1 + + local url="${APOLLO_PORTAL_ADDRESS}/${url_suffix}" + curl ${CURL_OPTIONS} --header "Authorization: ${APOLLO_OPENAPI_TOKEN}" --header "Content-Type: application/json;charset=UTF-8" "${url}" +} + +####################################### +# Http post by curl. +# Globals: +# APOLLO_PORTAL_ADDRESS: portal's address +# APOLLO_OPENAPI_TOKEN: openapi's token +# CURL_OPTIONS: options in curl +# Arguments: +# url_suffix +# body +####################################### +function openapi_post() { + local url_suffix=$1 + local body=$2 + + local url="${APOLLO_PORTAL_ADDRESS}/${url_suffix}" + curl ${CURL_OPTIONS} --header "Authorization: ${APOLLO_OPENAPI_TOKEN}" --header "Content-Type: application/json;charset=UTF-8" --data "${body}" "${url}" +} + +####################################### +# Http put by curl. +# Globals: +# APOLLO_PORTAL_ADDRESS: portal's address +# APOLLO_OPENAPI_TOKEN: openapi's token +# CURL_OPTIONS: options in curl +# Arguments: +# url_suffix +# body +####################################### +function openapi_put() { + local url_suffix=$1 + local body=$2 + + local url="${APOLLO_PORTAL_ADDRESS}/${url_suffix}" + curl ${CURL_OPTIONS} --header "Authorization: ${APOLLO_OPENAPI_TOKEN}" --header "Content-Type: application/json;charset=UTF-8" -X PUT --data "${body}" "${url}" +} + +####################################### +# Http delete by curl. +# Globals: +# APOLLO_PORTAL_ADDRESS: portal's address +# APOLLO_OPENAPI_TOKEN: openapi's token +# CURL_OPTIONS: options in curl +# Arguments: +# url_suffix +# body +####################################### +function openapi_delete() { + local url_suffix=$1 + local body=$2 + + local url="${APOLLO_PORTAL_ADDRESS}/${url_suffix}" + curl ${CURL_OPTIONS} --header "Authorization: ${APOLLO_OPENAPI_TOKEN}" --header "Content-Type: application/json;charset=UTF-8" -X DELETE --data "${body}" "${url}" +} +####################################### end of basic http call ####################################### + + +####################################### cluster ####################################### +####################################### +# Get cluster. +# 获取集群 +# Arguments: +# env +# appId +# clusterName +####################################### +function cluster_get() { + local env=$1 + local appId=$2 + local clusterName=$3 + openapi_get "openapi/v1/envs/${env}/apps/${appId}/clusters/${clusterName}" +} + +####################################### +# Create cluster in app's environment. +# 创建集群 +# Arguments: +# env +# appId +# clusterName +# dataChangeCreatedBy +####################################### +function cluster_create() { + local env=$1 + local appId=$2 + local clusterName=$3 + local dataChangeCreatedBy=$4 + openapi_post "openapi/v1/envs/${env}/apps/${appId}/clusters" "$(cat <