From f0e9a752d827a4f1557f2e053e3cd9470ea7a832 Mon Sep 17 00:00:00 2001 From: luke <348358584@qq.com> Date: Fri, 24 Jan 2025 22:19:33 +0800 Subject: [PATCH 1/4] feat(OpenApi): notification by email when release by OpenApi --- .../v1/controller/ReleaseController.java | 54 +++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) 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 cb0d0ef9e14..37c2b2a3f59 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 @@ -30,10 +30,12 @@ import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils; import com.ctrip.framework.apollo.portal.entity.model.NamespaceGrayDelReleaseModel; import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel; +import com.ctrip.framework.apollo.portal.listener.ConfigPublishEvent; import com.ctrip.framework.apollo.portal.service.NamespaceBranchService; import com.ctrip.framework.apollo.portal.service.ReleaseService; import com.ctrip.framework.apollo.portal.spi.UserService; import javax.servlet.http.HttpServletRequest; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; @@ -54,18 +56,21 @@ public class ReleaseController { private final NamespaceBranchService namespaceBranchService; private final ConsumerPermissionValidator consumerPermissionValidator; private final ReleaseOpenApiService releaseOpenApiService; + private final ApplicationEventPublisher publisher; public ReleaseController( final ReleaseService releaseService, final UserService userService, final NamespaceBranchService namespaceBranchService, final ConsumerPermissionValidator consumerPermissionValidator, - ReleaseOpenApiService releaseOpenApiService) { + ReleaseOpenApiService releaseOpenApiService, + ApplicationEventPublisher publisher) { this.releaseService = releaseService; this.userService = userService; this.namespaceBranchService = namespaceBranchService; this.consumerPermissionValidator = consumerPermissionValidator; this.releaseOpenApiService = releaseOpenApiService; + this.publisher = publisher; } @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#request, #appId, #namespaceName, #env)") @@ -83,7 +88,19 @@ public OpenReleaseDTO createRelease(@PathVariable String appId, @PathVariable St throw BadRequestException.userNotExists(model.getReleasedBy()); } - return this.releaseOpenApiService.publishNamespace(appId, env, clusterName, namespaceName, model); + OpenReleaseDTO releaseDTO = this.releaseOpenApiService.publishNamespace(appId, env, + clusterName, namespaceName, model); + + ConfigPublishEvent event = ConfigPublishEvent.instance(); + event.withAppId(appId) + .withCluster(clusterName) + .withNamespace(namespaceName) + .withReleaseId(releaseDTO.getId()) + .setNormalPublishEvent(true) + .setEnv(Env.valueOf(env)); + publisher.publishEvent(event); + + return releaseDTO; } @GetMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/latest") @@ -111,6 +128,16 @@ public OpenReleaseDTO merge(@PathVariable String appId, @PathVariable String env model.getReleaseTitle(), model.getReleaseComment(), model.isEmergencyPublish(), deleteBranch, model.getReleasedBy()); + + ConfigPublishEvent event = ConfigPublishEvent.instance(); + event.withAppId(appId) + .withCluster(clusterName) + .withNamespace(namespaceName) + .withReleaseId(mergedRelease.getId()) + .setMergeEvent(true) + .setEnv(Env.valueOf(env)); + publisher.publishEvent(event); + return OpenApiBeanUtils.transformFromReleaseDTO(mergedRelease); } @@ -136,7 +163,18 @@ public OpenReleaseDTO createGrayRelease(@PathVariable String appId, releaseModel.setClusterName(branchName); releaseModel.setNamespaceName(namespaceName); - return OpenApiBeanUtils.transformFromReleaseDTO(releaseService.publish(releaseModel)); + ReleaseDTO releaseDTO = releaseService.publish(releaseModel); + + ConfigPublishEvent event = ConfigPublishEvent.instance(); + event.withAppId(appId) + .withCluster(clusterName) + .withNamespace(namespaceName) + .withReleaseId(releaseDTO.getId()) + .setGrayPublishEvent(true) + .setEnv(Env.valueOf(env)); + publisher.publishEvent(event); + + return OpenApiBeanUtils.transformFromReleaseDTO(releaseDTO); } @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#request, #appId, #namespaceName, #env)") @@ -185,6 +223,16 @@ public void rollback(@PathVariable String env, throw new AccessDeniedException("Forbidden operation. you don't have release permission"); } + ConfigPublishEvent event = ConfigPublishEvent.instance(); + event.withAppId(release.getAppId()) + .withCluster(release.getClusterName()) + .withNamespace(release.getNamespaceName()) + .withReleaseId(release.getId()) + .setRollbackEvent(true) + .setEnv(Env.valueOf(env)); + publisher.publishEvent(event); + + this.releaseOpenApiService.rollbackRelease(env, releaseId, operator); } From 9c8ed0f7819b2d9dd3198217385a2118056bebcd Mon Sep 17 00:00:00 2001 From: luke <348358584@qq.com> Date: Fri, 24 Jan 2025 22:20:22 +0800 Subject: [PATCH 2/4] chore(OpenApi): code style fix --- .../v1/controller/ReleaseController.java | 166 +++++++++--------- 1 file changed, 79 insertions(+), 87 deletions(-) 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 37c2b2a3f59..b12895bb5af 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 @@ -110,99 +110,91 @@ public OpenReleaseDTO loadLatestActiveRelease(@PathVariable String appId, @PathV return this.releaseOpenApiService.getLatestActiveRelease(appId, env, clusterName, namespaceName); } - @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#request, #appId, #namespaceName, #env)") - @PostMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/merge") - public OpenReleaseDTO merge(@PathVariable String appId, @PathVariable String env, - @PathVariable String clusterName, @PathVariable String namespaceName, - @PathVariable String branchName, @RequestParam(value = "deleteBranch", defaultValue = "true") boolean deleteBranch, - @RequestBody NamespaceReleaseDTO model, HttpServletRequest request) { - RequestPrecondition.checkArguments(!StringUtils.isContainEmpty(model.getReleasedBy(), model - .getReleaseTitle()), - "Params(releaseTitle and releasedBy) can not be empty"); - - if (userService.findByUserId(model.getReleasedBy()) == null) { - throw BadRequestException.userNotExists(model.getReleasedBy()); - } - - ReleaseDTO mergedRelease = namespaceBranchService.merge(appId, Env.valueOf(env.toUpperCase()), clusterName, namespaceName, branchName, - model.getReleaseTitle(), model.getReleaseComment(), - model.isEmergencyPublish(), deleteBranch, model.getReleasedBy()); - - - ConfigPublishEvent event = ConfigPublishEvent.instance(); - event.withAppId(appId) - .withCluster(clusterName) - .withNamespace(namespaceName) - .withReleaseId(mergedRelease.getId()) - .setMergeEvent(true) - .setEnv(Env.valueOf(env)); - publisher.publishEvent(event); - - return OpenApiBeanUtils.transformFromReleaseDTO(mergedRelease); + @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#request, #appId, #namespaceName, #env)") + @PostMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/merge") + public OpenReleaseDTO merge(@PathVariable String appId, @PathVariable String env, + @PathVariable String clusterName, @PathVariable String namespaceName, + @PathVariable String branchName, + @RequestParam(value = "deleteBranch", defaultValue = "true") boolean deleteBranch, + @RequestBody NamespaceReleaseDTO model, HttpServletRequest request) { + RequestPrecondition.checkArguments( + !StringUtils.isContainEmpty(model.getReleasedBy(), model.getReleaseTitle()), + "Params(releaseTitle and releasedBy) can not be empty"); + + if (userService.findByUserId(model.getReleasedBy()) == null) { + throw BadRequestException.userNotExists(model.getReleasedBy()); } - @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#request, #appId, #namespaceName, #env)") - @PostMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/releases") - public OpenReleaseDTO createGrayRelease(@PathVariable String appId, - @PathVariable String env, @PathVariable String clusterName, - @PathVariable String namespaceName, @PathVariable String branchName, - @RequestBody NamespaceReleaseDTO model, - HttpServletRequest request) { - RequestPrecondition.checkArguments(!StringUtils.isContainEmpty(model.getReleasedBy(), model - .getReleaseTitle()), - "Params(releaseTitle and releasedBy) can not be empty"); - - if (userService.findByUserId(model.getReleasedBy()) == null) { - throw BadRequestException.userNotExists(model.getReleasedBy()); - } - - NamespaceReleaseModel releaseModel = BeanUtils.transform(NamespaceReleaseModel.class, model); - - releaseModel.setAppId(appId); - releaseModel.setEnv(Env.valueOf(env).toString()); - releaseModel.setClusterName(branchName); - releaseModel.setNamespaceName(namespaceName); - - ReleaseDTO releaseDTO = releaseService.publish(releaseModel); - - ConfigPublishEvent event = ConfigPublishEvent.instance(); - event.withAppId(appId) - .withCluster(clusterName) - .withNamespace(namespaceName) - .withReleaseId(releaseDTO.getId()) - .setGrayPublishEvent(true) - .setEnv(Env.valueOf(env)); - publisher.publishEvent(event); - - return OpenApiBeanUtils.transformFromReleaseDTO(releaseDTO); + ReleaseDTO mergedRelease = namespaceBranchService.merge(appId, Env.valueOf(env.toUpperCase()), + clusterName, namespaceName, branchName, model.getReleaseTitle(), model.getReleaseComment(), + model.isEmergencyPublish(), deleteBranch, model.getReleasedBy()); + + ConfigPublishEvent event = ConfigPublishEvent.instance(); + event.withAppId(appId).withCluster(clusterName).withNamespace(namespaceName) + .withReleaseId(mergedRelease.getId()).setMergeEvent(true).setEnv(Env.valueOf(env)); + publisher.publishEvent(event); + + return OpenApiBeanUtils.transformFromReleaseDTO(mergedRelease); + } + + @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#request, #appId, #namespaceName, #env)") + @PostMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/releases") + public OpenReleaseDTO createGrayRelease(@PathVariable String appId, @PathVariable String env, + @PathVariable String clusterName, @PathVariable String namespaceName, + @PathVariable String branchName, @RequestBody NamespaceReleaseDTO model, + HttpServletRequest request) { + RequestPrecondition.checkArguments( + !StringUtils.isContainEmpty(model.getReleasedBy(), model.getReleaseTitle()), + "Params(releaseTitle and releasedBy) can not be empty"); + + if (userService.findByUserId(model.getReleasedBy()) == null) { + throw BadRequestException.userNotExists(model.getReleasedBy()); } - @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#request, #appId, #namespaceName, #env)") - @PostMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/gray-del-releases") - public OpenReleaseDTO createGrayDelRelease(@PathVariable String appId, - @PathVariable String env, @PathVariable String clusterName, - @PathVariable String namespaceName, @PathVariable String branchName, - @RequestBody NamespaceGrayDelReleaseDTO model, - HttpServletRequest request) { - RequestPrecondition.checkArguments(!StringUtils.isContainEmpty(model.getReleasedBy(), model - .getReleaseTitle()), - "Params(releaseTitle and releasedBy) can not be empty"); - RequestPrecondition.checkArguments(model.getGrayDelKeys() != null, - "Params(grayDelKeys) can not be null"); - - if (userService.findByUserId(model.getReleasedBy()) == null) { - throw BadRequestException.userNotExists(model.getReleasedBy()); - } - - NamespaceGrayDelReleaseModel releaseModel = BeanUtils.transform(NamespaceGrayDelReleaseModel.class, model); - releaseModel.setAppId(appId); - releaseModel.setEnv(env.toUpperCase()); - releaseModel.setClusterName(branchName); - releaseModel.setNamespaceName(namespaceName); - - return OpenApiBeanUtils.transformFromReleaseDTO(releaseService.publish(releaseModel, releaseModel.getReleasedBy())); + NamespaceReleaseModel releaseModel = BeanUtils.transform(NamespaceReleaseModel.class, model); + + releaseModel.setAppId(appId); + releaseModel.setEnv(Env.valueOf(env).toString()); + releaseModel.setClusterName(branchName); + releaseModel.setNamespaceName(namespaceName); + + ReleaseDTO releaseDTO = releaseService.publish(releaseModel); + + ConfigPublishEvent event = ConfigPublishEvent.instance(); + event.withAppId(appId).withCluster(clusterName).withNamespace(namespaceName) + .withReleaseId(releaseDTO.getId()).setGrayPublishEvent(true).setEnv(Env.valueOf(env)); + publisher.publishEvent(event); + + return OpenApiBeanUtils.transformFromReleaseDTO(releaseDTO); + } + + @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#request, #appId, #namespaceName, #env)") + @PostMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/gray-del-releases") + public OpenReleaseDTO createGrayDelRelease(@PathVariable String appId, @PathVariable String env, + @PathVariable String clusterName, @PathVariable String namespaceName, + @PathVariable String branchName, @RequestBody NamespaceGrayDelReleaseDTO model, + HttpServletRequest request) { + RequestPrecondition.checkArguments( + !StringUtils.isContainEmpty(model.getReleasedBy(), model.getReleaseTitle()), + "Params(releaseTitle and releasedBy) can not be empty"); + RequestPrecondition.checkArguments(model.getGrayDelKeys() != null, + "Params(grayDelKeys) can not be null"); + + if (userService.findByUserId(model.getReleasedBy()) == null) { + throw BadRequestException.userNotExists(model.getReleasedBy()); } + NamespaceGrayDelReleaseModel releaseModel = BeanUtils.transform( + NamespaceGrayDelReleaseModel.class, model); + releaseModel.setAppId(appId); + releaseModel.setEnv(env.toUpperCase()); + releaseModel.setClusterName(branchName); + releaseModel.setNamespaceName(namespaceName); + + return OpenApiBeanUtils.transformFromReleaseDTO( + releaseService.publish(releaseModel, releaseModel.getReleasedBy())); + } + @PutMapping(path = "/releases/{releaseId}/rollback") public void rollback(@PathVariable String env, @PathVariable long releaseId, @RequestParam String operator, HttpServletRequest request) { From 8dd5122d56c25e2e2979307b4afe1741dcab5774 Mon Sep 17 00:00:00 2001 From: luke <348358584@qq.com> Date: Sat, 25 Jan 2025 21:11:30 +0800 Subject: [PATCH 3/4] chore(changes): update changes-2.4.0.md --- changes/changes-2.4.0.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changes/changes-2.4.0.md b/changes/changes-2.4.0.md index 3ebe2e98381..ebfad70e2b1 100644 --- a/changes/changes-2.4.0.md +++ b/changes/changes-2.4.0.md @@ -9,6 +9,8 @@ Apollo 2.4.0 * [Feature support portal restTemplate Client connection pool config](https://github.com/apolloconfig/apollo/pull/5200) * [Feature added the ability for administrators to globally search for Value](https://github.com/apolloconfig/apollo/pull/5182) * [Feature support the observe status access-key for pre-check and logging only](https://github.com/apolloconfig/apollo/pull/5236) +* [Feature support to assign users management authority by the cluster (modify, publish)](https://github.com/apolloconfig/apollo/pull/5302) +* [Feature support to notification by email when releasing by OpenApi also](https://github.com/apolloconfig/apollo/pull/5324) ------------------ All issues and pull requests are [here](https://github.com/apolloconfig/apollo/milestone/15?closed=1) From 35fd4330100d92782027a6589873f766f320e2ed Mon Sep 17 00:00:00 2001 From: luke <348358584@qq.com> Date: Sun, 26 Jan 2025 13:12:41 +0800 Subject: [PATCH 4/4] minor(changes): update CHANGES.md --- CHANGES.md | 2 ++ changes/changes-2.4.0.md | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3591e1fd14f..b1bca735d35 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -26,6 +26,8 @@ Apollo 2.4.0 * [Feature: add JSON formatting function in apollo-portal](https://github.com/apolloconfig/apollo/pull/5287) * [Fix: add missing url patterns for AdminServiceAuthenticationFilter](https://github.com/apolloconfig/apollo/pull/5291) * [Fix: support java.time.Instant serialization with gson](https://github.com/apolloconfig/apollo/pull/5298) +* [Feature: support to assign users management authority by the cluster (modify, publish)](https://github.com/apolloconfig/apollo/pull/5302) +* [Feature: notification by email when releasing by OpenApi also](https://github.com/apolloconfig/apollo/pull/5324) ------------------ All issues and pull requests are [here](https://github.com/apolloconfig/apollo/milestone/15?closed=1) diff --git a/changes/changes-2.4.0.md b/changes/changes-2.4.0.md index ebfad70e2b1..25fe3e1fb99 100644 --- a/changes/changes-2.4.0.md +++ b/changes/changes-2.4.0.md @@ -9,8 +9,5 @@ Apollo 2.4.0 * [Feature support portal restTemplate Client connection pool config](https://github.com/apolloconfig/apollo/pull/5200) * [Feature added the ability for administrators to globally search for Value](https://github.com/apolloconfig/apollo/pull/5182) * [Feature support the observe status access-key for pre-check and logging only](https://github.com/apolloconfig/apollo/pull/5236) -* [Feature support to assign users management authority by the cluster (modify, publish)](https://github.com/apolloconfig/apollo/pull/5302) -* [Feature support to notification by email when releasing by OpenApi also](https://github.com/apolloconfig/apollo/pull/5324) - ------------------ All issues and pull requests are [here](https://github.com/apolloconfig/apollo/milestone/15?closed=1)