diff --git a/CHANGES.md b/CHANGES.md index fefb6b01aaa..4db03025755 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,7 @@ Apollo 1.10.0 * [Remove spring dependencies from internal code](https://github.com/apolloconfig/apollo/pull/3937) * [Fix issue: ingress syntax](https://github.com/apolloconfig/apollo/pull/3933) * [Support search by item](https://github.com/apolloconfig/apollo/pull/3977) +* [refactor: let open api more easier to use and development](https://github.com/apolloconfig/apollo/pull/3943) ------------------ All issues and pull requests are [here](https://github.com/ctripcorp/apollo/milestone/8?closed=1) 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 2f8bd7be021..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 @@ -91,6 +91,9 @@ 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); 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 3bead951e04..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 @@ -106,6 +106,9 @@ 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); 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 4095885bc7f..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 @@ -67,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/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/NamespaceService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/NamespaceService.java index 3bc375a9418..bf6616265bb 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 @@ -183,6 +183,9 @@ 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()); } 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/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"