Skip to content

Commit

Permalink
feat(openapi): create app (#32)
Browse files Browse the repository at this point in the history
* feat(openapi): create app

* feat(openapi): create app

* fix: remove create app by env

* feat: support init admins when create app

* refactor: path "apps/create" -> "apps"

* test: move to IntegrationTest

* Update AppOpenApiServiceTest.java

* refactor: use OpenCreateAppDTO instead of OpenAppDTO

* test: testCreateAppThenCreateNamespaceThenRelease

* Update CHANGES.md

* add assignAppRoleToSelf to mark role permission

* testCreateAppButHaveNoAppRole

* refactor: use composite instead of extend OpenAppDTO

* Update OpenCreateAppDTO.java

* test: make test more complex. create the cluster too
  • Loading branch information
Anilople committed Aug 30, 2023
1 parent d2eca1f commit 5344bc4
Show file tree
Hide file tree
Showing 8 changed files with 416 additions and 4 deletions.
10 changes: 6 additions & 4 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ Release Notes.
Apollo Java 2.2.0

------------------
[refactor(apollo-client): Optimize the exception message when failing to retrieve configuration information.](https://github.com/apolloconfig/apollo-java/pull/22)
[Add JUnit5 extension support for apollo mock server.](https://github.com/apolloconfig/apollo-java/pull/25)
[Support concurrent loading of Config for different namespaces.](https://github.com/apolloconfig/apollo-java/pull/31)
[Fix snakeyaml 2.x compatibility issues](https://github.com/apolloconfig/apollo-java/pull/35)
* [refactor(apollo-client): Optimize the exception message when failing to retrieve configuration information.](https://github.com/apolloconfig/apollo-java/pull/22)
* [Add JUnit5 extension support for apollo mock server.](https://github.com/apolloconfig/apollo-java/pull/25)
* [Support concurrent loading of Config for different namespaces.](https://github.com/apolloconfig/apollo-java/pull/31)
* [Fix snakeyaml 2.x compatibility issues](https://github.com/apolloconfig/apollo-java/pull/35)
* [feat(openapi): create app](https://github.com/apolloconfig/apollo-java/pull/32)

------------------
All issues and pull requests are [here](https://github.com/apolloconfig/apollo-java/milestone/2?closed=1)
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.ctrip.framework.apollo.openapi.api;

import com.ctrip.framework.apollo.openapi.dto.OpenAppDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenCreateAppDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenEnvClusterDTO;
import java.util.List;

Expand All @@ -25,6 +26,10 @@
*/
public interface AppOpenApiService {

default void createApp(OpenCreateAppDTO req) {
throw new UnsupportedOperationException();
}

List<OpenEnvClusterDTO> getEnvClusterInfo(String appId);

List<OpenAppDTO> getAllApps();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ private ApolloOpenApiClient(String portalUrl, String token, RequestConfig reques
releaseService = new ReleaseOpenApiService(client, baseUrl, GSON);
}

public void createApp(OpenCreateAppDTO req) {
appService.createApp(req);
}

/**
* Get the environment and cluster information
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ private void checkHttpResponseStatus(HttpResponse response) {
throw new ApolloOpenApiException(status.getStatusCode(), status.getReasonPhrase(), message);
}

protected void checkNotNull(Object value, String name) {
Preconditions.checkArgument(null != value, name + " should not be null");
}

protected void checkNotEmpty(String value, String name) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(value), name + " should not be null or empty");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.ctrip.framework.apollo.openapi.client.url.OpenApiPathBuilder;
import com.ctrip.framework.apollo.openapi.dto.OpenAppDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenCreateAppDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenEnvClusterDTO;
import com.google.common.base.Joiner;
import com.google.gson.Gson;
Expand All @@ -39,6 +40,24 @@ public AppOpenApiService(CloseableHttpClient client, String baseUrl, Gson gson)
super(client, baseUrl, gson);
}

@Override
public void createApp(OpenCreateAppDTO req) {
OpenAppDTO app = req.getApp();
checkNotNull(app, "App");
checkNotEmpty(app.getAppId(), "App id");
checkNotEmpty(app.getName(), "App name");
OpenApiPathBuilder pathBuilder = OpenApiPathBuilder.newBuilder()
.customResource("apps");

try (CloseableHttpResponse response = post(pathBuilder, req)) {
gson.fromJson(EntityUtils.toString(response.getEntity()), void.class);
} catch (Throwable ex) {
throw new RuntimeException(
String.format("Create app: %s for appId: %s failed", app.getName(),
app.getAppId()), ex);
}
}

@Override
public List<OpenEnvClusterDTO> getEnvClusterInfo(String appId) {
checkNotEmpty(appId, "App id");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2022 Apollo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.ctrip.framework.apollo.openapi.dto;

import java.util.Set;

public class OpenCreateAppDTO {

/**
* when {@code assignAppRoleToSelf} is true,
* you can do anything with the app by current token!
*/
private boolean assignAppRoleToSelf;

/**
* The application owner has project administrator permission by default.
* <p>
* Administrators can create namespace, cluster, and assign user permissions
*/
private Set<String> admins;

private OpenAppDTO app;

public boolean isAssignAppRoleToSelf() {
return assignAppRoleToSelf;
}

public void setAssignAppRoleToSelf(boolean assignAppRoleToSelf) {
this.assignAppRoleToSelf = assignAppRoleToSelf;
}

public Set<String> getAdmins() {
return admins;
}

public void setAdmins(Set<String> admins) {
this.admins = admins;
}

public OpenAppDTO getApp() {
return app;
}

public void setApp(OpenAppDTO app) {
this.app = app;
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder("OpenCreateAppDTO{");
sb.append("assignAppRoleToSelf='").append(assignAppRoleToSelf).append('\'');
sb.append(", admins='").append(admins).append('\'');
sb.append(", app=").append(app);
sb.append('}');
return sb.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
/*
* Copyright 2022 Apollo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.ctrip.framework.apollo.openapi.client;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenAppDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenAppNamespaceDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenClusterDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenCreateAppDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Do not run in 'mvn clean test',
* left code here for develop test,
* you can run the method by ide.
*/
class ApolloOpenApiClientIntegrationTest {

private final Logger log = LoggerFactory.getLogger(this.getClass());

private final ApolloOpenApiClient client = newClient();

private final String env = "DEV";
private final String clusterName = "default";

ApolloOpenApiClient newClient() {
String someUrl = "http://localhost:8070";
// String someToken = "0627b87948c30517157e8b2a9565e473b5a97323a50128f584838ed10559d3fd";
String someToken = "9d0a241e9cb2300f302a875b1195340b2b6f56373cf5ca5d006a3f4e1a46b3ef";

return ApolloOpenApiClient.newBuilder()
.withPortalUrl(someUrl)
.withToken(someToken)
.withReadTimeout(2000 * 1000)
.withConnectTimeout(2000 * 1000)
.build();
}

void createApp(String appId, String ownerName, boolean assignAppRoleToSelf, String ... admins) {
{
OpenAppDTO app = new OpenAppDTO();
app.setName("openapi create app 测试名字 " + appId);
app.setAppId(appId);
app.setOwnerName(ownerName);
app.setOwnerEmail(ownerName + "@apollo.apollo");
app.setOrgId("orgIdFromOpenapi");
app.setOrgName("orgNameFromOpenapi");
OpenCreateAppDTO req = new OpenCreateAppDTO();
req.setApp(app);
req.setAdmins(new HashSet<>(Arrays.asList(admins)));
req.setAssignAppRoleToSelf(assignAppRoleToSelf);
log.info("create app {}, ownerName {} assignAppRoleToSelf {}", appId, ownerName, assignAppRoleToSelf);
client.createApp(req);
}
}

@Test
@Disabled("only for integration test")
public void testCreateApp() {
final String appId = "openapi-create-app1";
final String ownerName = "user-test-xxx1";
createApp(appId, ownerName, false, "user-test-xxx2", "user3");

List<OpenAppDTO> list = client.getAppsByIds(Collections.singletonList(appId));
assertEquals(1, list.size());
OpenAppDTO openAppDTO = list.get(0);
log.info("{}", openAppDTO);
assertEquals(appId, openAppDTO.getAppId());
assertEquals(ownerName, openAppDTO.getOwnerName());
}


@Test
@Disabled("only for integration test")
public void testCreateAppButHaveNoAppRole() {
// create app
final String appIdSuffix = LocalDateTime.now().format(
DateTimeFormatter.ofPattern("yyyyMMdd-HH-mm-ss")
);
final String appId = "openapi-create-app-" + appIdSuffix;
final String ownerName = "test-create-release1";
createApp(appId, ownerName, false, "user-test-xxx1", "user-test-xxx2");

{
List<OpenAppDTO> list = client.getAppsByIds(Collections.singletonList(appId));
assertEquals(1, list.size());
OpenAppDTO openAppDTO = list.get(0);
log.info("{}", openAppDTO);
assertEquals(appId, openAppDTO.getAppId());
assertEquals(ownerName, openAppDTO.getOwnerName());
}

// create namespace
final String namespaceName = "openapi-create-namespace";
{
OpenAppNamespaceDTO dto = new OpenAppNamespaceDTO();
dto.setName(namespaceName);
dto.setAppId(appId);
dto.setComment("create from openapi");
dto.setDataChangeCreatedBy(ownerName);
log.info("create namespace {} should fail because have no app role", namespaceName);
assertThrows(RuntimeException.class, () -> client.createAppNamespace(dto));
}
}

@Test
@Disabled("only for integration test")
public void testCreateAppThenCreateClusterCreateNamespaceThenRelease() {
// create app
final String appIdSuffix = LocalDateTime.now().format(
DateTimeFormatter.ofPattern("yyyyMMdd-HH-mm-ss")
);
final String appId = "openapi-create-app-" + appIdSuffix;
final String ownerName = "test-create-release1";
createApp(appId, ownerName, true, "user-test-xxx1", "user-test-xxx2");

{
List<OpenAppDTO> list = client.getAppsByIds(Collections.singletonList(appId));
assertEquals(1, list.size());
OpenAppDTO openAppDTO = list.get(0);
log.info("{}", openAppDTO);
assertEquals(appId, openAppDTO.getAppId());
assertEquals(ownerName, openAppDTO.getOwnerName());
}

// create cluster
final String clusterName = "cluster-openapi";
{
OpenClusterDTO dto = new OpenClusterDTO();
dto.setAppId(appId);
dto.setName(clusterName);
dto.setDataChangeCreatedBy(ownerName);
log.info("create cluster {}", clusterName);
client.createCluster(env, dto);
}

// create namespace
final String namespaceName = "openapi-create-namespace";
{
OpenAppNamespaceDTO dto = new OpenAppNamespaceDTO();
dto.setName(namespaceName);
dto.setAppId(appId);
dto.setComment("create from openapi");
dto.setDataChangeCreatedBy(ownerName);
log.info("create namespace {}", namespaceName);
client.createAppNamespace(dto);
}

// modify
// k1=v1
{
OpenItemDTO itemDTO = new OpenItemDTO();
itemDTO.setKey("k1");
itemDTO.setValue("v1");
itemDTO.setDataChangeCreatedBy(ownerName);
client.createOrUpdateItem(
appId, env, clusterName, namespaceName, itemDTO
);
}
// k2=v2
{
OpenItemDTO itemDTO = new OpenItemDTO();
itemDTO.setKey("k2");
itemDTO.setValue("v2");
itemDTO.setDataChangeCreatedBy(ownerName);
client.createOrUpdateItem(
appId, env, clusterName, namespaceName, itemDTO
);
}

// release namespace
{
NamespaceReleaseDTO dto = new NamespaceReleaseDTO();
dto.setReleaseTitle("openapi-release");
dto.setReleasedBy(ownerName);
dto.setReleaseComment("test openapi release in " + LocalDateTime.now());
log.info("release namespace {}", namespaceName);
client.publishNamespace(appId, env, clusterName, namespaceName, dto);
}

// read then namespace
{
OpenNamespaceDTO namespaceDTO
= client.getNamespace(appId, env, clusterName, namespaceName);
List<OpenItemDTO> items = namespaceDTO.getItems();
Map<String, String> map = new HashMap<>(16);
for (OpenItemDTO item : items) {
map.put(item.getKey(), item.getValue());
}
assertEquals(2, map.size());
assertEquals("v1", map.get("k1"));
assertEquals("v2", map.get("k2"));
log.info("create app {} namespace {} and release {} success", appId, namespaceName, map);
}
}

}
Loading

0 comments on commit 5344bc4

Please sign in to comment.