diff --git a/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/controller/ItemController.java b/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/controller/ItemController.java
index 3a24de84c0edef11ca21adaab3933e95aac449e8..400e19bd167e5c3ec61aeb7437a4ec82a67f3646 100644
--- a/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/controller/ItemController.java
+++ b/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/controller/ItemController.java
@@ -43,7 +43,7 @@ public class ItemController {
ConfigChangeContentBuilder builder = new ConfigChangeContentBuilder();
Item managedEntity = itemService.findOne(appId, clusterName, namespaceName, entity.getKey());
if (managedEntity != null) {
- throw new BadRequestException("item already exist");
+ throw new BadRequestException("item already exists");
} else {
entity = itemService.save(entity);
builder.createItem(entity);
diff --git a/apollo-mockserver/pom.xml b/apollo-mockserver/pom.xml
index d2c09b551183d81f2a688f193fa86fb917025322..eb8afef3584621356552fd844a80d4a9822b96c1 100644
--- a/apollo-mockserver/pom.xml
+++ b/apollo-mockserver/pom.xml
@@ -10,6 +10,11 @@
4.0.0
apollo-mockserver
+ Apollo Mock Server
+
+
+ 1.7
+
diff --git a/apollo-mockserver/src/main/java/com/ctrip/framework/apollo/mockserver/EmbeddedApollo.java b/apollo-mockserver/src/main/java/com/ctrip/framework/apollo/mockserver/EmbeddedApollo.java
index 3c2ed099a30544ff929be7da1926b9db66b34ded..02b73355621a8649415e9d07b7ae569c26e3630e 100644
--- a/apollo-mockserver/src/main/java/com/ctrip/framework/apollo/mockserver/EmbeddedApollo.java
+++ b/apollo-mockserver/src/main/java/com/ctrip/framework/apollo/mockserver/EmbeddedApollo.java
@@ -5,10 +5,9 @@ import com.ctrip.framework.apollo.core.dto.ApolloConfig;
import com.ctrip.framework.apollo.core.dto.ApolloConfigNotification;
import com.ctrip.framework.apollo.core.utils.ResourceUtils;
import com.ctrip.framework.apollo.internals.ConfigServiceLocator;
-import com.ctrip.framework.apollo.spring.config.PropertySourcesProcessor;
-import com.ctrip.framework.apollo.spring.property.SpringValueDefinitionProcessor;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Method;
@@ -19,7 +18,6 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
-import java.util.stream.Collectors;
import okhttp3.mockwebserver.Dispatcher;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
@@ -110,8 +108,10 @@ public class EmbeddedApollo extends ExternalResource {
private String loadConfigFor(String namespace) {
String filename = String.format("mockdata-%s.properties", namespace);
final Properties prop = ResourceUtils.readConfigFile(filename, new Properties());
- Map configurations = prop.stringPropertyNames().stream().collect(
- Collectors.toMap(key -> key, prop::getProperty));
+ Map configurations = Maps.newHashMap();
+ for (String propertyName : prop.stringPropertyNames()) {
+ configurations.put(propertyName, prop.getProperty(propertyName));
+ }
ApolloConfig apolloConfig = new ApolloConfig("someAppId", "someCluster", namespace, "someReleaseKey");
Map mergedConfigurations = mergeOverriddenProperties(namespace, configurations);
diff --git a/apollo-mockserver/src/test/java/com/ctrip/framework/apollo/mockserver/ApolloMockServerApiTest.java b/apollo-mockserver/src/test/java/com/ctrip/framework/apollo/mockserver/ApolloMockServerApiTest.java
index 7f62cb8dc88dbd8b918efc71c3e2df2167341c29..c99a5f63ab5ad95b9d66796bd674bbda5810cc6b 100644
--- a/apollo-mockserver/src/test/java/com/ctrip/framework/apollo/mockserver/ApolloMockServerApiTest.java
+++ b/apollo-mockserver/src/test/java/com/ctrip/framework/apollo/mockserver/ApolloMockServerApiTest.java
@@ -4,6 +4,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.ctrip.framework.apollo.Config;
+import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.google.common.util.concurrent.SettableFuture;
@@ -32,9 +33,14 @@ public class ApolloMockServerApiTest {
Config otherConfig = ConfigService.getConfig(otherNamespace);
- SettableFuture future = SettableFuture.create();
+ final SettableFuture future = SettableFuture.create();
- otherConfig.addChangeListener(future::set);
+ otherConfig.addChangeListener(new ConfigChangeListener() {
+ @Override
+ public void onChange(ConfigChangeEvent changeEvent) {
+ future.set(changeEvent);
+ }
+ });
assertEquals("otherValue1", otherConfig.getProperty("key1", null));
assertEquals("otherValue2", otherConfig.getProperty("key2", null));
diff --git a/apollo-openapi/pom.xml b/apollo-openapi/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6a44a9bbb299715d00685f3d4308e8e4c4870cb3
--- /dev/null
+++ b/apollo-openapi/pom.xml
@@ -0,0 +1,50 @@
+
+
+
+ apollo
+ com.ctrip.framework.apollo
+ 1.1.0-SNAPSHOT
+
+ 4.0.0
+
+ apollo-openapi
+ Apollo Open Api
+
+
+ 1.7
+
+
+
+
+ com.ctrip.framework.apollo
+ apollo-core
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+ org.slf4j
+ jcl-over-slf4j
+
+
+ org.apache.logging.log4j
+ log4j-slf4j-impl
+ test
+
+
+ org.apache.logging.log4j
+ log4j-core
+ test
+
+
+
+
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
new file mode 100644
index 0000000000000000000000000000000000000000..f87a28f7cc2d544ce7b83069ac763da02cc6cbed
--- /dev/null
+++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/ApolloOpenApiClient.java
@@ -0,0 +1,209 @@
+package com.ctrip.framework.apollo.openapi.client;
+
+import com.ctrip.framework.apollo.openapi.client.constant.ApolloOpenApiConstants;
+import com.ctrip.framework.apollo.openapi.client.service.AppOpenApiService;
+import com.ctrip.framework.apollo.openapi.client.service.ItemOpenApiService;
+import com.ctrip.framework.apollo.openapi.client.service.NamespaceOpenApiService;
+import com.ctrip.framework.apollo.openapi.client.service.ReleaseOpenApiService;
+import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;
+import com.ctrip.framework.apollo.openapi.dto.OpenAppNamespaceDTO;
+import com.ctrip.framework.apollo.openapi.dto.OpenEnvClusterDTO;
+import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
+import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO;
+import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceLockDTO;
+import com.ctrip.framework.apollo.openapi.dto.OpenReleaseDTO;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.util.List;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicHeader;
+
+/**
+ * This class contains collections of methods to access Apollo Open Api.
+ *
+ * For more information, please refer Apollo Wiki.
+ *
+ */
+public class ApolloOpenApiClient {
+ private final String portalUrl;
+ private final String token;
+ private final AppOpenApiService appService;
+ private final ItemOpenApiService itemService;
+ private final ReleaseOpenApiService releaseService;
+ private final NamespaceOpenApiService namespaceService;
+
+ private ApolloOpenApiClient(String portalUrl, String token, RequestConfig requestConfig) {
+ this.portalUrl = portalUrl;
+ this.token = token;
+ CloseableHttpClient client = HttpClients.custom().setDefaultRequestConfig(requestConfig)
+ .setDefaultHeaders(Lists.newArrayList(new BasicHeader("Authorization", token))).build();
+ Gson gson = new GsonBuilder().setDateFormat(ApolloOpenApiConstants.JSON_DATE_FORMAT).create();
+
+ String baseUrl = this.portalUrl + ApolloOpenApiConstants.OPEN_API_V1_PREFIX;
+ appService = new AppOpenApiService(client, baseUrl, gson);
+ namespaceService = new NamespaceOpenApiService(client, baseUrl, gson);
+ itemService = new ItemOpenApiService(client, baseUrl, gson);
+ releaseService = new ReleaseOpenApiService(client, baseUrl, gson);
+ }
+
+ /**
+ * Get the environment and cluster information
+ */
+ public List getEnvClusterInfo(String appId) {
+ return appService.getEnvClusterInfo(appId);
+ }
+
+ /**
+ * Get the namespaces
+ */
+ public List getNamespaces(String appId, String env, String clusterName) {
+ return namespaceService.getNamespaces(appId, env, clusterName);
+ }
+
+ /**
+ * Get the namespace
+ */
+ public OpenNamespaceDTO getNamespace(String appId, String env, String clusterName, String namespaceName) {
+ return namespaceService.getNamespace(appId, env, clusterName, namespaceName);
+ }
+
+ /**
+ * Create the app namespace
+ */
+ public OpenAppNamespaceDTO createAppNamespace(OpenAppNamespaceDTO appNamespaceDTO) {
+ return namespaceService.createAppNamespace(appNamespaceDTO);
+ }
+
+ /**
+ * Get the namespace lock
+ */
+ public OpenNamespaceLockDTO getNamespaceLock(String appId, String env, String clusterName, String namespaceName) {
+ return namespaceService.getNamespaceLock(appId, env, clusterName, namespaceName);
+ }
+
+ /**
+ * Add config
+ * @return the created config
+ */
+ public OpenItemDTO createItem(String appId, String env, String clusterName, String namespaceName,
+ OpenItemDTO itemDTO) {
+ return itemService.createItem(appId, env, clusterName, namespaceName, itemDTO);
+ }
+
+ /**
+ * Update config
+ */
+ public void updateItem(String appId, String env, String clusterName, String namespaceName, OpenItemDTO itemDTO) {
+ itemService.updateItem(appId, env, clusterName, namespaceName, itemDTO);
+ }
+
+ /**
+ * Create config if not exists or update config if already exists
+ */
+ public void createOrUpdateItem(String appId, String env, String clusterName, String namespaceName, OpenItemDTO itemDTO) {
+ itemService.createOrUpdateItem(appId, env, clusterName, namespaceName, itemDTO);
+ }
+
+ /**
+ * Remove config
+ *
+ * @param operator the user who removes the item
+ */
+ public void removeItem(String appId, String env, String clusterName, String namespaceName, String key,
+ String operator) {
+ itemService.removeItem(appId, env, clusterName, namespaceName, key, operator);
+ }
+
+ /**
+ * publish namespace
+ * @return the released configurations
+ */
+ public OpenReleaseDTO publishNamespace(String appId, String env, String clusterName, String namespaceName,
+ NamespaceReleaseDTO releaseDTO) {
+ return releaseService.publishNamespace(appId, env, clusterName, namespaceName, releaseDTO);
+ }
+
+ /**
+ * @return the latest active release information or null
if not found
+ */
+ public OpenReleaseDTO getLatestActiveRelease(String appId, String env, String clusterName, String namespaceName) {
+ return releaseService.getLatestActiveRelease(appId, env, clusterName, namespaceName);
+ }
+
+
+ public String getPortalUrl() {
+ return portalUrl;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public static ApolloOpenApiClientBuilder newBuilder() {
+ return new ApolloOpenApiClientBuilder();
+ }
+
+ public static class ApolloOpenApiClientBuilder {
+
+ private String portalUrl;
+ private String token;
+ private int connectTimeout = -1;
+ private int readTimeout = -1;
+
+ /**
+ * @param portalUrl The apollo portal url, e.g http://localhost:8070
+ */
+ public ApolloOpenApiClientBuilder withPortalUrl(String portalUrl) {
+ this.portalUrl = portalUrl;
+ return this;
+ }
+
+ /**
+ * @param token The authorization token, e.g. e16e5cd903fd0c97a116c873b448544b9d086de8
+ */
+ public ApolloOpenApiClientBuilder withToken(String token) {
+ this.token = token;
+ return this;
+ }
+
+ /**
+ * @param connectTimeout an int that specifies the connect timeout value in milliseconds
+ */
+ public ApolloOpenApiClientBuilder withConnectTimeout(int connectTimeout) {
+ this.connectTimeout = connectTimeout;
+ return this;
+ }
+
+ /**
+ * @param readTimeout an int that specifies the timeout value to be used in milliseconds
+ */
+ public ApolloOpenApiClientBuilder withReadTimeout(int readTimeout) {
+ this.readTimeout = readTimeout;
+ return this;
+ }
+
+ public ApolloOpenApiClient build() {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(portalUrl), "Portal url should not be null or empty!");
+ Preconditions.checkArgument(portalUrl.startsWith("http://") || portalUrl.startsWith("https://"), "Portal url should start with http:// or https://" );
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(token), "Token should not be null or empty!");
+
+ if (connectTimeout < 0) {
+ connectTimeout = ApolloOpenApiConstants.DEFAULT_CONNECT_TIMEOUT;
+ }
+
+ if (readTimeout < 0) {
+ readTimeout = ApolloOpenApiConstants.DEFAULT_READ_TIMEOUT;
+ }
+
+ RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(connectTimeout)
+ .setSocketTimeout(readTimeout).build();
+
+ return new ApolloOpenApiClient(portalUrl, token, requestConfig);
+ }
+ }
+}
diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/constant/ApolloOpenApiConstants.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/constant/ApolloOpenApiConstants.java
new file mode 100644
index 0000000000000000000000000000000000000000..e88759c9ce9b687ec426bc20fe95158841819767
--- /dev/null
+++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/constant/ApolloOpenApiConstants.java
@@ -0,0 +1,9 @@
+package com.ctrip.framework.apollo.openapi.client.constant;
+
+public interface ApolloOpenApiConstants {
+ int DEFAULT_CONNECT_TIMEOUT = 1000; //1 second
+ int DEFAULT_READ_TIMEOUT = 5000; //5 seconds
+ String OPEN_API_V1_PREFIX = "/openapi/v1";
+ String JSON_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
+
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..af20a904ceb5a160fc85d91a09b857cfb654a26b
--- /dev/null
+++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/exception/ApolloOpenApiException.java
@@ -0,0 +1,9 @@
+package com.ctrip.framework.apollo.openapi.client.exception;
+
+public class ApolloOpenApiException extends RuntimeException {
+
+ 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,
+ message));
+ }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..3093df6cd44101aade5f55a81b2d972abe32d145
--- /dev/null
+++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/AbstractOpenApiService.java
@@ -0,0 +1,106 @@
+package com.ctrip.framework.apollo.openapi.client.service;
+
+import com.ctrip.framework.apollo.openapi.client.exception.ApolloOpenApiException;
+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.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();
+
+ private final String baseUrl;
+
+ protected final CloseableHttpClient client;
+ protected final Gson gson;
+
+ AbstractOpenApiService(CloseableHttpClient client, String baseUrl, Gson gson) {
+ this.client = client;
+ this.baseUrl = baseUrl;
+ this.gson = gson;
+ }
+
+ protected CloseableHttpResponse get(String path) throws IOException {
+ HttpGet get = new HttpGet(String.format("%s/%s", baseUrl, path));
+
+ return execute(get);
+ }
+
+ protected CloseableHttpResponse post(String path, Object entity) throws IOException {
+ HttpPost post = new HttpPost(String.format("%s/%s", baseUrl, path));
+
+ return execute(post, entity);
+ }
+
+ protected CloseableHttpResponse put(String path, Object entity) throws IOException {
+ HttpPut put = new HttpPut(String.format("%s/%s", baseUrl, path));
+
+ return execute(put, entity);
+ }
+
+ protected CloseableHttpResponse delete(String path) throws IOException {
+ HttpDelete delete = new HttpDelete(String.format("%s/%s", baseUrl, path));
+
+ 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));
+
+ return execute(requestBase);
+ }
+
+ private CloseableHttpResponse execute(HttpUriRequest request) throws IOException {
+ CloseableHttpResponse response = client.execute(request);
+
+ checkHttpResponseStatus(response);
+
+ return response;
+ }
+
+
+ private void checkHttpResponseStatus(HttpResponse response) {
+ if (response.getStatusLine().getStatusCode() == 200) {
+ return;
+ }
+
+ StatusLine status = response.getStatusLine();
+ String message = "";
+ try {
+ message = EntityUtils.toString(response.getEntity());
+ } catch (IOException e) {
+ //ignore
+ }
+
+ throw new ApolloOpenApiException(status.getStatusCode(), status.getReasonPhrase(), message);
+ }
+
+ protected void checkNotEmpty(String value, String name) {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(value), name + " should not be null or empty");
+ }
+
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..8358c1c90be7be63bf8bdee2e1be24ba210be24c
--- /dev/null
+++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/AppOpenApiService.java
@@ -0,0 +1,31 @@
+package com.ctrip.framework.apollo.openapi.client.service;
+
+import com.ctrip.framework.apollo.openapi.dto.OpenEnvClusterDTO;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Type;
+import java.util.List;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.util.EntityUtils;
+
+public class AppOpenApiService extends AbstractOpenApiService {
+ private static final Type OPEN_ENV_CLUSTER_DTO_LIST_TYPE = new TypeToken>() {
+ }.getType();
+
+ public AppOpenApiService(CloseableHttpClient client, String baseUrl, Gson gson) {
+ super(client, baseUrl, gson);
+ }
+
+ public List getEnvClusterInfo(String appId) {
+ checkNotEmpty(appId, "App id");
+
+ String path = String.format("apps/%s/envclusters", escapePath(appId));
+
+ try (CloseableHttpResponse response = get(path)) {
+ 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);
+ }
+ }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..dd81c5bd428bad65e66de1538aae5138d5724203
--- /dev/null
+++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/ItemOpenApiService.java
@@ -0,0 +1,124 @@
+package com.ctrip.framework.apollo.openapi.client.service;
+
+import com.ctrip.framework.apollo.core.ConfigConsts;
+import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
+import com.google.common.base.Strings;
+import com.google.gson.Gson;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.util.EntityUtils;
+
+public class ItemOpenApiService extends AbstractOpenApiService {
+
+ public ItemOpenApiService(CloseableHttpClient client, String baseUrl, Gson gson) {
+ super(client, baseUrl, gson);
+ }
+
+ public OpenItemDTO createItem(String appId, String env, String clusterName, String namespaceName, OpenItemDTO itemDTO) {
+ if (Strings.isNullOrEmpty(clusterName)) {
+ clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
+ }
+ if (Strings.isNullOrEmpty(namespaceName)) {
+ namespaceName = ConfigConsts.NAMESPACE_APPLICATION;
+ }
+
+ checkNotEmpty(appId, "App id");
+ checkNotEmpty(env, "Env");
+ checkNotEmpty(itemDTO.getKey(), "Item key");
+ checkNotEmpty(itemDTO.getValue(), "Item value");
+ 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));
+
+ try (CloseableHttpResponse response = post(path, itemDTO)) {
+ return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenItemDTO.class);
+ } catch (Throwable ex) {
+ throw new RuntimeException(String
+ .format("Create item: %s for appId: %s, cluster: %s, namespace: %s in env: %s failed", itemDTO.getKey(),
+ appId, clusterName, namespaceName, env), ex);
+ }
+ }
+
+ public void updateItem(String appId, String env, String clusterName, String namespaceName, OpenItemDTO itemDTO) {
+ if (Strings.isNullOrEmpty(clusterName)) {
+ clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
+ }
+ if (Strings.isNullOrEmpty(namespaceName)) {
+ namespaceName = ConfigConsts.NAMESPACE_APPLICATION;
+ }
+
+ checkNotEmpty(appId, "App id");
+ checkNotEmpty(env, "Env");
+ checkNotEmpty(itemDTO.getKey(), "Item key");
+ checkNotEmpty(itemDTO.getValue(), "Item value");
+ 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()));
+
+ try (CloseableHttpResponse ignored = put(path, 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(),
+ appId, clusterName, namespaceName, env), ex);
+ }
+ }
+
+ public void createOrUpdateItem(String appId, String env, String clusterName, String namespaceName, OpenItemDTO itemDTO) {
+ if (Strings.isNullOrEmpty(clusterName)) {
+ clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
+ }
+ if (Strings.isNullOrEmpty(namespaceName)) {
+ namespaceName = ConfigConsts.NAMESPACE_APPLICATION;
+ }
+
+ checkNotEmpty(appId, "App id");
+ checkNotEmpty(env, "Env");
+ checkNotEmpty(itemDTO.getKey(), "Item key");
+ checkNotEmpty(itemDTO.getValue(), "Item value");
+ checkNotEmpty(itemDTO.getDataChangeCreatedBy(), "Item created by");
+
+ if (Strings.isNullOrEmpty(itemDTO.getDataChangeLastModifiedBy())) {
+ 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()));
+
+ try (CloseableHttpResponse ignored = put(path, 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(),
+ appId, clusterName, namespaceName, env), ex);
+ }
+ }
+
+ public void removeItem(String appId, String env, String clusterName, String namespaceName, String key, String operator) {
+ if (Strings.isNullOrEmpty(clusterName)) {
+ clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
+ }
+ if (Strings.isNullOrEmpty(namespaceName)) {
+ namespaceName = ConfigConsts.NAMESPACE_APPLICATION;
+ }
+
+ checkNotEmpty(appId, "App id");
+ checkNotEmpty(env, "Env");
+ 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));
+
+ try (CloseableHttpResponse ignored = delete(path)) {
+ } catch (Throwable ex) {
+ throw new RuntimeException(String
+ .format("Remove item: %s for appId: %s, cluster: %s, namespace: %s in env: %s failed", key, appId,
+ clusterName, namespaceName, env), ex);
+ }
+
+ }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..15fa8f64dfd5a7aa8b6c6f45e814bcb305ab1c49
--- /dev/null
+++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/NamespaceOpenApiService.java
@@ -0,0 +1,109 @@
+package com.ctrip.framework.apollo.openapi.client.service;
+
+import com.ctrip.framework.apollo.core.ConfigConsts;
+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 com.ctrip.framework.apollo.openapi.dto.OpenNamespaceLockDTO;
+import com.google.common.base.Strings;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Type;
+import java.util.List;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.util.EntityUtils;
+
+public class NamespaceOpenApiService extends AbstractOpenApiService {
+ private static final Type OPEN_NAMESPACE_DTO_LIST_TYPE = new TypeToken>() {
+ }.getType();
+
+ public NamespaceOpenApiService(CloseableHttpClient client, String baseUrl, Gson gson) {
+ super(client, baseUrl, gson);
+ }
+
+ public OpenNamespaceDTO getNamespace(String appId, String env, String clusterName, String namespaceName) {
+ if (Strings.isNullOrEmpty(clusterName)) {
+ clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
+ }
+ if (Strings.isNullOrEmpty(namespaceName)) {
+ namespaceName = ConfigConsts.NAMESPACE_APPLICATION;
+ }
+
+ 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));
+
+ try (CloseableHttpResponse response = get(path)) {
+ return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenNamespaceDTO.class);
+ } catch (Throwable ex) {
+ throw new RuntimeException(String
+ .format("Get namespace for appId: %s, cluster: %s, namespace: %s in env: %s failed", appId, clusterName,
+ namespaceName, env), ex);
+ }
+ }
+
+ public List getNamespaces(String appId, String env, String clusterName) {
+ if (Strings.isNullOrEmpty(clusterName)) {
+ clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
+ }
+
+ checkNotEmpty(appId, "App id");
+ checkNotEmpty(env, "Env");
+
+ String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces", escapePath(env), escapePath(appId),
+ escapePath(clusterName));
+
+ try (CloseableHttpResponse response = get(path)) {
+ return gson.fromJson(EntityUtils.toString(response.getEntity()), OPEN_NAMESPACE_DTO_LIST_TYPE);
+ } catch (Throwable ex) {
+ throw new RuntimeException(String
+ .format("Get namespaces for appId: %s, cluster: %s in env: %s failed", appId, clusterName, env), ex);
+ }
+ }
+
+ public OpenAppNamespaceDTO createAppNamespace(OpenAppNamespaceDTO appNamespaceDTO) {
+ checkNotEmpty(appNamespaceDTO.getAppId(), "App id");
+ checkNotEmpty(appNamespaceDTO.getName(), "Name");
+ checkNotEmpty(appNamespaceDTO.getDataChangeCreatedBy(), "Created by");
+
+ if (Strings.isNullOrEmpty(appNamespaceDTO.getFormat())) {
+ appNamespaceDTO.setFormat(ConfigFileFormat.Properties.getValue());
+ }
+
+ String path = String.format("apps/%s/appnamespaces", escapePath(appNamespaceDTO.getAppId()));
+
+ try (CloseableHttpResponse response = post(path, appNamespaceDTO)) {
+ return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenAppNamespaceDTO.class);
+ } catch (Throwable ex) {
+ throw new RuntimeException(String
+ .format("Create app namespace: %s for appId: %s, format: %s failed", appNamespaceDTO.getName(),
+ appNamespaceDTO.getAppId(), appNamespaceDTO.getFormat()), ex);
+ }
+ }
+
+ public OpenNamespaceLockDTO getNamespaceLock(String appId, String env, String clusterName, String namespaceName) {
+ if (Strings.isNullOrEmpty(clusterName)) {
+ clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
+ }
+ if (Strings.isNullOrEmpty(namespaceName)) {
+ namespaceName = ConfigConsts.NAMESPACE_APPLICATION;
+ }
+
+ 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));
+
+ try (CloseableHttpResponse response = get(path)) {
+ return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenNamespaceLockDTO.class);
+ } catch (Throwable ex) {
+ throw new RuntimeException(String
+ .format("Get namespace lock for appId: %s, cluster: %s, namespace: %s in env: %s failed", appId, clusterName,
+ namespaceName, env), ex);
+ }
+ }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..f06023ac4d06c7ebea95741ac006a237cf2227dd
--- /dev/null
+++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/client/service/ReleaseOpenApiService.java
@@ -0,0 +1,67 @@
+package com.ctrip.framework.apollo.openapi.client.service;
+
+import com.ctrip.framework.apollo.core.ConfigConsts;
+import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;
+import com.ctrip.framework.apollo.openapi.dto.OpenReleaseDTO;
+import com.google.common.base.Strings;
+import com.google.gson.Gson;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.util.EntityUtils;
+
+public class ReleaseOpenApiService extends AbstractOpenApiService {
+
+ public ReleaseOpenApiService(CloseableHttpClient client, String baseUrl, Gson gson) {
+ super(client, baseUrl, gson);
+ }
+
+ public OpenReleaseDTO publishNamespace(String appId, String env, String clusterName, String namespaceName,
+ NamespaceReleaseDTO releaseDTO) {
+ if (Strings.isNullOrEmpty(clusterName)) {
+ clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
+ }
+ if (Strings.isNullOrEmpty(namespaceName)) {
+ namespaceName = ConfigConsts.NAMESPACE_APPLICATION;
+ }
+
+ checkNotEmpty(appId, "App id");
+ checkNotEmpty(env, "Env");
+ 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));
+
+ try (CloseableHttpResponse response = post(path, releaseDTO)) {
+ return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenReleaseDTO.class);
+ } catch (Throwable ex) {
+ throw new RuntimeException(String
+ .format("Release namespace: %s for appId: %s, cluster: %s in env: %s failed", namespaceName, appId,
+ clusterName, env), ex);
+ }
+ }
+
+ public OpenReleaseDTO getLatestActiveRelease(String appId, String env, String clusterName, String namespaceName) {
+ if (Strings.isNullOrEmpty(clusterName)) {
+ clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
+ }
+ if (Strings.isNullOrEmpty(namespaceName)) {
+ namespaceName = ConfigConsts.NAMESPACE_APPLICATION;
+ }
+
+ 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));
+
+ try (CloseableHttpResponse response = get(path)) {
+ return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenReleaseDTO.class);
+ } catch (Throwable ex) {
+ throw new RuntimeException(String
+ .format("Get latest active release for appId: %s, cluster: %s, namespace: %s in env: %s failed", appId,
+ clusterName, namespaceName, env), ex);
+ }
+ }
+
+}
diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/BaseDTO.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/BaseDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..dbc2645111336deb2701b92a844409c158a0eb5d
--- /dev/null
+++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/BaseDTO.java
@@ -0,0 +1,46 @@
+package com.ctrip.framework.apollo.openapi.dto;
+
+import java.util.Date;
+
+public class BaseDTO {
+
+ protected String dataChangeCreatedBy;
+
+ protected String dataChangeLastModifiedBy;
+
+ protected Date dataChangeCreatedTime;
+
+ protected Date dataChangeLastModifiedTime;
+
+ public String getDataChangeCreatedBy() {
+ return dataChangeCreatedBy;
+ }
+
+ public void setDataChangeCreatedBy(String dataChangeCreatedBy) {
+ this.dataChangeCreatedBy = dataChangeCreatedBy;
+ }
+
+ public String getDataChangeLastModifiedBy() {
+ return dataChangeLastModifiedBy;
+ }
+
+ public void setDataChangeLastModifiedBy(String dataChangeLastModifiedBy) {
+ this.dataChangeLastModifiedBy = dataChangeLastModifiedBy;
+ }
+
+ public Date getDataChangeCreatedTime() {
+ return dataChangeCreatedTime;
+ }
+
+ public void setDataChangeCreatedTime(Date dataChangeCreatedTime) {
+ this.dataChangeCreatedTime = dataChangeCreatedTime;
+ }
+
+ public Date getDataChangeLastModifiedTime() {
+ return dataChangeLastModifiedTime;
+ }
+
+ public void setDataChangeLastModifiedTime(Date dataChangeLastModifiedTime) {
+ this.dataChangeLastModifiedTime = dataChangeLastModifiedTime;
+ }
+}
diff --git a/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/NamespaceReleaseDTO.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/NamespaceReleaseDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..c86759c0f249f2ac600d49fe359ef60ad8e6c9ca
--- /dev/null
+++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/NamespaceReleaseDTO.java
@@ -0,0 +1,42 @@
+package com.ctrip.framework.apollo.openapi.dto;
+
+
+public class NamespaceReleaseDTO {
+
+ private String releaseTitle;
+ private String releaseComment;
+ private String releasedBy;
+ private boolean isEmergencyPublish;
+
+ public String getReleaseTitle() {
+ return releaseTitle;
+ }
+
+ public void setReleaseTitle(String releaseTitle) {
+ this.releaseTitle = releaseTitle;
+ }
+
+ public String getReleaseComment() {
+ return releaseComment;
+ }
+
+ public void setReleaseComment(String releaseComment) {
+ this.releaseComment = releaseComment;
+ }
+
+ public String getReleasedBy() {
+ return releasedBy;
+ }
+
+ public void setReleasedBy(String releasedBy) {
+ this.releasedBy = releasedBy;
+ }
+
+ public boolean isEmergencyPublish() {
+ return isEmergencyPublish;
+ }
+
+ public void setEmergencyPublish(boolean emergencyPublish) {
+ isEmergencyPublish = emergencyPublish;
+ }
+}
diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenAppNamespaceDTO.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenAppNamespaceDTO.java
similarity index 94%
rename from apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenAppNamespaceDTO.java
rename to apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenAppNamespaceDTO.java
index e7b1c0bac10c82238980b07ceac958936f8ac688..c067403d5666117df12dbe404d7c2525d52d19e7 100644
--- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenAppNamespaceDTO.java
+++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenAppNamespaceDTO.java
@@ -1,8 +1,5 @@
package com.ctrip.framework.apollo.openapi.dto;
-
-import com.ctrip.framework.apollo.common.dto.BaseDTO;
-
public class OpenAppNamespaceDTO extends BaseDTO {
private String name;
diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenEnvClusterDTO.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenEnvClusterDTO.java
similarity index 100%
rename from apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenEnvClusterDTO.java
rename to apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenEnvClusterDTO.java
diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenItemDTO.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenItemDTO.java
similarity index 90%
rename from apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenItemDTO.java
rename to apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenItemDTO.java
index 23393d17cae4db5d1d55faf523074c06fb27bb1f..622ddc04da6d61a5d20402d504aca3c6032dd033 100644
--- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenItemDTO.java
+++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenItemDTO.java
@@ -1,7 +1,5 @@
package com.ctrip.framework.apollo.openapi.dto;
-import com.ctrip.framework.apollo.common.dto.BaseDTO;
-
public class OpenItemDTO extends BaseDTO {
private String key;
diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenNamespaceDTO.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenNamespaceDTO.java
similarity index 95%
rename from apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenNamespaceDTO.java
rename to apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenNamespaceDTO.java
index 70658795e8ee5696f54af3cf86c9bb4f8615b168..b3921eaad9726258ebadf600770dcc1393aa7530 100644
--- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenNamespaceDTO.java
+++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenNamespaceDTO.java
@@ -1,7 +1,5 @@
package com.ctrip.framework.apollo.openapi.dto;
-import com.ctrip.framework.apollo.common.dto.BaseDTO;
-
import java.util.List;
public class OpenNamespaceDTO extends BaseDTO {
diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenNamespaceLockDTO.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenNamespaceLockDTO.java
similarity index 100%
rename from apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenNamespaceLockDTO.java
rename to apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenNamespaceLockDTO.java
diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenReleaseDTO.java b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenReleaseDTO.java
similarity index 95%
rename from apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenReleaseDTO.java
rename to apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenReleaseDTO.java
index 99befb6c179d1e1a2620eb33ed0d4a33f16a1ae1..5d1c21c76ad1b5aafeaeb39f00c5c670c8f2c824 100644
--- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenReleaseDTO.java
+++ b/apollo-openapi/src/main/java/com/ctrip/framework/apollo/openapi/dto/OpenReleaseDTO.java
@@ -1,7 +1,5 @@
package com.ctrip.framework.apollo.openapi.dto;
-import com.ctrip.framework.apollo.common.dto.BaseDTO;
-
import java.util.Map;
public class OpenReleaseDTO extends BaseDTO {
diff --git a/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/ApolloOpenApiClientTest.java b/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/ApolloOpenApiClientTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2aee85260028c8f8ff820cba605ba790d2268f49
--- /dev/null
+++ b/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/ApolloOpenApiClientTest.java
@@ -0,0 +1,27 @@
+package com.ctrip.framework.apollo.openapi.client;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class ApolloOpenApiClientTest {
+
+ @Test
+ public void testCreate() {
+ String someUrl = "http://someUrl";
+ String someToken = "someToken";
+
+ ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder().withPortalUrl(someUrl).withToken(someToken).build();
+
+ assertEquals(someUrl, client.getPortalUrl());
+ assertEquals(someToken, client.getToken());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateWithInvalidUrl() {
+ String someInvalidUrl = "someInvalidUrl";
+ String someToken = "someToken";
+
+ ApolloOpenApiClient.newBuilder().withPortalUrl(someInvalidUrl).withToken(someToken).build();
+ }
+}
diff --git a/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/AbstractOpenApiServiceTest.java b/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/AbstractOpenApiServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..7571a5f4267ce18a0e85c7d10cfa676df871c2ad
--- /dev/null
+++ b/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/AbstractOpenApiServiceTest.java
@@ -0,0 +1,42 @@
+package com.ctrip.framework.apollo.openapi.client.service;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+
+import com.ctrip.framework.apollo.openapi.client.constant.ApolloOpenApiConstants;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.apache.http.StatusLine;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+abstract class AbstractOpenApiServiceTest {
+ @Mock
+ protected CloseableHttpClient httpClient;
+ @Mock
+ protected CloseableHttpResponse someHttpResponse;
+ @Mock
+ protected StatusLine statusLine;
+
+ protected Gson gson;
+
+ protected String someBaseUrl;
+
+ @Before
+ public void setUp() throws Exception {
+ gson = new GsonBuilder().setDateFormat(ApolloOpenApiConstants.JSON_DATE_FORMAT).create();
+ someBaseUrl = "http://someBaseUrl";
+
+ when(someHttpResponse.getStatusLine()).thenReturn(statusLine);
+ when(statusLine.getStatusCode()).thenReturn(200);
+
+ when(httpClient.execute(any(HttpUriRequest.class))).thenReturn(someHttpResponse);
+ }
+
+}
diff --git a/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/AppOpenApiServiceTest.java b/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/AppOpenApiServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a04ec1454d603f5f17edb7dc4be37d3e4f63155
--- /dev/null
+++ b/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/AppOpenApiServiceTest.java
@@ -0,0 +1,52 @@
+package com.ctrip.framework.apollo.openapi.client.service;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.entity.StringEntity;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+public class AppOpenApiServiceTest extends AbstractOpenApiServiceTest {
+
+ private AppOpenApiService appOpenApiService;
+
+ private String someAppId;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ someAppId = "someAppId";
+
+ StringEntity responseEntity = new StringEntity("[]");
+ when(someHttpResponse.getEntity()).thenReturn(responseEntity);
+
+ appOpenApiService = new AppOpenApiService(httpClient, someBaseUrl, gson);
+ }
+
+ @Test
+ public void testGetEnvClusterInfo() throws Exception {
+ final ArgumentCaptor request = ArgumentCaptor.forClass(HttpGet.class);
+
+ appOpenApiService.getEnvClusterInfo(someAppId);
+
+ verify(httpClient, times(1)).execute(request.capture());
+
+ HttpGet get = request.getValue();
+
+ assertEquals(String
+ .format("%s/apps/%s/envclusters", someBaseUrl, someAppId), get.getURI().toString());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testGetEnvClusterInfoWithError() throws Exception {
+ when(statusLine.getStatusCode()).thenReturn(500);
+
+ appOpenApiService.getEnvClusterInfo(someAppId);
+ }
+}
diff --git a/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/ItemOpenApiServiceTest.java b/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/ItemOpenApiServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5fa7325dc4a6f49d88161a94595a96cf3f7f6d6c
--- /dev/null
+++ b/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/ItemOpenApiServiceTest.java
@@ -0,0 +1,195 @@
+package com.ctrip.framework.apollo.openapi.client.service;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.util.EntityUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+public class ItemOpenApiServiceTest extends AbstractOpenApiServiceTest {
+
+ private ItemOpenApiService itemOpenApiService;
+
+ private String someAppId;
+ private String someEnv;
+ private String someCluster;
+ private String someNamespace;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ someAppId = "someAppId";
+ someEnv = "someEnv";
+ someCluster = "someCluster";
+ someNamespace = "someNamespace";
+
+ StringEntity responseEntity = new StringEntity("{}");
+ when(someHttpResponse.getEntity()).thenReturn(responseEntity);
+
+ itemOpenApiService = new ItemOpenApiService(httpClient, someBaseUrl, gson);
+ }
+
+ @Test
+ public void testCreateItem() throws Exception {
+ String someKey = "someKey";
+ String someValue = "someValue";
+ String someCreatedBy = "someCreatedBy";
+
+ OpenItemDTO itemDTO = new OpenItemDTO();
+ itemDTO.setKey(someKey);
+ itemDTO.setValue(someValue);
+ itemDTO.setDataChangeCreatedBy(someCreatedBy);
+
+ final ArgumentCaptor request = ArgumentCaptor.forClass(HttpPost.class);
+
+ itemOpenApiService.createItem(someAppId, someEnv, someCluster, someNamespace, itemDTO);
+
+ verify(httpClient, times(1)).execute(request.capture());
+
+ HttpPost post = request.getValue();
+
+ assertEquals(String
+ .format("%s/envs/%s/apps/%s/clusters/%s/namespaces/%s/items", someBaseUrl, someEnv, someAppId, someCluster,
+ someNamespace), post.getURI().toString());
+
+ StringEntity entity = (StringEntity) post.getEntity();
+
+ assertEquals(ContentType.APPLICATION_JSON.toString(), entity.getContentType().getValue());
+ assertEquals(gson.toJson(itemDTO), EntityUtils.toString(entity));
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testCreateItemWithError() throws Exception {
+ String someKey = "someKey";
+ String someValue = "someValue";
+ String someCreatedBy = "someCreatedBy";
+
+ OpenItemDTO itemDTO = new OpenItemDTO();
+ itemDTO.setKey(someKey);
+ itemDTO.setValue(someValue);
+ itemDTO.setDataChangeCreatedBy(someCreatedBy);
+
+ when(statusLine.getStatusCode()).thenReturn(400);
+
+ itemOpenApiService.createItem(someAppId, someEnv, someCluster, someNamespace, itemDTO);
+ }
+
+ @Test
+ public void testUpdateItem() throws Exception {
+ String someKey = "someKey";
+ String someValue = "someValue";
+ String someModifiedBy = "someModifiedBy";
+
+ OpenItemDTO itemDTO = new OpenItemDTO();
+ itemDTO.setKey(someKey);
+ itemDTO.setValue(someValue);
+ itemDTO.setDataChangeLastModifiedBy(someModifiedBy);
+
+ final ArgumentCaptor request = ArgumentCaptor.forClass(HttpPut.class);
+
+ itemOpenApiService.updateItem(someAppId, someEnv, someCluster, someNamespace, itemDTO);
+
+ verify(httpClient, times(1)).execute(request.capture());
+
+ HttpPut put = request.getValue();
+
+ assertEquals(String
+ .format("%s/envs/%s/apps/%s/clusters/%s/namespaces/%s/items/%s", someBaseUrl, someEnv, someAppId, someCluster,
+ someNamespace, someKey), put.getURI().toString());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testUpdateItemWithError() throws Exception {
+ String someKey = "someKey";
+ String someValue = "someValue";
+ String someModifiedBy = "someModifiedBy";
+
+ OpenItemDTO itemDTO = new OpenItemDTO();
+ itemDTO.setKey(someKey);
+ itemDTO.setValue(someValue);
+ itemDTO.setDataChangeLastModifiedBy(someModifiedBy);
+
+ when(statusLine.getStatusCode()).thenReturn(400);
+
+ itemOpenApiService.updateItem(someAppId, someEnv, someCluster, someNamespace, itemDTO);
+ }
+
+ @Test
+ public void testCreateOrUpdateItem() throws Exception {
+ String someKey = "someKey";
+ String someValue = "someValue";
+ String someCreatedBy = "someCreatedBy";
+
+ OpenItemDTO itemDTO = new OpenItemDTO();
+ itemDTO.setKey(someKey);
+ itemDTO.setValue(someValue);
+ itemDTO.setDataChangeCreatedBy(someCreatedBy);
+
+ final ArgumentCaptor request = ArgumentCaptor.forClass(HttpPut.class);
+
+ itemOpenApiService.createOrUpdateItem(someAppId, someEnv, someCluster, someNamespace, itemDTO);
+
+ verify(httpClient, times(1)).execute(request.capture());
+
+ HttpPut put = request.getValue();
+
+ assertEquals(String
+ .format("%s/envs/%s/apps/%s/clusters/%s/namespaces/%s/items/%s?createIfNotExists=true", someBaseUrl, someEnv,
+ someAppId, someCluster, someNamespace, someKey), put.getURI().toString());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testCreateOrUpdateItemWithError() throws Exception {
+ String someKey = "someKey";
+ String someValue = "someValue";
+ String someCreatedBy = "someCreatedBy";
+
+ OpenItemDTO itemDTO = new OpenItemDTO();
+ itemDTO.setKey(someKey);
+ itemDTO.setValue(someValue);
+ itemDTO.setDataChangeCreatedBy(someCreatedBy);
+
+ when(statusLine.getStatusCode()).thenReturn(400);
+
+ itemOpenApiService.createOrUpdateItem(someAppId, someEnv, someCluster, someNamespace, itemDTO);
+ }
+
+ @Test
+ public void testRemoveItem() throws Exception {
+ String someKey = "someKey";
+ String someOperator = "someOperator";
+
+ final ArgumentCaptor request = ArgumentCaptor.forClass(HttpDelete.class);
+
+ itemOpenApiService.removeItem(someAppId, someEnv, someCluster, someNamespace, someKey, someOperator);
+
+ verify(httpClient, times(1)).execute(request.capture());
+
+ HttpDelete delete = request.getValue();
+
+ assertEquals(String
+ .format("%s/envs/%s/apps/%s/clusters/%s/namespaces/%s/items/%s?operator=%s", someBaseUrl, someEnv,
+ someAppId, someCluster, someNamespace, someKey, someOperator), delete.getURI().toString());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testRemoveItemWithError() throws Exception {
+ String someKey = "someKey";
+ String someOperator = "someOperator";
+
+ when(statusLine.getStatusCode()).thenReturn(404);
+
+ itemOpenApiService.removeItem(someAppId, someEnv, someCluster, someNamespace, someKey, someOperator);
+ }
+}
diff --git a/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/NamespaceOpenApiServiceTest.java b/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/NamespaceOpenApiServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f03bb49b0de1d5e1f47678a8063db0900c98a75
--- /dev/null
+++ b/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/NamespaceOpenApiServiceTest.java
@@ -0,0 +1,145 @@
+package com.ctrip.framework.apollo.openapi.client.service;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.ctrip.framework.apollo.openapi.dto.OpenAppNamespaceDTO;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+public class NamespaceOpenApiServiceTest extends AbstractOpenApiServiceTest {
+
+ private NamespaceOpenApiService namespaceOpenApiService;
+
+ private String someAppId;
+ private String someEnv;
+ private String someCluster;
+ private String someNamespace;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ someAppId = "someAppId";
+ someEnv = "someEnv";
+ someCluster = "someCluster";
+ someNamespace = "someNamespace";
+
+ StringEntity responseEntity = new StringEntity("{}");
+ when(someHttpResponse.getEntity()).thenReturn(responseEntity);
+
+ namespaceOpenApiService = new NamespaceOpenApiService(httpClient, someBaseUrl, gson);
+ }
+
+ @Test
+ public void testGetNamespace() throws Exception {
+ final ArgumentCaptor request = ArgumentCaptor.forClass(HttpGet.class);
+
+ namespaceOpenApiService.getNamespace(someAppId, someEnv, someCluster, someNamespace);
+
+ verify(httpClient, times(1)).execute(request.capture());
+
+ HttpGet get = request.getValue();
+
+ assertEquals(String
+ .format("%s/envs/%s/apps/%s/clusters/%s/namespaces/%s", someBaseUrl, someEnv, someAppId, someCluster,
+ someNamespace), get.getURI().toString());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testGetNamespaceWithError() throws Exception {
+ when(statusLine.getStatusCode()).thenReturn(404);
+
+ namespaceOpenApiService.getNamespace(someAppId, someEnv, someCluster, someNamespace);
+ }
+
+ @Test
+ public void testGetNamespaces() throws Exception {
+ StringEntity responseEntity = new StringEntity("[]");
+ when(someHttpResponse.getEntity()).thenReturn(responseEntity);
+
+ final ArgumentCaptor request = ArgumentCaptor.forClass(HttpGet.class);
+
+ namespaceOpenApiService.getNamespaces(someAppId, someEnv, someCluster);
+
+ verify(httpClient, times(1)).execute(request.capture());
+
+ HttpGet get = request.getValue();
+
+ assertEquals(String
+ .format("%s/envs/%s/apps/%s/clusters/%s/namespaces", someBaseUrl, someEnv, someAppId, someCluster),
+ get.getURI().toString());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testGetNamespacesWithError() throws Exception {
+ when(statusLine.getStatusCode()).thenReturn(404);
+
+ namespaceOpenApiService.getNamespaces(someAppId, someEnv, someCluster);
+ }
+
+ @Test
+ public void testCreateAppNamespace() throws Exception {
+ String someName = "someName";
+ String someCreatedBy = "someCreatedBy";
+
+ OpenAppNamespaceDTO appNamespaceDTO = new OpenAppNamespaceDTO();
+ appNamespaceDTO.setAppId(someAppId);
+ appNamespaceDTO.setName(someName);
+ appNamespaceDTO.setDataChangeCreatedBy(someCreatedBy);
+
+ final ArgumentCaptor request = ArgumentCaptor.forClass(HttpPost.class);
+
+ namespaceOpenApiService.createAppNamespace(appNamespaceDTO);
+
+ verify(httpClient, times(1)).execute(request.capture());
+
+ HttpPost post = request.getValue();
+
+ assertEquals(String.format("%s/apps/%s/appnamespaces", someBaseUrl, someAppId), post.getURI().toString());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testCreateAppNamespaceWithError() throws Exception {
+ String someName = "someName";
+ String someCreatedBy = "someCreatedBy";
+
+ OpenAppNamespaceDTO appNamespaceDTO = new OpenAppNamespaceDTO();
+ appNamespaceDTO.setAppId(someAppId);
+ appNamespaceDTO.setName(someName);
+ appNamespaceDTO.setDataChangeCreatedBy(someCreatedBy);
+
+ when(statusLine.getStatusCode()).thenReturn(400);
+
+ namespaceOpenApiService.createAppNamespace(appNamespaceDTO);
+ }
+
+ @Test
+ public void testGetNamespaceLock() throws Exception {
+ final ArgumentCaptor request = ArgumentCaptor.forClass(HttpGet.class);
+
+ namespaceOpenApiService.getNamespaceLock(someAppId, someEnv, someCluster, someNamespace);
+
+ verify(httpClient, times(1)).execute(request.capture());
+
+ HttpGet post = request.getValue();
+
+ assertEquals(String
+ .format("%s/envs/%s/apps/%s/clusters/%s/namespaces/%s/lock", someBaseUrl, someEnv, someAppId, someCluster,
+ someNamespace), post.getURI().toString());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testGetNamespaceLockWithError() throws Exception {
+ when(statusLine.getStatusCode()).thenReturn(404);
+
+ namespaceOpenApiService.getNamespaceLock(someAppId, someEnv, someCluster, someNamespace);
+ }
+}
diff --git a/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/ReleaseOpenApiServiceTest.java b/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/ReleaseOpenApiServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae1f86da0fbb857e513607f84391efd0a53cb95f
--- /dev/null
+++ b/apollo-openapi/src/test/java/com/ctrip/framework/apollo/openapi/client/service/ReleaseOpenApiServiceTest.java
@@ -0,0 +1,98 @@
+package com.ctrip.framework.apollo.openapi.client.service;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+public class ReleaseOpenApiServiceTest extends AbstractOpenApiServiceTest {
+
+ private ReleaseOpenApiService releaseOpenApiService;
+
+ private String someAppId;
+ private String someEnv;
+ private String someCluster;
+ private String someNamespace;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ someAppId = "someAppId";
+ someEnv = "someEnv";
+ someCluster = "someCluster";
+ someNamespace = "someNamespace";
+
+ StringEntity responseEntity = new StringEntity("{}");
+ when(someHttpResponse.getEntity()).thenReturn(responseEntity);
+
+ releaseOpenApiService = new ReleaseOpenApiService(httpClient, someBaseUrl, gson);
+ }
+
+ @Test
+ public void testPublishNamespace() throws Exception {
+ String someReleaseTitle = "someReleaseTitle";
+ String someReleasedBy = "someReleasedBy";
+
+ NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();
+ namespaceReleaseDTO.setReleaseTitle(someReleaseTitle);
+ namespaceReleaseDTO.setReleasedBy(someReleasedBy);
+
+ final ArgumentCaptor request = ArgumentCaptor.forClass(HttpPost.class);
+
+ releaseOpenApiService.publishNamespace(someAppId, someEnv, someCluster, someNamespace, namespaceReleaseDTO);
+
+ verify(httpClient, times(1)).execute(request.capture());
+
+ HttpPost post = request.getValue();
+
+ assertEquals(String
+ .format("%s/envs/%s/apps/%s/clusters/%s/namespaces/%s/releases", someBaseUrl, someEnv, someAppId, someCluster,
+ someNamespace), post.getURI().toString());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testPublishNamespaceWithError() throws Exception {
+ String someReleaseTitle = "someReleaseTitle";
+ String someReleasedBy = "someReleasedBy";
+
+ NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();
+ namespaceReleaseDTO.setReleaseTitle(someReleaseTitle);
+ namespaceReleaseDTO.setReleasedBy(someReleasedBy);
+
+ when(statusLine.getStatusCode()).thenReturn(400);
+
+ releaseOpenApiService.publishNamespace(someAppId, someEnv, someCluster, someNamespace, namespaceReleaseDTO);
+ }
+
+ @Test
+ public void testGetLatestActiveRelease() throws Exception {
+ final ArgumentCaptor request = ArgumentCaptor.forClass(HttpGet.class);
+
+ releaseOpenApiService.getLatestActiveRelease(someAppId, someEnv, someCluster, someNamespace);
+
+ verify(httpClient, times(1)).execute(request.capture());
+
+ HttpGet get = request.getValue();
+
+ assertEquals(String
+ .format("%s/envs/%s/apps/%s/clusters/%s/namespaces/%s/releases/latest", someBaseUrl, someEnv, someAppId, someCluster,
+ someNamespace), get.getURI().toString());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testGetLatestActiveReleaseWithError() throws Exception {
+ when(statusLine.getStatusCode()).thenReturn(400);
+
+ releaseOpenApiService.getLatestActiveRelease(someAppId, someEnv, someCluster, someNamespace);
+ }
+}
diff --git a/apollo-openapi/src/test/resources/log4j2.xml b/apollo-openapi/src/test/resources/log4j2.xml
new file mode 100644
index 0000000000000000000000000000000000000000..384e92d7a033e05cb41768b7aa7fcfee68cded82
--- /dev/null
+++ b/apollo-openapi/src/test/resources/log4j2.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apollo-portal/pom.xml b/apollo-portal/pom.xml
index c3dd9b62e09e316b57323ee32cdfce069ba171a8..0ea96a69169b4d9dc35fc98061e647b93f21a2eb 100644
--- a/apollo-portal/pom.xml
+++ b/apollo-portal/pom.xml
@@ -22,6 +22,10 @@
com.ctrip.framework.apollo
apollo-common
+
+ com.ctrip.framework.apollo
+ apollo-openapi
+
com.h2database
h2
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 61a2445f94e0de0ba08ddad1e83e86d1b119c3f8..5c93f344246c5d99bff73d54481f82ccdb8996a0 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
@@ -11,6 +11,7 @@ import com.ctrip.framework.apollo.portal.service.ItemService;
import com.ctrip.framework.apollo.portal.spi.UserService;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
@@ -20,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
+import org.springframework.web.client.HttpStatusCodeException;
@RestController("openapiItemController")
@@ -40,10 +42,10 @@ public class ItemController {
RequestPrecondition.checkArguments(
!StringUtils.isContainEmpty(item.getKey(), item.getValue(), item.getDataChangeCreatedBy()),
- "key,value,dataChangeCreatedBy 字段不能为空");
+ "key, value and dataChangeCreatedBy should not be null or empty");
if (userService.findByUserId(item.getDataChangeCreatedBy()) == null) {
- throw new BadRequestException("用户不存在.");
+ throw new BadRequestException("User " + item.getDataChangeCreatedBy() + " doesn't exist!");
}
ItemDTO toCreate = OpenApiBeanUtils.transformToItemDTO(item);
@@ -64,13 +66,14 @@ public class ItemController {
@RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items/{key:.+}", method = RequestMethod.PUT)
public void updateItem(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName,
- @PathVariable String key, @RequestBody OpenItemDTO item, HttpServletRequest request) {
+ @PathVariable String key, @RequestBody OpenItemDTO item,
+ @RequestParam(defaultValue = "false") boolean createIfNotExists, HttpServletRequest request) {
RequestPrecondition.checkArguments(item != null, "item payload can not be empty");
RequestPrecondition.checkArguments(
!StringUtils.isContainEmpty(item.getKey(), item.getValue(), item.getDataChangeLastModifiedBy()),
- "key,value,dataChangeLastModifiedBy can not be empty");
+ "key, value and dataChangeLastModifiedBy can not be empty");
RequestPrecondition.checkArguments(item.getKey().equals(key), "Key in path and payload is not consistent");
@@ -78,16 +81,25 @@ public class ItemController {
throw new BadRequestException("user(dataChangeLastModifiedBy) not exists");
}
- ItemDTO toUpdateItem = itemService.loadItem(Env.fromString(env), appId, clusterName, namespaceName, item.getKey());
- if (toUpdateItem == null) {
- throw new BadRequestException("item not exists");
+ try {
+ ItemDTO toUpdateItem = itemService
+ .loadItem(Env.fromString(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.fromString(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;
}
- //protect. only value,comment,lastModifiedBy can be modified
- toUpdateItem.setComment(item.getComment());
- toUpdateItem.setValue(item.getValue());
- toUpdateItem.setDataChangeLastModifiedBy(item.getDataChangeLastModifiedBy());
-
- itemService.updateItem(appId, Env.fromString(env), clusterName, namespaceName, toUpdateItem);
}
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 2a5911dcf79825e0e5c38d2200e1924c426cf9bf..55643d0e1eccdb884bbf1806033b84320c9b81c4 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
@@ -1,11 +1,12 @@
package com.ctrip.framework.apollo.openapi.v1.controller;
-
import com.ctrip.framework.apollo.common.dto.ReleaseDTO;
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.core.enums.Env;
import com.ctrip.framework.apollo.core.utils.StringUtils;
+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;
@@ -38,7 +39,7 @@ public class ReleaseController {
public OpenReleaseDTO createRelease(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName,
@PathVariable String namespaceName,
- @RequestBody NamespaceReleaseModel model,
+ @RequestBody NamespaceReleaseDTO model,
HttpServletRequest request) {
checkModel(model != null);
@@ -50,12 +51,14 @@ public class ReleaseController {
throw new BadRequestException("user(releaseBy) not exists");
}
- model.setAppId(appId);
- model.setEnv(Env.fromString(env).toString());
- model.setClusterName(clusterName);
- model.setNamespaceName(namespaceName);
+ NamespaceReleaseModel releaseModel = BeanUtils.transfrom(NamespaceReleaseModel.class, model);
+
+ releaseModel.setAppId(appId);
+ releaseModel.setEnv(Env.fromString(env).toString());
+ releaseModel.setClusterName(clusterName);
+ releaseModel.setNamespaceName(namespaceName);
- return OpenApiBeanUtils.transformFromReleaseDTO(releaseService.publish(model));
+ return OpenApiBeanUtils.transformFromReleaseDTO(releaseService.publish(releaseModel));
}
@RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/latest", method = RequestMethod.GET)
diff --git a/pom.xml b/pom.xml
index c693eebfd7ae6886ad57e15ad975841cb15496cf..deff3bcb44d13dfc7461a5667069b2ac29c44ca0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -104,6 +104,7 @@
apollo-assembly
apollo-demo
apollo-mockserver
+ apollo-openapi
@@ -148,6 +149,11 @@
apollo-portal
${project.version}
+
+ com.ctrip.framework.apollo
+ apollo-openapi
+ ${project.version}
+
com.dianping.cat