diff --git a/build.gradle b/build.gradle index ba4062fd127dfe6302a9397e3d1c0b653a9ff1da..3a0d07daa528b359046eeb9a763d3397dd99b823 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ repositories { mavenCentral() } -version = '1.6.3-SNAPSHOT' +version = '1.7.0-SNAPSHOT' group = 'net.geant.nmaas' java { diff --git a/src/main/java/net/geant/nmaas/notifications/NotificationController.java b/src/main/java/net/geant/nmaas/notifications/NotificationController.java index 479709ca7e41fbcb2f1982131a3ba2c7de897879..762740d7c4e176ae4899267ace857a461e4ee268 100644 --- a/src/main/java/net/geant/nmaas/notifications/NotificationController.java +++ b/src/main/java/net/geant/nmaas/notifications/NotificationController.java @@ -31,7 +31,7 @@ public class NotificationController { // TODO verify if captcha token must be verified MailType mailType = mailAttributes.getMailType(); - if(mailType.equals(MailType.CONTACT_FORM) || mailType.equals(MailType.ISSUE_REPORT) || mailType.equals(MailType.NEW_DOMAIN_REQUEST)) { + if (mailType.equals(MailType.CONTACT_FORM) || mailType.equals(MailType.ISSUE_REPORT) || mailType.equals(MailType.NEW_DOMAIN_REQUEST)) { eventPublisher.publishEvent(new NotificationEvent(this, mailAttributes)); } else { throw new AuthenticationException("You are not allowed to send this mail"); diff --git a/src/main/java/net/geant/nmaas/notifications/NotificationManager.java b/src/main/java/net/geant/nmaas/notifications/NotificationManager.java index 401a06eff27c9af5acb55971e2a36c494347a66c..ad62a89f3520b7a159ff5c70293faba432531cf3 100644 --- a/src/main/java/net/geant/nmaas/notifications/NotificationManager.java +++ b/src/main/java/net/geant/nmaas/notifications/NotificationManager.java @@ -70,7 +70,7 @@ public class NotificationManager { try { template = templateService.getHTMLTemplate(); } catch (IOException e) { - log.error(String.format("Cannot retrieve html template: %s", e.getMessage())); + log.error("Cannot retrieve html template: {}", e.getMessage()); throw new ProcessingException(e); } @@ -193,7 +193,7 @@ public class NotificationManager { } private String getFilledTemplate(Template template, LanguageMailContentView langContent, UserView user, MailAttributes mailAttributes, MailTemplateView mailTemplate) throws IOException, TemplateException { - boolean showAdditional = mailAttributes.getOtherAttributes().get("message") != null; + boolean showAdditional = mailAttributes.getMailType() == MailType.NEW_ACTIVE_APP && mailAttributes.getOtherAttributes().get("message") != null; return FreeMarkerTemplateUtils.processTemplateIntoString(template, ImmutableMap.builder() .putAll(mailTemplate.getGlobalInformation()) .put(MailTemplateElements.PORTAL_LINK, this.portalAddress == null ? "" : this.portalAddress) diff --git a/src/main/java/net/geant/nmaas/portal/api/bulk/BulkController.java b/src/main/java/net/geant/nmaas/portal/api/bulk/BulkController.java index a03794d9dc9e031a079913b66b85166756667720..b92cedea409b9577edeca2d7b850b7834deee12a 100644 --- a/src/main/java/net/geant/nmaas/portal/api/bulk/BulkController.java +++ b/src/main/java/net/geant/nmaas/portal/api/bulk/BulkController.java @@ -14,9 +14,11 @@ import net.geant.nmaas.portal.service.BulkDomainService; import net.geant.nmaas.portal.service.UserService; import org.modelmapper.ModelMapper; import org.springframework.core.io.InputStreamResource; +import org.springframework.dao.PermissionDeniedDataAccessException; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -30,6 +32,7 @@ import java.security.Principal; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; @RestController @@ -91,17 +94,14 @@ public class BulkController { @GetMapping @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN')") public ResponseEntity<List<BulkDeploymentViewS>> getAllDeploymentRecords() { - return ResponseEntity.ok(mapToView(bulkDeploymentRepository.findAll())); + return ResponseEntity.ok(mapToViewList(bulkDeploymentRepository.findAll())); } @GetMapping("/{id}") @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_VL_MANAGER')") public ResponseEntity<BulkDeploymentView> getDeploymentRecord(@PathVariable Long id) { BulkDeployment bulk = bulkDeploymentRepository.findById(id).orElseThrow(); - BulkDeploymentView bulkView = modelMapper.map(bulk, BulkDeploymentView.class); - bulkView.setCreator(getUserView(bulk.getCreatorId())); - mapDetails(bulk, bulkView); - return ResponseEntity.ok(bulkView); + return ResponseEntity.ok(mapToView(bulk, BulkDeploymentView.class)); } @GetMapping(value = "/app/csv/{id}", produces = "text/csv") @@ -124,7 +124,7 @@ public class BulkController { @GetMapping("/domains") @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN')") public ResponseEntity<List<BulkDeploymentViewS>> getDomainDeploymentRecords() { - return ResponseEntity.ok(mapToView(bulkDeploymentRepository.findByType(BulkType.DOMAIN))); + return ResponseEntity.ok(mapToViewList(bulkDeploymentRepository.findByType(BulkType.DOMAIN))); } @GetMapping("/domains/vl") @@ -132,14 +132,14 @@ public class BulkController { public ResponseEntity<List<BulkDeploymentViewS>> getDomainDeploymentRecordsRestrictedToOwner(Principal principal) { User user = this.userService.findByUsername(principal.getName()).orElseThrow(() -> new MissingElementException("Missing user " + principal.getName())); - return ResponseEntity.ok(mapToView(bulkDeploymentRepository.findByType(BulkType.DOMAIN)).stream() + return ResponseEntity.ok(mapToViewList(bulkDeploymentRepository.findByType(BulkType.DOMAIN)).stream() .filter(bulk -> bulk.getCreator().getId().equals(user.getId())).collect(Collectors.toList())); } @GetMapping("/apps") @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN')") public ResponseEntity<List<BulkDeploymentViewS>> getAppDeploymentRecords() { - return ResponseEntity.ok(mapToView(bulkDeploymentRepository.findByType(BulkType.APPLICATION))); + return ResponseEntity.ok(mapToViewList(bulkDeploymentRepository.findByType(BulkType.APPLICATION))); } @GetMapping("/apps/vl") @@ -147,10 +147,35 @@ public class BulkController { public ResponseEntity<List<BulkDeploymentViewS>> getAppDeploymentRecordsRestrictedToOwner(Principal principal) { User user = this.userService.findByUsername(principal.getName()).orElseThrow(() -> new MissingElementException("Missing user " + principal.getName())); - return ResponseEntity.ok(mapToView(bulkDeploymentRepository.findByType(BulkType.APPLICATION)).stream() + return ResponseEntity.ok(mapToViewList(bulkDeploymentRepository.findByType(BulkType.APPLICATION)).stream() .filter(bulk -> bulk.getCreator().getId().equals(user.getId())).collect(Collectors.toList())); } + @DeleteMapping("/{id}") + @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_VL_MANAGER')") + public ResponseEntity<Void> removeBulkDeployment( + @PathVariable Long id, + @RequestParam(name = "removeAll") boolean removeApps, + Principal principal + ) { + User user = this.userService.findByUsername(principal.getName()).orElseThrow(() -> new MissingElementException("Missing user " + principal.getName())); + + Optional<BulkDeployment> bulk = this.bulkDeploymentRepository.findById(id); + if (bulk.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + if(bulk.get().getCreatorId().equals(user.getId()) ) { + throw new PermissionDeniedDataAccessException("User doesnt have access to this bulk deployment", new Throwable()); + } + if (removeApps) { + bulkApplicationService.deleteAppInstancesFromBulk(mapToView(bulk.get(), BulkDeploymentView.class)); + } + bulkDeploymentRepository.delete(bulk.get()); + return ResponseEntity.ok().build(); + } + + private List<BulkDeploymentViewS> mapToViewList(List<BulkDeployment> deployments) { @GetMapping("/refresh/{id}") @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_VL_MANAGER')") public ResponseEntity<BulkDeploymentViewS> getRefreshedState(@PathVariable Long id) { @@ -159,15 +184,17 @@ public class BulkController { private List<BulkDeploymentViewS> mapToView(List<BulkDeployment> deployments) { return deployments.stream() - .map(bulk -> { - BulkDeploymentViewS bulkView = modelMapper.map(bulk, BulkDeploymentViewS.class); - bulkView.setCreator(getUserView(bulk.getCreatorId())); - mapDetails(bulk, bulkView); - return bulkView; - }) + .map(bulk -> mapToView(bulk, BulkDeploymentViewS.class)) .collect(Collectors.toList()); } + private <T extends BulkDeploymentViewS> T mapToView(BulkDeployment bulk, Class<T> viewType) { + T bulkView = modelMapper.map(bulk, viewType); + bulkView.setCreator(getUserView(bulk.getCreatorId())); + mapDetails(bulk, bulkView); + return bulkView; + } + private BulkDeploymentView mapToView(BulkDeployment deployment) { BulkDeploymentView bulkView = modelMapper.map(deployment, BulkDeploymentView.class); bulkView.setCreator(getUserView(deployment.getCreatorId())); diff --git a/src/main/java/net/geant/nmaas/portal/service/BulkApplicationService.java b/src/main/java/net/geant/nmaas/portal/service/BulkApplicationService.java index 08583400d7b96e1a1e3c44cf474202e3d4c3b378..15c439137c55ac8b47e13419f3913835f439c351 100644 --- a/src/main/java/net/geant/nmaas/portal/service/BulkApplicationService.java +++ b/src/main/java/net/geant/nmaas/portal/service/BulkApplicationService.java @@ -23,7 +23,9 @@ public interface BulkApplicationService { List<BulkAppDetails> getAppsBulkDetails(BulkDeploymentView view); - InputStreamResource getInputStreamAppBulkDetails(List<BulkAppDetails> list ); + InputStreamResource getInputStreamAppBulkDetails(List<BulkAppDetails> list); + + void deleteAppInstancesFromBulk(BulkDeploymentView bulk); BulkDeployment updateState(Long bulkId); diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/BulkApplicationServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/BulkApplicationServiceImpl.java index cb73a8fe91af9a396c5c29d6dd632deb9623a139..9f654497228a4176dce689cef1409e1522ed8dd4 100644 --- a/src/main/java/net/geant/nmaas/portal/service/impl/BulkApplicationServiceImpl.java +++ b/src/main/java/net/geant/nmaas/portal/service/impl/BulkApplicationServiceImpl.java @@ -14,6 +14,7 @@ import net.geant.nmaas.orchestration.events.app.AppAutoDeploymentReviewEvent; import net.geant.nmaas.orchestration.events.app.AppAutoDeploymentStatusUpdateEvent; import net.geant.nmaas.orchestration.events.app.AppAutoDeploymentTriggeredEvent; import net.geant.nmaas.portal.api.bulk.BulkAppDetails; +import net.geant.nmaas.portal.api.bulk.BulkDeploymentEntryView; import net.geant.nmaas.portal.api.bulk.BulkDeploymentView; import net.geant.nmaas.portal.api.bulk.BulkDeploymentViewS; import net.geant.nmaas.portal.api.bulk.BulkType; @@ -21,6 +22,7 @@ import net.geant.nmaas.portal.api.bulk.CsvApplication; import net.geant.nmaas.portal.api.domain.AppInstanceState; import net.geant.nmaas.portal.api.domain.UserViewMinimal; import net.geant.nmaas.portal.api.exception.MissingElementException; +import net.geant.nmaas.portal.exceptions.ObjectNotFoundException; import net.geant.nmaas.portal.persistent.entity.AppInstance; import net.geant.nmaas.portal.persistent.entity.Application; import net.geant.nmaas.portal.persistent.entity.BulkDeployment; @@ -301,6 +303,24 @@ public class BulkApplicationServiceImpl implements BulkApplicationService { ); } + @Override + public void deleteAppInstancesFromBulk(BulkDeploymentView bulk) { + List<BulkDeploymentEntryView> apps = bulk.getEntries(); + for (BulkDeploymentEntryView app : apps) { + Long appInstanceId = Long.valueOf(findAppDetail(app, BULK_ENTRY_DETAIL_KEY_APP_INSTANCE_ID)); + AppInstance appInstance = instanceService.find(appInstanceId) + .orElseThrow(() -> new ObjectNotFoundException("App instance not found")); + + appLifecycleManager.removeApplication(appInstance.getInternalId()); + instanceService.delete(appInstanceId); + } + } + + private String findAppDetail(BulkDeploymentEntryView app, String key) { + return Optional.ofNullable(app.getDetails().get(key)) + .orElseThrow(() -> new ObjectNotFoundException(key + " not found")); + } + @Override public BulkDeployment updateState(Long bulkId) { log.info("Update all states for bulk {}", bulkId); @@ -408,8 +428,8 @@ public class BulkApplicationServiceImpl implements BulkApplicationService { //deploy Map<String, String> params = appDeploymentMonitor.appDeploymentParameters(instance.getInternalId()); - params.forEach( (key, value) -> { - configurationParameters.put(key, Objects.isNull(value) || Objects.equals(value, "") ? EMPTY_VALUE : value.replace("\"", "")); + params.forEach((key, value) -> { + configurationParameters.put(key, Objects.isNull(value) || Objects.equals(value, "") ? EMPTY_VALUE : value.replace("\"", "")); log.debug("Params = {} - {}", key, value); }); diff --git a/src/main/resources/changelog.json b/src/main/resources/changelog.json index fee877193c49c37774b1cd093bf70fd5b9d2dc7f..b46ad9eecc2904e3d08302f32f517ed8a6ec6298 100644 --- a/src/main/resources/changelog.json +++ b/src/main/resources/changelog.json @@ -2,7 +2,7 @@ "versions" : [ { "verNo" : "1.6.3", - "date" : "(2024/09/04)", + "date" : "(2024/09/05)", "topic" : [ { "title" : "New layout for the portal landing pages", @@ -13,6 +13,16 @@ "title" : "Support for SSO accounts provisioned ahead of time", "tags" : "[Enhancement]", "description" : "Bulk user deployment process was enhanced with an option to indicate that given user account should allow for SSO login" + }, + { + "title" : "New email notifications for user accounts created in bulk", + "tags" : "[Enhancement]", + "description" : "New users with accounts created in bulk process will receive customized email messages with instructions on first login" + }, + { + "title" : "Fixed issue with selection of application version for manual upgrade", + "tags" : "[Bugfix]", + "description" : "Fix was applied to drop down version selector on manual application instance version upgrade modal" } ] }, diff --git a/src/test/shell/data/i18n/de.json b/src/test/shell/data/i18n/de.json index 7c918b109d911070d0c5acac3185ae8bb8c63495..3be95453107a87a24be554ac82e93f9b52862154 100644 --- a/src/test/shell/data/i18n/de.json +++ b/src/test/shell/data/i18n/de.json @@ -1224,8 +1224,9 @@ }, "REMOVE" : { "HEADER" : "Remove bulk deployment", - "APP" : "Do you want to remove all instances created by this bulk ? ", - "REMOVE" : "Remove" + "APP" : "Do you want to remove all instances created by this bulk deployment?", + "REMOVE" : "Remove", + "ERROR": "Error removing bulk deployment:" } }, "SHARED" : { diff --git a/src/test/shell/data/i18n/en.json b/src/test/shell/data/i18n/en.json index b81160208ee64ef13c35eab767ffdc226d52662a..cda3ab52fa13d53283750e4f9cdb937fb1535092 100644 --- a/src/test/shell/data/i18n/en.json +++ b/src/test/shell/data/i18n/en.json @@ -1227,8 +1227,9 @@ }, "REMOVE" : { "HEADER" : "Remove bulk deployment", - "APP" : "Do you want to remove all instances created by this bulk ? ", - "REMOVE" : "Remove" + "APP" : "Do you want to remove all instances created by this bulk deployment?", + "REMOVE" : "Remove", + "ERROR": "Error removing bulk deployment:" } }, "SHARED" : { diff --git a/src/test/shell/data/i18n/fr.json b/src/test/shell/data/i18n/fr.json index 6804efd139fea4ad1f142a9bae2d29a080f1ff10..9bfad90280fb54f451d8239f9c1c4e5d14a5fb63 100644 --- a/src/test/shell/data/i18n/fr.json +++ b/src/test/shell/data/i18n/fr.json @@ -1225,8 +1225,9 @@ }, "REMOVE" : { "HEADER" : "Remove bulk deployment", - "APP" : "Do you want to remove all instances created by this bulk ? ", - "REMOVE" : "Remove" + "APP" : "Do you want to remove all instances created by this bulk deployment?", + "REMOVE" : "Remove", + "ERROR": "Error removing bulk deployment:" } }, "SHARED" : { diff --git a/src/test/shell/data/i18n/pl.json b/src/test/shell/data/i18n/pl.json index f96811b72a60d63ad166508d6af86b80657bcb64..4de03e0fb75ecb0b2f7b0904deedc3bb6214e708 100644 --- a/src/test/shell/data/i18n/pl.json +++ b/src/test/shell/data/i18n/pl.json @@ -1226,8 +1226,9 @@ }, "REMOVE" : { "HEADER" : "Remove bulk deployment", - "APP" : "Do you want to remove all instances created by this bulk ? ", - "REMOVE" : "Remove" + "APP" : "Do you want to remove all instances created by this bulk deployment?", + "REMOVE" : "Remove", + "ERROR": "Error removing bulk deployment:" } }, "SHARED" : {