From 8e703d81fe2e26ffa0f57d13ce5ecd651b0d3511 Mon Sep 17 00:00:00 2001
From: pgiertych <pgiertych@man.poznan.pl>
Date: Tue, 12 Mar 2024 13:02:05 +0100
Subject: [PATCH 01/50] added endpoint for bulk deployment removal

---
 .../nmaas/portal/api/bulk/BulkController.java | 53 +++++++++++++------
 .../service/BulkApplicationService.java       |  4 +-
 .../impl/BulkApplicationServiceImpl.java      | 25 ++++++++-
 src/test/shell/data/i18n/de.json              |  5 +-
 src/test/shell/data/i18n/en.json              |  5 +-
 src/test/shell/data/i18n/fr.json              |  5 +-
 src/test/shell/data/i18n/pl.json              |  5 +-
 7 files changed, 74 insertions(+), 28 deletions(-)

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 c42118894..df2fd49cc 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
@@ -17,6 +17,7 @@ import org.springframework.core.io.InputStreamResource;
 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 +31,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 +93,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 +123,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 +131,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()
-                    .filter(bulk -> bulk.getCreator().getId().equals(user.getId())).collect(Collectors.toList()));
+        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,21 +146,41 @@ 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()));
     }
 
-    private List<BulkDeploymentViewS> mapToView(List<BulkDeployment> deployments) {
+    @DeleteMapping("/{id}")
+    @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN')")
+    public ResponseEntity<Void> removeBulkDeployment(
+            @PathVariable Long id,
+            @RequestParam(name = "removeAll") boolean removeApps
+    ) {
+
+        Optional<BulkDeployment> bulk = this.bulkDeploymentRepository.findById(id);
+        if (bulk.isEmpty()) {
+            return ResponseEntity.notFound().build();
+        }
+        if (removeApps) {
+            bulkApplicationService.deleteAppInstancesFromBulk(mapToView(bulk.get(), BulkDeploymentView.class));
+        }
+        bulkDeploymentRepository.delete(bulk.get());
+        return ResponseEntity.ok().build();
+    }
+
+    private List<BulkDeploymentViewS> mapToViewList(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 void mapDetails(BulkDeployment deployment, BulkDeploymentViewS view) {
         if (deployment.getType().equals(BulkType.APPLICATION)) {
             Map<String, String> details = new HashMap<>();
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 bebf1c9f8..656dcd11d 100644
--- a/src/main/java/net/geant/nmaas/portal/service/BulkApplicationService.java
+++ b/src/main/java/net/geant/nmaas/portal/service/BulkApplicationService.java
@@ -22,6 +22,8 @@ public interface BulkApplicationService {
 
     List<BulkAppDetails> getAppsBulkDetails(BulkDeploymentView view);
 
-    InputStreamResource getInputStreamAppBulkDetails(List<BulkAppDetails> list );
+    InputStreamResource getInputStreamAppBulkDetails(List<BulkAppDetails> list);
+
+    void deleteAppInstancesFromBulk(BulkDeploymentView bulk);
 
 }
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 aea95a4ed..34ea8b171 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;
@@ -55,6 +57,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -300,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("Domain 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"));
+    }
+
     private static BulkDeployment createBulkDeployment(UserViewMinimal creator) {
         BulkDeployment bulkDeployment = new BulkDeployment();
         bulkDeployment.setType(BulkType.APPLICATION);
@@ -355,8 +376,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/test/shell/data/i18n/de.json b/src/test/shell/data/i18n/de.json
index 6d0b19d12..b1508c109 100644
--- a/src/test/shell/data/i18n/de.json
+++ b/src/test/shell/data/i18n/de.json
@@ -1219,8 +1219,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 db0f617fa..a0f291875 100644
--- a/src/test/shell/data/i18n/en.json
+++ b/src/test/shell/data/i18n/en.json
@@ -1223,8 +1223,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 2efa5331d..55338e03d 100644
--- a/src/test/shell/data/i18n/fr.json
+++ b/src/test/shell/data/i18n/fr.json
@@ -1219,8 +1219,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 992805e42..251a54ee5 100644
--- a/src/test/shell/data/i18n/pl.json
+++ b/src/test/shell/data/i18n/pl.json
@@ -1221,8 +1221,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" : {
-- 
GitLab


From e657128d1ccea5d9695e2ee8c0b4374225a8f0c3 Mon Sep 17 00:00:00 2001
From: pgiertych <pgiertych@man.poznan.pl>
Date: Tue, 12 Mar 2024 13:06:50 +0100
Subject: [PATCH 02/50] change error message

---
 .../nmaas/portal/service/impl/BulkApplicationServiceImpl.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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 34ea8b171..8efe9388d 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
@@ -309,7 +309,7 @@ public class BulkApplicationServiceImpl implements BulkApplicationService {
         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("Domain not found"));
+                    .orElseThrow(() -> new ObjectNotFoundException("App instance not found"));
 
             appLifecycleManager.removeApplication(appInstance.getInternalId());
             instanceService.delete(appInstanceId);
-- 
GitLab


From 36d1f85ee936cd4a623253755a2de75ecd6e8767 Mon Sep 17 00:00:00 2001
From: pgiertych <pgiertych@man.poznan.pl>
Date: Wed, 10 Apr 2024 14:08:41 +0200
Subject: [PATCH 03/50] token management

---
 .../security/CustomAccessTokenController.java | 58 +++++++++++++++++++
 .../portal/persistent/entity/AccessToken.java | 31 ++++++++++
 .../repositories/AccessTokenRepository.java   | 12 ++++
 .../service/CustomAccessTokenService.java     | 13 +++++
 .../impl/CustomAccessTokenServiceImpl.java    | 54 +++++++++++++++++
 src/test/shell/data/i18n/de.json              | 12 ++++
 src/test/shell/data/i18n/en.json              | 12 ++++
 src/test/shell/data/i18n/fr.json              | 12 ++++
 src/test/shell/data/i18n/pl.json              | 12 ++++
 9 files changed, 216 insertions(+)
 create mode 100644 src/main/java/net/geant/nmaas/portal/api/security/CustomAccessTokenController.java
 create mode 100644 src/main/java/net/geant/nmaas/portal/persistent/entity/AccessToken.java
 create mode 100644 src/main/java/net/geant/nmaas/portal/persistent/repositories/AccessTokenRepository.java
 create mode 100644 src/main/java/net/geant/nmaas/portal/service/CustomAccessTokenService.java
 create mode 100644 src/main/java/net/geant/nmaas/portal/service/impl/CustomAccessTokenServiceImpl.java

diff --git a/src/main/java/net/geant/nmaas/portal/api/security/CustomAccessTokenController.java b/src/main/java/net/geant/nmaas/portal/api/security/CustomAccessTokenController.java
new file mode 100644
index 000000000..1a7d19a72
--- /dev/null
+++ b/src/main/java/net/geant/nmaas/portal/api/security/CustomAccessTokenController.java
@@ -0,0 +1,58 @@
+package net.geant.nmaas.portal.api.security;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import net.geant.nmaas.portal.exceptions.ObjectNotFoundException;
+import net.geant.nmaas.portal.persistent.entity.AccessToken;
+import net.geant.nmaas.portal.persistent.entity.User;
+import net.geant.nmaas.portal.service.CustomAccessTokenService;
+import net.geant.nmaas.portal.service.UserService;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.security.Principal;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/tokens")
+@RequiredArgsConstructor
+@Log4j2
+public class CustomAccessTokenController {
+
+    private final CustomAccessTokenService accessTokenService;
+    private final UserService userService;
+
+    @GetMapping()
+    public List<AccessToken> getAll(Principal principal) {
+        User user = getUser(principal);
+        return accessTokenService.getAll(user.getId());
+    }
+
+    @PostMapping()
+    public AccessToken createNewToken(Principal principal) {
+        User user = getUser(principal);
+        return accessTokenService.createToken(user.getId());
+    }
+
+    @PutMapping("/{id}")
+    public void invalidateToken(@PathVariable Long id) {
+        accessTokenService.invalidate(id);
+    }
+
+    @GetMapping("/test")
+    public String testSecurity() {
+        return "works";
+    }
+
+
+
+    private User getUser(Principal principal) {
+        String principalName = principal.getName();
+        return userService.findByUsername(principalName)
+                .orElseThrow(() -> new ObjectNotFoundException("User not found"));
+    }
+}
diff --git a/src/main/java/net/geant/nmaas/portal/persistent/entity/AccessToken.java b/src/main/java/net/geant/nmaas/portal/persistent/entity/AccessToken.java
new file mode 100644
index 000000000..2cf56de55
--- /dev/null
+++ b/src/main/java/net/geant/nmaas/portal/persistent/entity/AccessToken.java
@@ -0,0 +1,31 @@
+package net.geant.nmaas.portal.persistent.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "accessTokens")
+@NoArgsConstructor
+@Getter
+@Setter
+@AllArgsConstructor
+public class AccessToken {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    private Long userId;
+
+    private String tokenValue;
+
+    private boolean valid;
+}
diff --git a/src/main/java/net/geant/nmaas/portal/persistent/repositories/AccessTokenRepository.java b/src/main/java/net/geant/nmaas/portal/persistent/repositories/AccessTokenRepository.java
new file mode 100644
index 000000000..7655ad0af
--- /dev/null
+++ b/src/main/java/net/geant/nmaas/portal/persistent/repositories/AccessTokenRepository.java
@@ -0,0 +1,12 @@
+package net.geant.nmaas.portal.persistent.repositories;
+
+import net.geant.nmaas.portal.persistent.entity.AccessToken;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface AccessTokenRepository extends JpaRepository<AccessToken, Long> {
+    List<AccessToken> findAllByUserId(Long userId);
+}
diff --git a/src/main/java/net/geant/nmaas/portal/service/CustomAccessTokenService.java b/src/main/java/net/geant/nmaas/portal/service/CustomAccessTokenService.java
new file mode 100644
index 000000000..5ba1a2268
--- /dev/null
+++ b/src/main/java/net/geant/nmaas/portal/service/CustomAccessTokenService.java
@@ -0,0 +1,13 @@
+package net.geant.nmaas.portal.service;
+
+import net.geant.nmaas.portal.persistent.entity.AccessToken;
+
+import java.util.List;
+
+public interface CustomAccessTokenService {
+
+    void invalidate(Long id);
+    AccessToken createToken(Long userId);
+    List<AccessToken> getAll(Long userId);
+
+}
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/CustomAccessTokenServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/CustomAccessTokenServiceImpl.java
new file mode 100644
index 000000000..90b0cf5ff
--- /dev/null
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/CustomAccessTokenServiceImpl.java
@@ -0,0 +1,54 @@
+package net.geant.nmaas.portal.service.impl;
+
+import lombok.RequiredArgsConstructor;
+import net.geant.nmaas.portal.exceptions.ObjectNotFoundException;
+import net.geant.nmaas.portal.persistent.entity.AccessToken;
+import net.geant.nmaas.portal.persistent.repositories.AccessTokenRepository;
+import net.geant.nmaas.portal.service.CustomAccessTokenService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.UUID;
+
+@Service
+@RequiredArgsConstructor
+public class CustomAccessTokenServiceImpl implements CustomAccessTokenService {
+
+    private final AccessTokenRepository accessTokenRepository;
+
+    @Override
+    public void invalidate(Long id) {
+        AccessToken token = findToken(id);
+        token.setValid(false);
+        accessTokenRepository.save(token);
+    }
+
+    @Override
+    public AccessToken createToken(Long userId) {
+        AccessToken token = createNewToken(userId);
+        return accessTokenRepository.save(token);
+    }
+
+    @Override
+    public List<AccessToken> getAll(Long userId) {
+        return accessTokenRepository.findAllByUserId(userId);
+    }
+
+    private AccessToken createNewToken(Long userId) {
+        AccessToken token = new AccessToken();
+        token.setUserId(userId);
+        token.setTokenValue(generateToken());
+        token.setValid(true);
+        return token;
+        }
+
+    private String generateToken() {
+        return UUID.randomUUID().toString();
+    }
+
+    private AccessToken findToken(Long id) {
+        return accessTokenRepository
+                .findById(id)
+                .orElseThrow(() -> new ObjectNotFoundException("Could not find access token with id: " + id));
+    }
+}
diff --git a/src/test/shell/data/i18n/de.json b/src/test/shell/data/i18n/de.json
index 6d0b19d12..3239b4c99 100644
--- a/src/test/shell/data/i18n/de.json
+++ b/src/test/shell/data/i18n/de.json
@@ -1151,6 +1151,18 @@
       }
     }
   },
+  "TOKENS": {
+    "HEADER": "Tokens",
+    "TABLE": {
+      "ID": "ID",
+      "VALUE": "Value",
+      "VALID": "Valid",
+      "ACTIONS": "Actions"
+    },
+    "BUTTON_INVALIDATE": "Invalidate",
+    "NO_TOKENS": "No tokens",
+    "NEW_TOKEN": "New token"
+  },
   "JSON_EDIT": {
     "INVALID_JSON": "Invalid JSON format. Changes made to the content of the wizard will not be persisted."
   },
diff --git a/src/test/shell/data/i18n/en.json b/src/test/shell/data/i18n/en.json
index db0f617fa..8a1ce458e 100644
--- a/src/test/shell/data/i18n/en.json
+++ b/src/test/shell/data/i18n/en.json
@@ -1153,6 +1153,18 @@
       }
     }
   },
+  "TOKENS": {
+    "HEADER": "Tokens",
+    "TABLE": {
+      "ID": "ID",
+      "VALUE": "Value",
+      "VALID": "Valid",
+      "ACTIONS": "Actions"
+    },
+    "BUTTON_INVALIDATE": "Invalidate",
+    "NO_TOKENS": "No tokens",
+    "NEW_TOKEN": "New token"
+  },
   "JSON_EDIT": {
     "INVALID_JSON": "Invalid JSON format. Changes made to the content of the wizard will not be persisted."
   },
diff --git a/src/test/shell/data/i18n/fr.json b/src/test/shell/data/i18n/fr.json
index 2efa5331d..7a1470747 100644
--- a/src/test/shell/data/i18n/fr.json
+++ b/src/test/shell/data/i18n/fr.json
@@ -1151,6 +1151,18 @@
       }
     }
   },
+  "TOKENS": {
+    "HEADER": "Tokens",
+    "TABLE": {
+      "ID": "ID",
+      "VALUE": "Value",
+      "VALID": "Valid",
+      "ACTIONS": "Actions"
+    },
+    "BUTTON_INVALIDATE": "Invalidate",
+    "NO_TOKENS": "No tokens",
+    "NEW_TOKEN": "New token"
+  },
   "JSON_EDIT": {
     "INVALID_JSON": "Invalid JSON format. Changes made to the content of the wizard will not be persisted."
   },
diff --git a/src/test/shell/data/i18n/pl.json b/src/test/shell/data/i18n/pl.json
index 992805e42..25f8a91e0 100644
--- a/src/test/shell/data/i18n/pl.json
+++ b/src/test/shell/data/i18n/pl.json
@@ -1152,6 +1152,18 @@
       }
     }
   },
+  "TOKENS": {
+    "HEADER": "Tokens",
+    "TABLE": {
+      "ID": "ID",
+      "VALUE": "Value",
+      "VALID": "Valid",
+      "ACTIONS": "Actions"
+    },
+    "BUTTON_INVALIDATE": "Invalidate",
+    "NO_TOKENS": "No tokens",
+    "NEW_TOKEN": "New token"
+  },
   "JSON_EDIT": {
     "INVALID_JSON": "Niewłaściwy format JSON. Zmiany wprowadzone do zawartości formularza nie zostaną zapisane."
   },
-- 
GitLab


From 8e0478f739bcc64885cc3c2e3a65d5e1288b3829 Mon Sep 17 00:00:00 2001
From: pgiertych <pgiertych@man.poznan.pl>
Date: Wed, 10 Apr 2024 17:14:02 +0200
Subject: [PATCH 04/50] remove test controller

---
 .../portal/api/security/CustomAccessTokenController.java   | 7 -------
 .../portal/service/impl/CustomAccessTokenServiceImpl.java  | 1 +
 2 files changed, 1 insertion(+), 7 deletions(-)

diff --git a/src/main/java/net/geant/nmaas/portal/api/security/CustomAccessTokenController.java b/src/main/java/net/geant/nmaas/portal/api/security/CustomAccessTokenController.java
index 1a7d19a72..647f129bf 100644
--- a/src/main/java/net/geant/nmaas/portal/api/security/CustomAccessTokenController.java
+++ b/src/main/java/net/geant/nmaas/portal/api/security/CustomAccessTokenController.java
@@ -43,13 +43,6 @@ public class CustomAccessTokenController {
         accessTokenService.invalidate(id);
     }
 
-    @GetMapping("/test")
-    public String testSecurity() {
-        return "works";
-    }
-
-
-
     private User getUser(Principal principal) {
         String principalName = principal.getName();
         return userService.findByUsername(principalName)
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/CustomAccessTokenServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/CustomAccessTokenServiceImpl.java
index 90b0cf5ff..a2a6ff7bf 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/CustomAccessTokenServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/CustomAccessTokenServiceImpl.java
@@ -43,6 +43,7 @@ public class CustomAccessTokenServiceImpl implements CustomAccessTokenService {
         }
 
     private String generateToken() {
+        // uuid is a placeholder for now
         return UUID.randomUUID().toString();
     }
 
-- 
GitLab


From b4555d2a035a2fbaa9c16ab5a51919a04486cfa3 Mon Sep 17 00:00:00 2001
From: pgiertych <pgiertych@man.poznan.pl>
Date: Wed, 10 Apr 2024 18:07:36 +0200
Subject: [PATCH 05/50] add name to access token

---
 .../security/CustomAccessTokenController.java |  5 +--
 .../portal/persistent/entity/AccessToken.java |  2 ++
 .../service/CustomAccessTokenService.java     |  2 +-
 .../impl/CustomAccessTokenServiceImpl.java    |  7 ++--
 src/test/shell/data/i18n/de.json              | 14 +++++++-
 src/test/shell/data/i18n/en.json              | 14 +++++++-
 src/test/shell/data/i18n/fr.json              | 34 +++++++++++++------
 src/test/shell/data/i18n/pl.json              | 14 +++++++-
 8 files changed, 72 insertions(+), 20 deletions(-)

diff --git a/src/main/java/net/geant/nmaas/portal/api/security/CustomAccessTokenController.java b/src/main/java/net/geant/nmaas/portal/api/security/CustomAccessTokenController.java
index 647f129bf..66839fd38 100644
--- a/src/main/java/net/geant/nmaas/portal/api/security/CustomAccessTokenController.java
+++ b/src/main/java/net/geant/nmaas/portal/api/security/CustomAccessTokenController.java
@@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
@@ -33,9 +34,9 @@ public class CustomAccessTokenController {
     }
 
     @PostMapping()
-    public AccessToken createNewToken(Principal principal) {
+    public AccessToken createNewToken(Principal principal, @RequestBody String name) {
         User user = getUser(principal);
-        return accessTokenService.createToken(user.getId());
+        return accessTokenService.createToken(user.getId(), name);
     }
 
     @PutMapping("/{id}")
diff --git a/src/main/java/net/geant/nmaas/portal/persistent/entity/AccessToken.java b/src/main/java/net/geant/nmaas/portal/persistent/entity/AccessToken.java
index 2cf56de55..7d2f1932d 100644
--- a/src/main/java/net/geant/nmaas/portal/persistent/entity/AccessToken.java
+++ b/src/main/java/net/geant/nmaas/portal/persistent/entity/AccessToken.java
@@ -23,6 +23,8 @@ public class AccessToken {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
 
+    private String name;
+
     private Long userId;
 
     private String tokenValue;
diff --git a/src/main/java/net/geant/nmaas/portal/service/CustomAccessTokenService.java b/src/main/java/net/geant/nmaas/portal/service/CustomAccessTokenService.java
index 5ba1a2268..96e59e1eb 100644
--- a/src/main/java/net/geant/nmaas/portal/service/CustomAccessTokenService.java
+++ b/src/main/java/net/geant/nmaas/portal/service/CustomAccessTokenService.java
@@ -7,7 +7,7 @@ import java.util.List;
 public interface CustomAccessTokenService {
 
     void invalidate(Long id);
-    AccessToken createToken(Long userId);
+    AccessToken createToken(Long userId, String name);
     List<AccessToken> getAll(Long userId);
 
 }
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/CustomAccessTokenServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/CustomAccessTokenServiceImpl.java
index a2a6ff7bf..58bea7e30 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/CustomAccessTokenServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/CustomAccessTokenServiceImpl.java
@@ -24,8 +24,8 @@ public class CustomAccessTokenServiceImpl implements CustomAccessTokenService {
     }
 
     @Override
-    public AccessToken createToken(Long userId) {
-        AccessToken token = createNewToken(userId);
+    public AccessToken createToken(Long userId, String name) {
+        AccessToken token = createNewToken(userId, name);
         return accessTokenRepository.save(token);
     }
 
@@ -34,8 +34,9 @@ public class CustomAccessTokenServiceImpl implements CustomAccessTokenService {
         return accessTokenRepository.findAllByUserId(userId);
     }
 
-    private AccessToken createNewToken(Long userId) {
+    private AccessToken createNewToken(Long userId, String name) {
         AccessToken token = new AccessToken();
+        token.setName(name);
         token.setUserId(userId);
         token.setTokenValue(generateToken());
         token.setValid(true);
diff --git a/src/test/shell/data/i18n/de.json b/src/test/shell/data/i18n/de.json
index 3239b4c99..e833a24cf 100644
--- a/src/test/shell/data/i18n/de.json
+++ b/src/test/shell/data/i18n/de.json
@@ -1155,13 +1155,25 @@
     "HEADER": "Tokens",
     "TABLE": {
       "ID": "ID",
+      "NAME": "Name",
       "VALUE": "Value",
       "VALID": "Valid",
       "ACTIONS": "Actions"
     },
+    "MODAL": {
+      "HEADER": "Add new access token",
+      "NAME": "Name",
+      "BUTTON_ADD": "Submit",
+      "BUTTON_CANCEL": "Cancel",
+      "ERROR": {
+        "NAME_REQUIRED": "Name is required",
+        "NAME_MINLENGTH": "Name must have at least 1 character",
+        "NAME_MAXLENGTH": "Name cannot be longer than 16 characters"
+      }
+    },
     "BUTTON_INVALIDATE": "Invalidate",
     "NO_TOKENS": "No tokens",
-    "NEW_TOKEN": "New token"
+    "NEW_TOKEN": "Add new token"
   },
   "JSON_EDIT": {
     "INVALID_JSON": "Invalid JSON format. Changes made to the content of the wizard will not be persisted."
diff --git a/src/test/shell/data/i18n/en.json b/src/test/shell/data/i18n/en.json
index 8a1ce458e..c04aeb3f4 100644
--- a/src/test/shell/data/i18n/en.json
+++ b/src/test/shell/data/i18n/en.json
@@ -1157,13 +1157,25 @@
     "HEADER": "Tokens",
     "TABLE": {
       "ID": "ID",
+      "NAME": "Name",
       "VALUE": "Value",
       "VALID": "Valid",
       "ACTIONS": "Actions"
     },
+    "MODAL": {
+      "HEADER": "Add new access token",
+      "NAME": "Name",
+      "BUTTON_ADD": "Submit",
+      "BUTTON_CANCEL": "Cancel",
+      "ERROR": {
+        "NAME_REQUIRED": "Name is required",
+        "NAME_MINLENGTH": "Name must have at least 1 character",
+        "NAME_MAXLENGTH": "Name cannot be longer than 16 characters"
+      }
+    },
     "BUTTON_INVALIDATE": "Invalidate",
     "NO_TOKENS": "No tokens",
-    "NEW_TOKEN": "New token"
+    "NEW_TOKEN": "Add new token"
   },
   "JSON_EDIT": {
     "INVALID_JSON": "Invalid JSON format. Changes made to the content of the wizard will not be persisted."
diff --git a/src/test/shell/data/i18n/fr.json b/src/test/shell/data/i18n/fr.json
index 7a1470747..fdc7764b4 100644
--- a/src/test/shell/data/i18n/fr.json
+++ b/src/test/shell/data/i18n/fr.json
@@ -1151,18 +1151,30 @@
       }
     }
   },
-  "TOKENS": {
-    "HEADER": "Tokens",
-    "TABLE": {
-      "ID": "ID",
-      "VALUE": "Value",
-      "VALID": "Valid",
-      "ACTIONS": "Actions"
-    },
-    "BUTTON_INVALIDATE": "Invalidate",
-    "NO_TOKENS": "No tokens",
-    "NEW_TOKEN": "New token"
+  ""TOKENS": {
+  "HEADER": "Tokens",
+  "TABLE": {
+    "ID": "ID",
+    "NAME": "Name",
+    "VALUE": "Value",
+    "VALID": "Valid",
+    "ACTIONS": "Actions"
+  },
+  "MODAL": {
+    "HEADER": "Add new access token",
+    "NAME": "Name",
+    "BUTTON_ADD": "Submit",
+    "BUTTON_CANCEL": "Cancel",
+    "ERROR": {
+      "NAME_REQUIRED": "Name is required",
+      "NAME_MINLENGTH": "Name must have at least 1 character",
+      "NAME_MAXLENGTH": "Name cannot be longer than 16 characters"
+    }
   },
+  "BUTTON_INVALIDATE": "Invalidate",
+  "NO_TOKENS": "No tokens",
+  "NEW_TOKEN": "Add new token"
+},
   "JSON_EDIT": {
     "INVALID_JSON": "Invalid JSON format. Changes made to the content of the wizard will not be persisted."
   },
diff --git a/src/test/shell/data/i18n/pl.json b/src/test/shell/data/i18n/pl.json
index 25f8a91e0..304c8ab41 100644
--- a/src/test/shell/data/i18n/pl.json
+++ b/src/test/shell/data/i18n/pl.json
@@ -1156,13 +1156,25 @@
     "HEADER": "Tokens",
     "TABLE": {
       "ID": "ID",
+      "NAME": "Name",
       "VALUE": "Value",
       "VALID": "Valid",
       "ACTIONS": "Actions"
     },
+    "MODAL": {
+      "HEADER": "Add new access token",
+      "NAME": "Name",
+      "BUTTON_ADD": "Submit",
+      "BUTTON_CANCEL": "Cancel",
+      "ERROR": {
+        "NAME_REQUIRED": "Name is required",
+        "NAME_MINLENGTH": "Name must have at least 1 character",
+        "NAME_MAXLENGTH": "Name cannot be longer than 16 characters"
+      }
+    },
     "BUTTON_INVALIDATE": "Invalidate",
     "NO_TOKENS": "No tokens",
-    "NEW_TOKEN": "New token"
+    "NEW_TOKEN": "Add new token"
   },
   "JSON_EDIT": {
     "INVALID_JSON": "Niewłaściwy format JSON. Zmiany wprowadzone do zawartości formularza nie zostaną zapisane."
-- 
GitLab


From cb222262ed5f4122d5bb086e753163037f364aa6 Mon Sep 17 00:00:00 2001
From: pgiertych <pgiertych@man.poznan.pl>
Date: Thu, 11 Apr 2024 10:32:42 +0200
Subject: [PATCH 06/50] fix translations

---
 src/test/shell/data/i18n/fr.json | 46 ++++++++++++++++----------------
 1 file changed, 23 insertions(+), 23 deletions(-)

diff --git a/src/test/shell/data/i18n/fr.json b/src/test/shell/data/i18n/fr.json
index fdc7764b4..5c11bc30c 100644
--- a/src/test/shell/data/i18n/fr.json
+++ b/src/test/shell/data/i18n/fr.json
@@ -1151,30 +1151,30 @@
       }
     }
   },
-  ""TOKENS": {
-  "HEADER": "Tokens",
-  "TABLE": {
-    "ID": "ID",
-    "NAME": "Name",
-    "VALUE": "Value",
-    "VALID": "Valid",
-    "ACTIONS": "Actions"
-  },
-  "MODAL": {
-    "HEADER": "Add new access token",
-    "NAME": "Name",
-    "BUTTON_ADD": "Submit",
-    "BUTTON_CANCEL": "Cancel",
-    "ERROR": {
-      "NAME_REQUIRED": "Name is required",
-      "NAME_MINLENGTH": "Name must have at least 1 character",
-      "NAME_MAXLENGTH": "Name cannot be longer than 16 characters"
-    }
+  "TOKENS": {
+    "HEADER": "Tokens",
+    "TABLE": {
+      "ID": "ID",
+      "NAME": "Name",
+      "VALUE": "Value",
+      "VALID": "Valid",
+      "ACTIONS": "Actions"
+    },
+    "MODAL": {
+      "HEADER": "Add new access token",
+      "NAME": "Name",
+      "BUTTON_ADD": "Submit",
+      "BUTTON_CANCEL": "Cancel",
+      "ERROR": {
+        "NAME_REQUIRED": "Name is required",
+        "NAME_MINLENGTH": "Name must have at least 1 character",
+        "NAME_MAXLENGTH": "Name cannot be longer than 16 characters"
+      }
+    },
+    "BUTTON_INVALIDATE": "Invalidate",
+    "NO_TOKENS": "No tokens",
+    "NEW_TOKEN": "Add new token"
   },
-  "BUTTON_INVALIDATE": "Invalidate",
-  "NO_TOKENS": "No tokens",
-  "NEW_TOKEN": "Add new token"
-},
   "JSON_EDIT": {
     "INVALID_JSON": "Invalid JSON format. Changes made to the content of the wizard will not be persisted."
   },
-- 
GitLab


From eee0221b230251b65d157a95d2b6b5e15e2e7b19 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Wed, 4 Sep 2024 10:49:07 +0200
Subject: [PATCH 07/50] add state handler for APPLICATION_REMOVED

---
 .../orchestration/entities/AppDeploymentState.java    | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/src/main/java/net/geant/nmaas/orchestration/entities/AppDeploymentState.java b/src/main/java/net/geant/nmaas/orchestration/entities/AppDeploymentState.java
index c2e158254..b35e07d63 100644
--- a/src/main/java/net/geant/nmaas/orchestration/entities/AppDeploymentState.java
+++ b/src/main/java/net/geant/nmaas/orchestration/entities/AppDeploymentState.java
@@ -412,10 +412,15 @@ public enum AppDeploymentState {
 
         @Override
         public AppDeploymentState nextState(NmServiceDeploymentState state) {
-            if  (NmServiceDeploymentState.CONFIGURATION_REMOVAL_INITIATED.equals(state)) {
-                return APPLICATION_CONFIGURATION_REMOVAL_IN_PROGRESS;
+            switch (state) {
+                case DEPLOYMENT_FAILED:
+                    return APPLICATION_DEPLOYMENT_FAILED;
+                case CONFIGURATION_REMOVAL_INITIATED:
+                    return APPLICATION_CONFIGURATION_REMOVAL_IN_PROGRESS;
+                default:
+                        return nextStateForNotMatchingNmServiceDeploymentState(this, state);
             }
-            return nextStateForNotMatchingNmServiceDeploymentState(this, state);
+
         }
 
         @Override
-- 
GitLab


From 32cb15fa32424cc07488a26e6e5be44b0636b01c Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Wed, 4 Sep 2024 13:44:21 +0200
Subject: [PATCH 08/50] add refresh label

---
 src/test/shell/data/i18n/de.json | 3 ++-
 src/test/shell/data/i18n/en.json | 3 ++-
 src/test/shell/data/i18n/fr.json | 3 ++-
 src/test/shell/data/i18n/pl.json | 3 ++-
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/test/shell/data/i18n/de.json b/src/test/shell/data/i18n/de.json
index e3e42ed33..7c918b109 100644
--- a/src/test/shell/data/i18n/de.json
+++ b/src/test/shell/data/i18n/de.json
@@ -1212,7 +1212,8 @@
       "DOMAIN" : "Domain",
       "CHECK_STATE" : "Check state",
       "UPLOAD_TEXT" : "or enter CSV content below",
-      "DOWNLOAD_CSV" : "Download CSV"
+      "DOWNLOAD_CSV" : "Download CSV",
+      "REFRESH" : "Refresh states"
     },
     "STATE" : {
       "PENDING" :  "Pending",
diff --git a/src/test/shell/data/i18n/en.json b/src/test/shell/data/i18n/en.json
index 402870080..b81160208 100644
--- a/src/test/shell/data/i18n/en.json
+++ b/src/test/shell/data/i18n/en.json
@@ -1215,7 +1215,8 @@
       "DOMAIN" : "Domain",
       "CHECK_STATE" : "Check state",
       "UPLOAD_TEXT" : "or enter the csv content below",
-      "DOWNLOAD_CSV" : "Download CSV"
+      "DOWNLOAD_CSV" : "Download CSV",
+      "REFRESH" : "Refresh states"
     },
     "STATE" : {
       "PENDING" :  "Pending",
diff --git a/src/test/shell/data/i18n/fr.json b/src/test/shell/data/i18n/fr.json
index 48d78d0ed..6804efd13 100644
--- a/src/test/shell/data/i18n/fr.json
+++ b/src/test/shell/data/i18n/fr.json
@@ -1213,7 +1213,8 @@
       "DOMAIN" : "Domain",
       "CHECK_STATE" : "Check state",
       "UPLOAD_TEXT" : "or enter CSV content below",
-      "DOWNLOAD_CSV" : "Download CSV"
+      "DOWNLOAD_CSV" : "Download CSV",
+      "REFRESH" : "Refresh states"
     },
     "STATE" : {
       "PENDING" :  "Pending",
diff --git a/src/test/shell/data/i18n/pl.json b/src/test/shell/data/i18n/pl.json
index d27810427..f96811b72 100644
--- a/src/test/shell/data/i18n/pl.json
+++ b/src/test/shell/data/i18n/pl.json
@@ -1214,7 +1214,8 @@
       "DOMAIN" : "Domena",
       "CHECK_STATE" : "Sprawdź status",
       "UPLOAD_TEXT" : "lub podaj tekst w formacie CSV poniżej",
-      "DOWNLOAD_CSV" : "Pobierz CSV"
+      "DOWNLOAD_CSV" : "Pobierz CSV",
+      "REFRESH" : "Odśwież stany"
     },
     "STATE" : {
       "PENDING" :  "Oczekuje",
-- 
GitLab


From 0cda3fc8685bfb9ddf9d83f80c3305af43c94872 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Wed, 4 Sep 2024 13:44:34 +0200
Subject: [PATCH 09/50] Bulk update status command

---
 .../nmaas/portal/api/bulk/BulkController.java | 16 +++++-
 .../service/BulkApplicationService.java       |  3 ++
 .../impl/BulkApplicationServiceImpl.java      | 53 +++++++++++++++++++
 3 files changed, 71 insertions(+), 1 deletion(-)

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 c42118894..a03794d9d 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
@@ -133,7 +133,7 @@ public class BulkController {
         User user = this.userService.findByUsername(principal.getName()).orElseThrow(() -> new MissingElementException("Missing user " + principal.getName()));
 
         return ResponseEntity.ok(mapToView(bulkDeploymentRepository.findByType(BulkType.DOMAIN)).stream()
-                    .filter(bulk -> bulk.getCreator().getId().equals(user.getId())).collect(Collectors.toList()));
+                .filter(bulk -> bulk.getCreator().getId().equals(user.getId())).collect(Collectors.toList()));
     }
 
     @GetMapping("/apps")
@@ -151,6 +151,12 @@ public class BulkController {
                 .filter(bulk -> bulk.getCreator().getId().equals(user.getId())).collect(Collectors.toList()));
     }
 
+    @GetMapping("/refresh/{id}")
+    @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_VL_MANAGER')")
+    public ResponseEntity<BulkDeploymentViewS> getRefreshedState(@PathVariable Long id) {
+        return ResponseEntity.ok(mapToView(this.bulkApplicationService.updateState(id)));
+    }
+
     private List<BulkDeploymentViewS> mapToView(List<BulkDeployment> deployments) {
         return deployments.stream()
                 .map(bulk -> {
@@ -162,6 +168,14 @@ public class BulkController {
                 .collect(Collectors.toList());
     }
 
+    private BulkDeploymentView mapToView(BulkDeployment deployment) {
+        BulkDeploymentView bulkView = modelMapper.map(deployment, BulkDeploymentView.class);
+        bulkView.setCreator(getUserView(deployment.getCreatorId()));
+        mapDetails(deployment, bulkView);
+        return bulkView;
+
+    }
+
     private void mapDetails(BulkDeployment deployment, BulkDeploymentViewS view) {
         if (deployment.getType().equals(BulkType.APPLICATION)) {
             Map<String, String> details = new HashMap<>();
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 bebf1c9f8..08583400d 100644
--- a/src/main/java/net/geant/nmaas/portal/service/BulkApplicationService.java
+++ b/src/main/java/net/geant/nmaas/portal/service/BulkApplicationService.java
@@ -7,6 +7,7 @@ import net.geant.nmaas.portal.api.bulk.BulkDeploymentView;
 import net.geant.nmaas.portal.api.bulk.BulkDeploymentViewS;
 import net.geant.nmaas.portal.api.bulk.CsvApplication;
 import net.geant.nmaas.portal.api.domain.UserViewMinimal;
+import net.geant.nmaas.portal.persistent.entity.BulkDeployment;
 import org.springframework.context.ApplicationEvent;
 import org.springframework.core.io.InputStreamResource;
 
@@ -24,4 +25,6 @@ public interface BulkApplicationService {
 
     InputStreamResource getInputStreamAppBulkDetails(List<BulkAppDetails> list );
 
+    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 aea95a4ed..cb73a8fe9 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
@@ -55,6 +55,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -300,6 +301,58 @@ public class BulkApplicationServiceImpl implements BulkApplicationService {
         );
     }
 
+    @Override
+    public BulkDeployment updateState(Long bulkId) {
+        log.info("Update all states for bulk {}", bulkId);
+        Optional<BulkDeployment> bulk = this.bulkDeploymentRepository.findById(bulkId);
+        if(bulk.isPresent()) {
+            BulkDeployment bulkDeployment = bulk.get();
+            bulkDeployment.getEntries().forEach(entry -> {
+                try {
+                    Long instanceId = Long.valueOf(entry.getDetails().get(BULK_ENTRY_DETAIL_KEY_APP_INSTANCE_ID));
+                    AppInstance instance = instanceService.find(instanceId).orElseThrow();
+
+                    AppLifecycleState state = appDeploymentMonitor.state(instance.getInternalId());
+                    log.error("For bulk entries {} state = {}", entry.getId(), state.toString());
+                    switch (state) {
+                        case APPLICATION_DEPLOYMENT_VERIFIED:
+                            entry.setState(BulkDeploymentState.COMPLETED);
+                            bulkDeploymentEntryRepository.save(entry);
+                            break;
+                        case APPLICATION_CONFIGURATION_FAILED:
+                        case APPLICATION_DEPLOYMENT_FAILED:
+                        case APPLICATION_DEPLOYMENT_VERIFICATION_FAILED:
+                        case INTERNAL_ERROR:
+                            entry.setState(BulkDeploymentState.FAILED);
+                            bulkDeploymentEntryRepository.save(entry);
+                            break;
+                        default:
+                            entry.setState(BulkDeploymentState.PENDING);
+                            bulkDeploymentEntryRepository.save(entry);
+                            break;
+                    }
+                    logBulkStateUpdate(entry.getId(), entry.getState().name());
+
+                } catch (Exception e) {
+                    log.error("Can not update state of {} bulk entry", entry.getId());
+                }
+            });
+            if (bulkDeployment.getEntries().stream().allMatch(e -> BulkDeploymentState.COMPLETED.equals(e.getState()))) {
+                bulkDeployment.setState(BulkDeploymentState.COMPLETED);
+            } else if (bulkDeployment.getEntries().stream().allMatch(e -> BulkDeploymentState.FAILED.equals(e.getState()))) {
+                bulkDeployment.setState(BulkDeploymentState.FAILED);
+            } else if (bulkDeployment.getEntries().stream().anyMatch(e -> BulkDeploymentState.FAILED.equals(e.getState()))) {
+                bulkDeployment.setState(BulkDeploymentState.PARTIALLY_FAILED);
+            }
+            logBulkStateUpdate(bulkDeployment.getId(),bulkDeployment.getState().name());
+            bulkDeploymentRepository.save(bulkDeployment);
+            return bulkDeployment;
+        } else {
+            log.error("Can not find bulk deployment {}", bulkId);
+            throw new MissingElementException("Can not find bulk deployment " + bulkId);
+        }
+    }
+
     private static BulkDeployment createBulkDeployment(UserViewMinimal creator) {
         BulkDeployment bulkDeployment = new BulkDeployment();
         bulkDeployment.setType(BulkType.APPLICATION);
-- 
GitLab


From d686d1f2744cc8f80063f0cb836aa6fcf208ffab Mon Sep 17 00:00:00 2001
From: llopat <llopat@man.poznan.pl>
Date: Fri, 6 Sep 2024 13:54:07 +0200
Subject: [PATCH 10/50] Update version to 1.7.0-SNAPSHOT

---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index 94458330e..3a0d07daa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,7 @@ repositories {
     mavenCentral()
 }
 
-version = '1.6.3'
+version = '1.7.0-SNAPSHOT'
 group = 'net.geant.nmaas'
 
 java {
-- 
GitLab


From 404616eafeeeaa3bfec378951e93599f59a1db5c Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Thu, 19 Sep 2024 09:30:56 +0200
Subject: [PATCH 11/50] Bulk delete update

---
 .../geant/nmaas/portal/api/bulk/BulkController.java   | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

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 df2fd49cc..ea7f46dd5 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,6 +14,7 @@ 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;
@@ -151,16 +152,22 @@ public class BulkController {
     }
 
     @DeleteMapping("/{id}")
-    @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN')")
+    @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_VL_MANAGER')")
     public ResponseEntity<Void> removeBulkDeployment(
             @PathVariable Long id,
-            @RequestParam(name = "removeAll") boolean removeApps
+            @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));
         }
-- 
GitLab


From d6830306abf17a5d260396c92427a7e9ef27f1b2 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Fri, 20 Sep 2024 14:20:38 +0200
Subject: [PATCH 12/50] update merge

---
 .../java/net/geant/nmaas/portal/api/bulk/BulkController.java | 5 +++++
 1 file changed, 5 insertions(+)

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 b92cedea4..0ae731c41 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
@@ -176,6 +176,11 @@ public class BulkController {
     }
 
     private List<BulkDeploymentViewS> mapToViewList(List<BulkDeployment> deployments) {
+        return deployments.stream()
+                .map(bulk -> mapToView(bulk, BulkDeploymentViewS.class))
+                .collect(Collectors.toList());
+    }
+
     @GetMapping("/refresh/{id}")
     @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_VL_MANAGER')")
     public ResponseEntity<BulkDeploymentViewS> getRefreshedState(@PathVariable Long id) {
-- 
GitLab


From 5816101ec682548735a878cdb8d9cfc14b4dbe3c Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Fri, 20 Sep 2024 14:33:38 +0200
Subject: [PATCH 13/50] Add flyway for accessToken

---
 .../geant/nmaas/portal/persistent/entity/AccessToken.java | 2 +-
 src/main/resources/application.properties                 | 4 ++--
 .../common/V1.7.0_20240920_1430__addedAccessTokens.sql    | 8 ++++++++
 3 files changed, 11 insertions(+), 3 deletions(-)
 create mode 100644 src/main/resources/db/migration/common/V1.7.0_20240920_1430__addedAccessTokens.sql

diff --git a/src/main/java/net/geant/nmaas/portal/persistent/entity/AccessToken.java b/src/main/java/net/geant/nmaas/portal/persistent/entity/AccessToken.java
index 7d2f1932d..80c117b2a 100644
--- a/src/main/java/net/geant/nmaas/portal/persistent/entity/AccessToken.java
+++ b/src/main/java/net/geant/nmaas/portal/persistent/entity/AccessToken.java
@@ -12,7 +12,7 @@ import javax.persistence.Id;
 import javax.persistence.Table;
 
 @Entity
-@Table(name = "accessTokens")
+@Table(name = "access_tokens")
 @NoArgsConstructor
 @Getter
 @Setter
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index de4403d1a..61b016016 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -60,8 +60,8 @@ db.standalone.username=nmaas
 db.standalone.password=nmaas
 # Common
 spring.h2.console.enabled=true
-spring.jpa.properties.hibernate.show_sql=false
-spring.jpa.properties.hibernate.format_sql=false
+spring.jpa.properties.hibernate.show_sql=true
+spring.jpa.properties.hibernate.format_sql=true
 spring.jpa.properties.hibernate.connection.autocommit=false
 spring.jpa.hibernate.ddl-auto=create-drop
 spring.jpa.show-sql=true
diff --git a/src/main/resources/db/migration/common/V1.7.0_20240920_1430__addedAccessTokens.sql b/src/main/resources/db/migration/common/V1.7.0_20240920_1430__addedAccessTokens.sql
new file mode 100644
index 000000000..cf5778322
--- /dev/null
+++ b/src/main/resources/db/migration/common/V1.7.0_20240920_1430__addedAccessTokens.sql
@@ -0,0 +1,8 @@
+create table access_tokens (
+       id bigint generated by default as identity,
+        name varchar(255),
+        token_value varchar(255),
+        user_id bigint,
+        valid boolean not null,
+        primary key (id)
+    )
\ No newline at end of file
-- 
GitLab


From bfd52b2f1e07536149ff84de0451aa4cc384d6de Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Fri, 20 Sep 2024 14:33:50 +0200
Subject: [PATCH 14/50] Add flyway for accessToken

---
 .../common/V1.7.0_20240920_1430__addedAccessTokens.sql          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/resources/db/migration/common/V1.7.0_20240920_1430__addedAccessTokens.sql b/src/main/resources/db/migration/common/V1.7.0_20240920_1430__addedAccessTokens.sql
index cf5778322..d81247f35 100644
--- a/src/main/resources/db/migration/common/V1.7.0_20240920_1430__addedAccessTokens.sql
+++ b/src/main/resources/db/migration/common/V1.7.0_20240920_1430__addedAccessTokens.sql
@@ -5,4 +5,4 @@ create table access_tokens (
         user_id bigint,
         valid boolean not null,
         primary key (id)
-    )
\ No newline at end of file
+ ó);
\ No newline at end of file
-- 
GitLab


From a5306cc1cf3721368ac55e4f2ceca9d0a00f18f3 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Fri, 20 Sep 2024 14:33:58 +0200
Subject: [PATCH 15/50] Add flyway for accessToken

---
 .../common/V1.7.0_20240920_1430__addedAccessTokens.sql          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/resources/db/migration/common/V1.7.0_20240920_1430__addedAccessTokens.sql b/src/main/resources/db/migration/common/V1.7.0_20240920_1430__addedAccessTokens.sql
index d81247f35..999454960 100644
--- a/src/main/resources/db/migration/common/V1.7.0_20240920_1430__addedAccessTokens.sql
+++ b/src/main/resources/db/migration/common/V1.7.0_20240920_1430__addedAccessTokens.sql
@@ -5,4 +5,4 @@ create table access_tokens (
         user_id bigint,
         valid boolean not null,
         primary key (id)
- ó);
\ No newline at end of file
+ );
\ No newline at end of file
-- 
GitLab


From 21b1e4b3ba90a0b3a78ef363b560f6956648d943 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Fri, 20 Sep 2024 16:01:09 +0200
Subject: [PATCH 16/50] reverse sql

---
 src/main/resources/application.properties | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 61b016016..de4403d1a 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -60,8 +60,8 @@ db.standalone.username=nmaas
 db.standalone.password=nmaas
 # Common
 spring.h2.console.enabled=true
-spring.jpa.properties.hibernate.show_sql=true
-spring.jpa.properties.hibernate.format_sql=true
+spring.jpa.properties.hibernate.show_sql=false
+spring.jpa.properties.hibernate.format_sql=false
 spring.jpa.properties.hibernate.connection.autocommit=false
 spring.jpa.hibernate.ddl-auto=create-drop
 spring.jpa.show-sql=true
-- 
GitLab


From f1ffc4392507d4ce1bb972e7bcff3da0d7d9eb3f Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Fri, 20 Sep 2024 16:02:55 +0200
Subject: [PATCH 17/50] delete log

---
 .../nmaas/portal/service/impl/BulkApplicationServiceImpl.java    | 1 -
 1 file changed, 1 deletion(-)

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 9f6544972..c7e5ae6c4 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
@@ -333,7 +333,6 @@ public class BulkApplicationServiceImpl implements BulkApplicationService {
                     AppInstance instance = instanceService.find(instanceId).orElseThrow();
 
                     AppLifecycleState state = appDeploymentMonitor.state(instance.getInternalId());
-                    log.error("For bulk entries {} state = {}", entry.getId(), state.toString());
                     switch (state) {
                         case APPLICATION_DEPLOYMENT_VERIFIED:
                             entry.setState(BulkDeploymentState.COMPLETED);
-- 
GitLab


From 8e50b2bd42497f747ed25b37000bd2d8d552bee4 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Tue, 24 Sep 2024 11:19:12 +0200
Subject: [PATCH 18/50] testing handler

---
 .../exceptions/IllegalExceptionHandler.java   | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 src/main/java/net/geant/nmaas/portal/exceptions/IllegalExceptionHandler.java

diff --git a/src/main/java/net/geant/nmaas/portal/exceptions/IllegalExceptionHandler.java b/src/main/java/net/geant/nmaas/portal/exceptions/IllegalExceptionHandler.java
new file mode 100644
index 000000000..5c83a2679
--- /dev/null
+++ b/src/main/java/net/geant/nmaas/portal/exceptions/IllegalExceptionHandler.java
@@ -0,0 +1,20 @@
+package net.geant.nmaas.portal.exceptions;
+
+import net.bytebuddy.pool.TypePool;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+
+@ControllerAdvice
+public class IllegalExceptionHandler {
+
+    @ExceptionHandler(IllegalArgumentException.class)
+    public ResponseEntity<?> handle(IllegalArgumentException ex) {
+        throw new ObjectAlreadyExistsException(ex.getMessage());
+    }
+
+    @ExceptionHandler(IllegalStateException.class)
+    public ResponseEntity<?> handle(IllegalStateException ex) {
+        throw new ObjectAlreadyExistsException(ex.getMessage());
+    }
+}
-- 
GitLab


From 8b5451c0f229e150e1f84a522ba570c4895db041 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Tue, 24 Sep 2024 12:20:15 +0200
Subject: [PATCH 19/50] delete appbase from appstateperdomain

---
 .../geant/nmaas/portal/persistent/entity/DomainGroup.java  | 1 +
 .../net/geant/nmaas/portal/service/DomainGroupService.java | 3 +++
 .../service/impl/ApplicationStatePerDomainServiceImpl.java | 1 -
 .../nmaas/portal/service/impl/DomainGroupServiceImpl.java  | 7 +++++++
 .../geant/nmaas/portal/service/impl/DomainServiceImpl.java | 2 +-
 5 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/main/java/net/geant/nmaas/portal/persistent/entity/DomainGroup.java b/src/main/java/net/geant/nmaas/portal/persistent/entity/DomainGroup.java
index 36f735f09..1533eaea4 100644
--- a/src/main/java/net/geant/nmaas/portal/persistent/entity/DomainGroup.java
+++ b/src/main/java/net/geant/nmaas/portal/persistent/entity/DomainGroup.java
@@ -23,6 +23,7 @@ import javax.validation.constraints.NotNull;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 @Entity
diff --git a/src/main/java/net/geant/nmaas/portal/service/DomainGroupService.java b/src/main/java/net/geant/nmaas/portal/service/DomainGroupService.java
index f8609b69c..a95e5c3c1 100644
--- a/src/main/java/net/geant/nmaas/portal/service/DomainGroupService.java
+++ b/src/main/java/net/geant/nmaas/portal/service/DomainGroupService.java
@@ -1,6 +1,7 @@
 package net.geant.nmaas.portal.service;
 
 import net.geant.nmaas.portal.api.domain.DomainGroupView;
+import net.geant.nmaas.portal.persistent.entity.ApplicationBase;
 import net.geant.nmaas.portal.persistent.entity.Domain;
 
 import java.util.List;
@@ -23,4 +24,6 @@ public interface DomainGroupService {
 
     DomainGroupView updateDomainGroup(Long domainGroupId, DomainGroupView view);
 
+    void deleteAppBaseFromAllAppState(ApplicationBase base);
+
 }
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationStatePerDomainServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationStatePerDomainServiceImpl.java
index cd043eb0f..297af2ebc 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationStatePerDomainServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationStatePerDomainServiceImpl.java
@@ -101,5 +101,4 @@ public class ApplicationStatePerDomainServiceImpl implements ApplicationStatePer
     public boolean validateAppConfigurationAgainstState(AppConfigurationView appConfig, ApplicationStatePerDomain appState) {
         return appConfig.getStorageSpace() == null || appConfig.getStorageSpace() <= appState.getPvStorageSizeLimit();
     }
-
 }
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceImpl.java
index 0c4cd891b..32c7e7598 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceImpl.java
@@ -6,6 +6,7 @@ import net.geant.nmaas.portal.api.domain.ApplicationStatePerDomainView;
 import net.geant.nmaas.portal.api.domain.DomainGroupView;
 import net.geant.nmaas.portal.api.exception.MissingElementException;
 import net.geant.nmaas.portal.api.exception.ProcessingException;
+import net.geant.nmaas.portal.persistent.entity.ApplicationBase;
 import net.geant.nmaas.portal.persistent.entity.ApplicationStatePerDomain;
 import net.geant.nmaas.portal.persistent.entity.Domain;
 import net.geant.nmaas.portal.persistent.entity.DomainGroup;
@@ -136,4 +137,10 @@ public class DomainGroupServiceImpl implements DomainGroupService {
         }
     }
 
+    public void deleteAppBaseFromAllAppState(ApplicationBase base) {
+        domainGroupRepository.findAll().forEach(d -> {
+          d.getApplicationStatePerDomain().removeIf(state -> state.getApplicationBase().equals(base));
+        });
+    }
+
 }
\ No newline at end of file
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/DomainServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/DomainServiceImpl.java
index 7f4ff1aff..0caa03568 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/DomainServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/DomainServiceImpl.java
@@ -536,8 +536,8 @@ public class DomainServiceImpl implements DomainService {
     public void removeAppBaseFromAllDomains(ApplicationBase base) {
         getDomains().forEach(domain -> {
             removeFromDomain(base, domain);
-
         });
+        domainGroupService.deleteAppBaseFromAllAppState(base);
     }
 
     private void removeFromDomain(ApplicationBase base, Domain domain) {
-- 
GitLab


From 98d6384e1c4373247dd884e9f8bcac1d4e166bd4 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Tue, 24 Sep 2024 12:41:07 +0200
Subject: [PATCH 20/50] delete appbase from appstateperdomain

---
 .../portal/service/impl/DomainServiceTest.java | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java
index d3c645f89..e9e2e9922 100644
--- a/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java
+++ b/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java
@@ -473,4 +473,22 @@ public class DomainServiceTest {
         assertEquals(0, domain2.getApplicationStatePerDomain().size());
     }
 
+    @Test
+    void shouldRemoveAppBaseFromAllDomainGroup() {
+        ApplicationBase applicationBase = new ApplicationBase(1L, "appBase");
+        ApplicationStatePerDomain statePerDomain = new ApplicationStatePerDomain(applicationBase);
+        DomainGroup domainGroup = new DomainGroup("test", "test1");
+        DomainGroup domainGroup2 = new DomainGroup("test2", "test2");
+        domainGroup.setApplicationStatePerDomain(new ArrayList<>(List.of(statePerDomain)));
+        domainGroup2.setApplicationStatePerDomain(new ArrayList<>(List.of(statePerDomain)));
+        when(domainGroupRepository.findAll()).thenReturn(List.of(domainGroup, domainGroup2));
+
+        domainGroupService.deleteAppBaseFromAllAppState(applicationBase);
+
+        assertEquals(0, domainGroup.getApplicationStatePerDomain().size());
+        assertEquals(0, domainGroup2.getApplicationStatePerDomain().size());
+
+
+    }
+
 }
-- 
GitLab


From ce39ad083b637f48622904fbc5053bbf4450fde3 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Thu, 26 Sep 2024 11:31:20 +0200
Subject: [PATCH 21/50] update domains group app adding

---
 .../service/impl/ApplicationStatePerDomainServiceImpl.java | 7 +++++++
 .../impl/ApplicationStatePerDomainServiceImplTest.java     | 5 ++++-
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationStatePerDomainServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationStatePerDomainServiceImpl.java
index 297af2ebc..c11f3b1d6 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationStatePerDomainServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationStatePerDomainServiceImpl.java
@@ -9,6 +9,7 @@ import net.geant.nmaas.portal.persistent.entity.ApplicationBase;
 import net.geant.nmaas.portal.persistent.entity.ApplicationStatePerDomain;
 import net.geant.nmaas.portal.persistent.entity.Domain;
 import net.geant.nmaas.portal.persistent.repositories.ApplicationBaseRepository;
+import net.geant.nmaas.portal.persistent.repositories.DomainGroupRepository;
 import net.geant.nmaas.portal.persistent.repositories.DomainRepository;
 import net.geant.nmaas.portal.service.ApplicationStatePerDomainService;
 import org.springframework.stereotype.Service;
@@ -25,6 +26,7 @@ public class ApplicationStatePerDomainServiceImpl implements ApplicationStatePer
 
     private final DomainRepository domainRepository;
     private final ApplicationBaseRepository applicationBaseRepository;
+    private final DomainGroupRepository domainGroupRepository;
 
     @Override
     public List<ApplicationStatePerDomain> generateListOfDefaultApplicationStatesPerDomain() {
@@ -44,6 +46,11 @@ public class ApplicationStatePerDomainServiceImpl implements ApplicationStatePer
         appState.setPvStorageSizeLimit(ApplicationStatePerDomainServiceImpl.DEFAULT_PV_STORAGE_SIZE_LIMIT);
         List<Domain> allDomains = domainRepository.findAll();
         allDomains.forEach(domain -> domain.addApplicationState(appState));
+        //update domains groups - set app to false by default
+        appState.setEnabled(false);
+        domainGroupRepository.findAll().forEach(d -> {
+            d.getApplicationStatePerDomain().add(appState);
+        });
         return domainRepository.saveAll(allDomains);
     }
 
diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/ApplicationStatePerDomainServiceImplTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/ApplicationStatePerDomainServiceImplTest.java
index 2203f1e38..0bfe44f77 100644
--- a/src/test/java/net/geant/nmaas/portal/service/impl/ApplicationStatePerDomainServiceImplTest.java
+++ b/src/test/java/net/geant/nmaas/portal/service/impl/ApplicationStatePerDomainServiceImplTest.java
@@ -5,6 +5,7 @@ import net.geant.nmaas.portal.persistent.entity.ApplicationBase;
 import net.geant.nmaas.portal.persistent.entity.ApplicationStatePerDomain;
 import net.geant.nmaas.portal.persistent.entity.Domain;
 import net.geant.nmaas.portal.persistent.repositories.ApplicationBaseRepository;
+import net.geant.nmaas.portal.persistent.repositories.DomainGroupRepository;
 import net.geant.nmaas.portal.persistent.repositories.DomainRepository;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -28,6 +29,8 @@ public class ApplicationStatePerDomainServiceImplTest {
     private final DomainRepository domains = mock(DomainRepository.class);
     private final ApplicationBaseRepository applications = mock(ApplicationBaseRepository.class);
 
+    private final DomainGroupRepository domainGroupRepository = mock(DomainGroupRepository.class);
+
     private ApplicationStatePerDomainServiceImpl appState;
 
     private final Domain domain1 = mock(Domain.class);
@@ -35,7 +38,7 @@ public class ApplicationStatePerDomainServiceImplTest {
 
     @BeforeEach
     void setup() {
-        appState = new ApplicationStatePerDomainServiceImpl(domains, applications);
+        appState = new ApplicationStatePerDomainServiceImpl(domains, applications, domainGroupRepository);
     }
 
     @Test
-- 
GitLab


From 3cfc22afa3915af95f494e01ff2323de83c64ef0 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Thu, 26 Sep 2024 13:14:08 +0200
Subject: [PATCH 22/50] update handler

---
 .../exceptions/IllegalExceptionHandler.java       | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/main/java/net/geant/nmaas/portal/exceptions/IllegalExceptionHandler.java b/src/main/java/net/geant/nmaas/portal/exceptions/IllegalExceptionHandler.java
index 5c83a2679..983d4b3b9 100644
--- a/src/main/java/net/geant/nmaas/portal/exceptions/IllegalExceptionHandler.java
+++ b/src/main/java/net/geant/nmaas/portal/exceptions/IllegalExceptionHandler.java
@@ -1,20 +1,31 @@
 package net.geant.nmaas.portal.exceptions;
 
+import lombok.extern.slf4j.Slf4j;
 import net.bytebuddy.pool.TypePool;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.ControllerAdvice;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 
 @ControllerAdvice
+@Slf4j
 public class IllegalExceptionHandler {
 
     @ExceptionHandler(IllegalArgumentException.class)
     public ResponseEntity<?> handle(IllegalArgumentException ex) {
-        throw new ObjectAlreadyExistsException(ex.getMessage());
+        log.error("Test throw ? ");
+        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_ACCEPTABLE);
     }
 
     @ExceptionHandler(IllegalStateException.class)
     public ResponseEntity<?> handle(IllegalStateException ex) {
-        throw new ObjectAlreadyExistsException(ex.getMessage());
+        log.error("Test throw 2 ? ");
+        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_ACCEPTABLE);
+    }
+
+    @ExceptionHandler(RuntimeException.class)
+    public ResponseEntity<?> handle(RuntimeException ex) {
+        log.error("Test throw 3 ? ");
+        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_ACCEPTABLE);
     }
 }
-- 
GitLab


From 70c947dd4cc5c82f63441efed95005f496f7ba2d Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Thu, 26 Sep 2024 14:12:15 +0200
Subject: [PATCH 23/50] add caching option for main page request

---
 build.gradle                                                 | 3 +++
 src/main/java/net/geant/nmaas/MainConfig.java                | 2 ++
 .../persistent/repositories/ApplicationBaseRepository.java   | 5 +++++
 .../portal/service/impl/ApplicationBaseServiceImpl.java      | 5 ++---
 4 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/build.gradle b/build.gradle
index 3a0d07daa..bc971ee4c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -95,6 +95,8 @@ dependencies {
     implementation('org.springframework.boot:spring-boot-starter-quartz')
     implementation('org.springframework.boot:spring-boot-starter-log4j2')
     implementation('org.springframework.boot:spring-boot-starter-validation')
+    implementation 'org.springframework.boot:spring-boot-starter-cache'
+
 
     implementation('org.springdoc:springdoc-openapi-ui:1.6.15')
 
@@ -114,6 +116,7 @@ dependencies {
     implementation('com.vdurmont:semver4j:3.1.0')
     implementation('io.micrometer:micrometer-registry-prometheus:1.11.2')
 
+
     // SSH library
     implementation('com.hierynomus:sshj:0.32.0')
 
diff --git a/src/main/java/net/geant/nmaas/MainConfig.java b/src/main/java/net/geant/nmaas/MainConfig.java
index 925bf1973..dfdb09578 100644
--- a/src/main/java/net/geant/nmaas/MainConfig.java
+++ b/src/main/java/net/geant/nmaas/MainConfig.java
@@ -2,6 +2,7 @@ package net.geant.nmaas;
 
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
 import org.springframework.context.event.ApplicationEventMulticaster;
@@ -14,6 +15,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 @SpringBootApplication
 @EnableScheduling
 @EnableAspectJAutoProxy
+@EnableCaching
 public class MainConfig {
 
     public static void main(String[] args) {
diff --git a/src/main/java/net/geant/nmaas/portal/persistent/repositories/ApplicationBaseRepository.java b/src/main/java/net/geant/nmaas/portal/persistent/repositories/ApplicationBaseRepository.java
index f5398bb34..52ea84b06 100644
--- a/src/main/java/net/geant/nmaas/portal/persistent/repositories/ApplicationBaseRepository.java
+++ b/src/main/java/net/geant/nmaas/portal/persistent/repositories/ApplicationBaseRepository.java
@@ -4,6 +4,7 @@ import net.geant.nmaas.portal.api.domain.ApplicationBaseS;
 import net.geant.nmaas.portal.persistent.entity.AppDescription;
 import net.geant.nmaas.portal.persistent.entity.ApplicationBase;
 import net.geant.nmaas.portal.persistent.entity.Tag;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.stereotype.Repository;
@@ -27,6 +28,10 @@ public interface ApplicationBaseRepository extends JpaRepository<ApplicationBase
     @Query(value = "SELECT DISTINCT(ab.*) FROM application_base ab JOIN application_base_versions abv on abv.application_base_id = ab.id JOIN application_version av ON av.id = abv.versions_id WHERE av.state = 'ACTIVE'", nativeQuery = true)
     List<ApplicationBaseS> findAllSmall();
 
+    @Cacheable("applicationBaseS")
+    @Query("SELECT ab FROM ApplicationBase ab JOIN Application a on a.name = ab.name WHERE a.state = 'ACTIVE'")
+    List<ApplicationBaseS> findAllSmall2();
+
     @Query("SELECT ab.tags FROM ApplicationBase ab WHERE ab.id =?1")
     List<Tag> findAllBaseTag(Long baseId);
 
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java
index d752b8963..82743ac7c 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java
@@ -136,13 +136,12 @@ public class ApplicationBaseServiceImpl implements ApplicationBaseService {
     public List<ApplicationBaseViewS> findAllActiveAppsSmall() {
         log.debug("Loading information about all applications");
         LocalDateTime beginning = LocalDateTime.now();
-        List<ApplicationBaseS> allSmall = appBaseRepository.findAllSmall();
+        List<ApplicationBaseS> allSmall = appBaseRepository.findAllSmall2();
         LocalDateTime end = LocalDateTime.now();
         log.debug("Loaded base data from db in {}ms", end.toInstant(ZoneOffset.UTC).toEpochMilli() - beginning.toInstant(ZoneOffset.UTC).toEpochMilli());
         List<ApplicationBaseViewS> result = allSmall.stream()
                 .map(app -> modelMapper.map(app, ApplicationBaseViewS.class))
-                .peek(app -> app.setDescriptions(List.of(modelMapper.map(appBaseRepository.findAllBaseDescription(app.getId()), AppDescriptionView[].class))))
-                .peek(app -> app.setTags(Set.of(modelMapper.map(appBaseRepository.findAllBaseTag(app.getId()), TagView[].class))))
+
                 .collect(Collectors.toList());
         LocalDateTime finish = LocalDateTime.now();
         log.debug("Complete data is ready after next {}ms", finish.toInstant(ZoneOffset.UTC).toEpochMilli() - end.toInstant(ZoneOffset.UTC).toEpochMilli());
-- 
GitLab


From 7e01d857847adece8834178b33b26b3e7fa05bd3 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Thu, 26 Sep 2024 14:13:26 +0200
Subject: [PATCH 24/50] change query names

---
 .../persistent/repositories/ApplicationBaseRepository.java   | 5 +----
 .../portal/service/impl/ApplicationBaseServiceImpl.java      | 3 +--
 2 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/main/java/net/geant/nmaas/portal/persistent/repositories/ApplicationBaseRepository.java b/src/main/java/net/geant/nmaas/portal/persistent/repositories/ApplicationBaseRepository.java
index 52ea84b06..77ef01da5 100644
--- a/src/main/java/net/geant/nmaas/portal/persistent/repositories/ApplicationBaseRepository.java
+++ b/src/main/java/net/geant/nmaas/portal/persistent/repositories/ApplicationBaseRepository.java
@@ -25,12 +25,9 @@ public interface ApplicationBaseRepository extends JpaRepository<ApplicationBase
     @Query("SELECT COUNT(DISTINCT ab.name) FROM ApplicationBase ab JOIN Application a on a.name = ab.name WHERE a.state = 'ACTIVE'")
     long countAllActive();
 
-    @Query(value = "SELECT DISTINCT(ab.*) FROM application_base ab JOIN application_base_versions abv on abv.application_base_id = ab.id JOIN application_version av ON av.id = abv.versions_id WHERE av.state = 'ACTIVE'", nativeQuery = true)
-    List<ApplicationBaseS> findAllSmall();
-
     @Cacheable("applicationBaseS")
     @Query("SELECT ab FROM ApplicationBase ab JOIN Application a on a.name = ab.name WHERE a.state = 'ACTIVE'")
-    List<ApplicationBaseS> findAllSmall2();
+    List<ApplicationBaseS> findAllSmall();
 
     @Query("SELECT ab.tags FROM ApplicationBase ab WHERE ab.id =?1")
     List<Tag> findAllBaseTag(Long baseId);
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java
index 82743ac7c..d2b26b5b9 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java
@@ -136,12 +136,11 @@ public class ApplicationBaseServiceImpl implements ApplicationBaseService {
     public List<ApplicationBaseViewS> findAllActiveAppsSmall() {
         log.debug("Loading information about all applications");
         LocalDateTime beginning = LocalDateTime.now();
-        List<ApplicationBaseS> allSmall = appBaseRepository.findAllSmall2();
+        List<ApplicationBaseS> allSmall = appBaseRepository.findAllSmall();
         LocalDateTime end = LocalDateTime.now();
         log.debug("Loaded base data from db in {}ms", end.toInstant(ZoneOffset.UTC).toEpochMilli() - beginning.toInstant(ZoneOffset.UTC).toEpochMilli());
         List<ApplicationBaseViewS> result = allSmall.stream()
                 .map(app -> modelMapper.map(app, ApplicationBaseViewS.class))
-
                 .collect(Collectors.toList());
         LocalDateTime finish = LocalDateTime.now();
         log.debug("Complete data is ready after next {}ms", finish.toInstant(ZoneOffset.UTC).toEpochMilli() - end.toInstant(ZoneOffset.UTC).toEpochMilli());
-- 
GitLab


From ba554c38ebd111254af87fed06c2e2aea9425f9d Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Fri, 27 Sep 2024 11:26:56 +0200
Subject: [PATCH 25/50] update query and test

---
 .../api/configuration/TestCacheConfig.java    | 15 +++++++++++++
 .../market/ApplicationControllerIntTest.java  | 22 +++++++++++--------
 .../resources/application.properties          |  2 ++
 .../ApplicationBaseRepository.java            |  2 +-
 .../impl/ApplicationBaseServiceImpl.java      |  7 ++++++
 .../service/impl/ApplicationServiceImpl.java  |  6 +++++
 6 files changed, 44 insertions(+), 10 deletions(-)
 create mode 100644 src/integrationTest/java/net/geant/nmaas/portal/api/configuration/TestCacheConfig.java

diff --git a/src/integrationTest/java/net/geant/nmaas/portal/api/configuration/TestCacheConfig.java b/src/integrationTest/java/net/geant/nmaas/portal/api/configuration/TestCacheConfig.java
new file mode 100644
index 000000000..fd4038056
--- /dev/null
+++ b/src/integrationTest/java/net/geant/nmaas/portal/api/configuration/TestCacheConfig.java
@@ -0,0 +1,15 @@
+package net.geant.nmaas.portal.api.configuration;
+
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.support.NoOpCacheManager;
+import org.springframework.context.annotation.Bean;
+
+@TestConfiguration
+public class TestCacheConfig {
+
+    @Bean
+    public CacheManager cacheManager() {
+        return new NoOpCacheManager();
+    }
+}
diff --git a/src/integrationTest/java/net/geant/nmaas/portal/api/market/ApplicationControllerIntTest.java b/src/integrationTest/java/net/geant/nmaas/portal/api/market/ApplicationControllerIntTest.java
index f2f9b6b7f..2610b3835 100644
--- a/src/integrationTest/java/net/geant/nmaas/portal/api/market/ApplicationControllerIntTest.java
+++ b/src/integrationTest/java/net/geant/nmaas/portal/api/market/ApplicationControllerIntTest.java
@@ -15,6 +15,7 @@ import net.geant.nmaas.orchestration.entities.AppAccessMethod;
 import net.geant.nmaas.orchestration.entities.AppDeploymentSpec;
 import net.geant.nmaas.orchestration.entities.AppStorageVolume;
 import net.geant.nmaas.portal.api.BaseControllerTestSetup;
+import net.geant.nmaas.portal.api.configuration.TestCacheConfig;
 import net.geant.nmaas.portal.api.domain.AppAccessMethodView;
 import net.geant.nmaas.portal.api.domain.AppConfigurationSpecView;
 import net.geant.nmaas.portal.api.domain.AppDeploymentSpecView;
@@ -44,6 +45,8 @@ import org.junit.jupiter.api.Test;
 import org.modelmapper.ModelMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.context.annotation.Import;
 import org.springframework.http.MediaType;
 import org.springframework.test.web.servlet.MvcResult;
 
@@ -65,8 +68,9 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
-@SpringBootTest
+@SpringBootTest(properties = "spring.cache.type=none")
 @Log4j2
+@Import(TestCacheConfig.class)
 class ApplicationControllerIntTest extends BaseControllerTestSetup {
 
     @Autowired
@@ -104,13 +108,13 @@ class ApplicationControllerIntTest extends BaseControllerTestSetup {
         this.testApp1 = this.applicationService.create(getDefaultApplication(APP_1_NAME, "1.1.0", ApplicationState.ACTIVE));
         this.testApp1Base.getVersions().addAll(
                 List.of(
-                    new ApplicationVersion(this.testApp1.getVersion(), this.testApp1.getState(), this.testApp1.getId()),
-                    new ApplicationVersion("1.1.1",
-                            ApplicationState.ACTIVE,
-                            this.applicationService.create(getDefaultApplication(APP_1_NAME, "1.1.1", ApplicationState.ACTIVE)).getId()),
-                    new ApplicationVersion("1.1.2",
-                            ApplicationState.DISABLED,
-                            this.applicationService.create(getDefaultApplication(APP_1_NAME, "1.1.2", ApplicationState.DISABLED)).getId())
+                        new ApplicationVersion(this.testApp1.getVersion(), this.testApp1.getState(), this.testApp1.getId()),
+                        new ApplicationVersion("1.1.1",
+                                ApplicationState.ACTIVE,
+                                this.applicationService.create(getDefaultApplication(APP_1_NAME, "1.1.1", ApplicationState.ACTIVE)).getId()),
+                        new ApplicationVersion("1.1.2",
+                                ApplicationState.DISABLED,
+                                this.applicationService.create(getDefaultApplication(APP_1_NAME, "1.1.2", ApplicationState.DISABLED)).getId())
                 )
         );
         this.testApp1Base = this.applicationBaseService.update(this.testApp1Base);
@@ -130,8 +134,8 @@ class ApplicationControllerIntTest extends BaseControllerTestSetup {
     }
 
     @Test
+    @CacheEvict(value = "applicationBaseS", allEntries = true)
     void shouldGetActiveApplications() throws Exception {
-        log.debug("Test = {} {}", this.applicationBaseRepository.findAll().size(), this.applicationBaseRepository.findAllSmall().size());
         MvcResult result = mvc.perform(get("/api/apps/base")
                         .header("Authorization", "Bearer " + getValidTokenForUser(UsersHelper.ADMIN))
                         .accept(MediaType.APPLICATION_JSON))
diff --git a/src/integrationTest/resources/application.properties b/src/integrationTest/resources/application.properties
index 5164bcec6..f4df103d0 100644
--- a/src/integrationTest/resources/application.properties
+++ b/src/integrationTest/resources/application.properties
@@ -159,3 +159,5 @@ portal.config.sendAppInstanceFailureEmails=false
 portal.config.showDomainRegistrationSelector=true
 # string - list of emails with ':' as a separator, e.g., admin1@nmaas.eu;admin2@nmaas.eu
 portal.config.appInstanceFailureEmailList=admin@nmaas.eu
+
+spring.cache.type=none
diff --git a/src/main/java/net/geant/nmaas/portal/persistent/repositories/ApplicationBaseRepository.java b/src/main/java/net/geant/nmaas/portal/persistent/repositories/ApplicationBaseRepository.java
index 77ef01da5..b4b89368b 100644
--- a/src/main/java/net/geant/nmaas/portal/persistent/repositories/ApplicationBaseRepository.java
+++ b/src/main/java/net/geant/nmaas/portal/persistent/repositories/ApplicationBaseRepository.java
@@ -26,7 +26,7 @@ public interface ApplicationBaseRepository extends JpaRepository<ApplicationBase
     long countAllActive();
 
     @Cacheable("applicationBaseS")
-    @Query("SELECT ab FROM ApplicationBase ab JOIN Application a on a.name = ab.name WHERE a.state = 'ACTIVE'")
+    @Query("SELECT DISTINCT ab FROM ApplicationBase ab JOIN Application a on a.name = ab.name WHERE a.state = 'ACTIVE'")
     List<ApplicationBaseS> findAllSmall();
 
     @Query("SELECT ab.tags FROM ApplicationBase ab WHERE ab.id =?1")
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java
index d2b26b5b9..c0b95f5da 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java
@@ -21,6 +21,8 @@ import net.geant.nmaas.portal.service.DomainService;
 import org.apache.commons.lang3.StringUtils;
 import org.modelmapper.ModelMapper;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.CachePut;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Service;
 
@@ -52,6 +54,7 @@ public class ApplicationBaseServiceImpl implements ApplicationBaseService {
 
     @Override
     @Transactional
+    @CachePut("applicationBaseS")
     public ApplicationBase create(ApplicationBase applicationBase) {
         if (applicationBase.getId() != null) {
             log.error("Cannot add ApplicationBase - id not null");
@@ -78,6 +81,7 @@ public class ApplicationBaseServiceImpl implements ApplicationBaseService {
 
     @Override
     @Transactional
+    @CachePut("applicationBaseS")
     public ApplicationBase update(ApplicationBase applicationBase) {
         if (applicationBase.getId() == null) {
             throw new ProcessingException("Updated entity id must not be null");
@@ -88,6 +92,7 @@ public class ApplicationBaseServiceImpl implements ApplicationBaseService {
 
     @Override
     @Transactional
+    @CachePut("applicationBaseS")
     public ApplicationBase updateOwner(Long id, String owner) {
         if (id == null) {
             throw new ProcessingException("Updated entity id must not be null");
@@ -104,6 +109,7 @@ public class ApplicationBaseServiceImpl implements ApplicationBaseService {
     }
 
     @Override
+    @CacheEvict(value = "applicationBaseS", allEntries = true)
     public void updateApplicationVersionState(String name, String version, ApplicationState state) {
         ApplicationBase appBase = findByName(name.contains(DELETED_MARKER) ? name.substring(0, name.indexOf(DELETED_MARKER)) : name);
         appBase.getVersions().stream()
@@ -170,6 +176,7 @@ public class ApplicationBaseServiceImpl implements ApplicationBaseService {
     }
 
     @Override
+    @CacheEvict(value = "applicationBaseS", allEntries = true)
     public void deleteAppBase(ApplicationBase base) {
         base.setName(base.getName() + DELETED_MARKER + OffsetDateTime.now());
         appBaseRepository.save(base);
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationServiceImpl.java
index 789cdb3d2..0751c87a6 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationServiceImpl.java
@@ -13,6 +13,8 @@ import net.geant.nmaas.portal.persistent.entity.Application;
 import net.geant.nmaas.portal.persistent.entity.ApplicationState;
 import net.geant.nmaas.portal.persistent.repositories.ApplicationRepository;
 import net.geant.nmaas.portal.service.ApplicationService;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.CachePut;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -42,6 +44,7 @@ public class ApplicationServiceImpl implements ApplicationService {
 
 	@Override
 	@Transactional
+	@CachePut("applicationBaseS")
 	public Application update(Application application) {
 		checkApp(application);
 		Application saved = applicationRepository.save(application);
@@ -66,6 +69,7 @@ public class ApplicationServiceImpl implements ApplicationService {
 	}
 
 	@Override
+	@CachePut("applicationBaseS")
 	public Application create(Application application) {
 		if(application.getId() != null) {
 			throw new ProcessingException("While creating id must be null");
@@ -77,6 +81,7 @@ public class ApplicationServiceImpl implements ApplicationService {
 	}
 
 	@Override
+	@CacheEvict(value = "applicationBaseS")
 	public void delete(Long id) {
 		checkParam(id);
 		applicationRepository.findById(id).ifPresent(app -> {
@@ -123,6 +128,7 @@ public class ApplicationServiceImpl implements ApplicationService {
 	}
 
 	@Override
+	@CacheEvict(value = "applicationBaseS", allEntries = true)
 	public void changeApplicationState(Application app, ApplicationState state) {
 		if(!app.getState().isChangeAllowed(state)) {
 			throw new IllegalStateException("Application state transition from " + app.getState() + " to " + state + " is not allowed.");
-- 
GitLab


From fe7084d7791352e7c2bf4dbb10a3505e65ea57f2 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Tue, 1 Oct 2024 10:41:23 +0200
Subject: [PATCH 26/50] Add helmrepoupdate on ApplicationListUpdateEvent

---
 .../kubernetes/HelmRepoUpdateListener.java                | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateListener.java b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateListener.java
index c827d8f5c..d3f980e13 100644
--- a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateListener.java
+++ b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateListener.java
@@ -3,6 +3,7 @@ package net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes;
 import lombok.RequiredArgsConstructor;
 import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.components.helm.HelmKServiceManager;
 import net.geant.nmaas.portal.events.ApplicationActivatedEvent;
+import net.geant.nmaas.portal.events.ApplicationListUpdatedEvent;
 import net.geant.nmaas.utils.logging.LogLevel;
 import net.geant.nmaas.utils.logging.Loggable;
 import org.springframework.context.ApplicationEvent;
@@ -22,4 +23,11 @@ public class HelmRepoUpdateListener {
         return null;
     }
 
+    @EventListener
+    @Loggable(LogLevel.INFO)
+    public ApplicationEvent trigger(ApplicationListUpdatedEvent event) {
+        helmKServiceManager.updateHelmRepo();
+        return null;
+    }
+
 }
-- 
GitLab


From 1e241c04dd6917dd16e4b93144bc790dab07252c Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Tue, 1 Oct 2024 10:57:10 +0200
Subject: [PATCH 27/50] Add helmrepoupdate on ApplicationListUpdateEvent

---
 .../kubernetes/HelmRepoUpdateListener.java                 | 7 -------
 .../components/helm/HelmChartUpdateListener.java           | 1 +
 2 files changed, 1 insertion(+), 7 deletions(-)

diff --git a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateListener.java b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateListener.java
index d3f980e13..734014f1e 100644
--- a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateListener.java
+++ b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateListener.java
@@ -23,11 +23,4 @@ public class HelmRepoUpdateListener {
         return null;
     }
 
-    @EventListener
-    @Loggable(LogLevel.INFO)
-    public ApplicationEvent trigger(ApplicationListUpdatedEvent event) {
-        helmKServiceManager.updateHelmRepo();
-        return null;
-    }
-
 }
diff --git a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmChartUpdateListener.java b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmChartUpdateListener.java
index e5fc014ca..0e2825320 100644
--- a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmChartUpdateListener.java
+++ b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmChartUpdateListener.java
@@ -35,6 +35,7 @@ public class HelmChartUpdateListener {
                 helmCommandExecutor.executeHelmRepoAddCommand(repoName, repoUrl);
             }
         }
+        helmCommandExecutor.executeHelmRepoUpdateCommand();
         return null;
     }
 
-- 
GitLab


From 259575eb49f0a66fb8ad3053d07ec6bfbded84f9 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Tue, 1 Oct 2024 11:28:34 +0200
Subject: [PATCH 28/50] delete import

---
 .../kubernetes/HelmRepoUpdateListener.java                       | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateListener.java b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateListener.java
index 734014f1e..c827d8f5c 100644
--- a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateListener.java
+++ b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateListener.java
@@ -3,7 +3,6 @@ package net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes;
 import lombok.RequiredArgsConstructor;
 import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.components.helm.HelmKServiceManager;
 import net.geant.nmaas.portal.events.ApplicationActivatedEvent;
-import net.geant.nmaas.portal.events.ApplicationListUpdatedEvent;
 import net.geant.nmaas.utils.logging.LogLevel;
 import net.geant.nmaas.utils.logging.Loggable;
 import org.springframework.context.ApplicationEvent;
-- 
GitLab


From dfa238fd826c46820b8004c02562e5a40264ed80 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Thu, 3 Oct 2024 09:32:52 +0200
Subject: [PATCH 29/50] Update translation

---
 src/test/shell/data/i18n/de.json | 7 ++++---
 src/test/shell/data/i18n/en.json | 7 ++++---
 src/test/shell/data/i18n/fr.json | 7 ++++---
 src/test/shell/data/i18n/pl.json | 5 +++--
 4 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/src/test/shell/data/i18n/de.json b/src/test/shell/data/i18n/de.json
index 0f09b429b..a3df58252 100644
--- a/src/test/shell/data/i18n/de.json
+++ b/src/test/shell/data/i18n/de.json
@@ -285,9 +285,10 @@
     },
     "SORT_MODE": {
       "NONE": "None",
-      "NAME": "By name",
-      "RATING": "Top rated",
-      "POPULAR": "Most popular"
+      "NAME": "Name",
+      "RATING": "Rating",
+      "POPULAR": "Popularity",
+      "DATE" : "Date added"
     }
   },
   "MONITOR": {
diff --git a/src/test/shell/data/i18n/en.json b/src/test/shell/data/i18n/en.json
index 8483d4366..e4b40cf3a 100644
--- a/src/test/shell/data/i18n/en.json
+++ b/src/test/shell/data/i18n/en.json
@@ -285,9 +285,10 @@
     },
     "SORT_MODE": {
       "NONE": "None",
-      "NAME": "By name",
-      "RATING": "Top rated",
-      "POPULAR": "Most popular"
+      "NAME": "Name",
+      "RATING": "Rating",
+      "POPULAR": "Popularity",
+      "DATE" : "Date added"
     }
   },
   "MONITOR": {
diff --git a/src/test/shell/data/i18n/fr.json b/src/test/shell/data/i18n/fr.json
index 44607c6f5..24affb2c2 100644
--- a/src/test/shell/data/i18n/fr.json
+++ b/src/test/shell/data/i18n/fr.json
@@ -285,9 +285,10 @@
     },
     "SORT_MODE": {
       "NONE": "None",
-      "NAME": "By name",
-      "RATING": "Top rated",
-      "POPULAR": "Most popular"
+      "NAME": "Name",
+      "RATING": "Rating",
+      "POPULAR": "Popularity",
+      "DATE" : "Date added"
     }
   },
   "MONITOR": {
diff --git a/src/test/shell/data/i18n/pl.json b/src/test/shell/data/i18n/pl.json
index 66b386afb..151d81c3e 100644
--- a/src/test/shell/data/i18n/pl.json
+++ b/src/test/shell/data/i18n/pl.json
@@ -286,8 +286,9 @@
     "SORT_MODE": {
       "NONE": "Brak",
       "NAME": "Nazwa",
-      "RATING": "Najlepiej oceniane",
-      "POPULAR": "Najbardziej popularne"
+      "RATING": "Ocena",
+      "POPULAR": "Popularność",
+      "DATE" : "Data dodania"
     }
   },
   "MONITOR": {
-- 
GitLab


From ecdb42c68b25cfd040f02342a72e3ffab94feece Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Tue, 8 Oct 2024 11:37:03 +0200
Subject: [PATCH 30/50] New SSOuser set firstname and lastname

---
 .../net/geant/nmaas/portal/service/impl/UserServiceImpl.java    | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/UserServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/UserServiceImpl.java
index ff1163577..a4b8e58a3 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/UserServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/UserServiceImpl.java
@@ -188,6 +188,8 @@ public class UserServiceImpl implements UserService {
 		if (configurationManager.getConfiguration().isBulkDomainsAllowForSsoAccounts()) {
 			if (csvUser.getSsoEnabled() != null && csvUser.getSsoEnabled()) {
 				newUser.setSamlToken(csvUser.getEmail());
+				newUser.setFirstname(csvUser.getAdminUserName());
+				newUser.setLastname(csvUser.getAdminUserName());
 				if(sendMails) this.sendMail(newUser, MailType.NEW_BULK_SSO_LOGIN);
 			}else {
 				if(sendMails) this.sendMail(newUser, MailType.NEW_BULK_LOGIN);
-- 
GitLab


From a6c1056eb051826a3cd66d0685de3206beafbe67 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Tue, 8 Oct 2024 11:37:17 +0200
Subject: [PATCH 31/50] add try catch for setting the creator

---
 .../geant/nmaas/portal/api/bulk/BulkController.java  | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

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 0ae731c41..2b2113fcf 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
@@ -195,14 +195,22 @@ public class BulkController {
 
     private <T extends BulkDeploymentViewS> T mapToView(BulkDeployment bulk, Class<T> viewType) {
         T bulkView = modelMapper.map(bulk, viewType);
-        bulkView.setCreator(getUserView(bulk.getCreatorId()));
+        try {
+            bulkView.setCreator(getUserView(bulk.getCreatorId()));
+        } catch (Exception ex) {
+            log.error("Can not find creator for {} - creatorId:  {}", bulk.getId(), bulk.getCreatorId());
+        }
         mapDetails(bulk, bulkView);
         return bulkView;
     }
 
     private BulkDeploymentView mapToView(BulkDeployment deployment) {
         BulkDeploymentView bulkView = modelMapper.map(deployment, BulkDeploymentView.class);
-        bulkView.setCreator(getUserView(deployment.getCreatorId()));
+        try {
+            bulkView.setCreator(getUserView(deployment.getCreatorId()));
+        } catch (Exception ex) {
+            log.error("Can not find creator for {} - creatorId:  {}", deployment.getId(), deployment.getCreatorId());
+        }
         mapDetails(deployment, bulkView);
         return bulkView;
 
-- 
GitLab


From fd0d9273dad79d0a6f7949b2d0cd628c0aff9303 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Tue, 8 Oct 2024 11:44:04 +0200
Subject: [PATCH 32/50] add try catch for setting the creator

---
 .../java/net/geant/nmaas/portal/api/bulk/BulkController.java    | 2 ++
 1 file changed, 2 insertions(+)

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 2b2113fcf..dc3629c55 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
@@ -199,6 +199,7 @@ public class BulkController {
             bulkView.setCreator(getUserView(bulk.getCreatorId()));
         } catch (Exception ex) {
             log.error("Can not find creator for {} - creatorId:  {}", bulk.getId(), bulk.getCreatorId());
+            return null;
         }
         mapDetails(bulk, bulkView);
         return bulkView;
@@ -208,6 +209,7 @@ public class BulkController {
         BulkDeploymentView bulkView = modelMapper.map(deployment, BulkDeploymentView.class);
         try {
             bulkView.setCreator(getUserView(deployment.getCreatorId()));
+            return null;
         } catch (Exception ex) {
             log.error("Can not find creator for {} - creatorId:  {}", deployment.getId(), deployment.getCreatorId());
         }
-- 
GitLab


From e6529122a34d736bd4322277c2329ab7fa7b1bdc Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Tue, 8 Oct 2024 12:35:47 +0200
Subject: [PATCH 33/50] add user manager delete before user database delete

---
 .../net/geant/nmaas/portal/service/DomainGroupService.java  | 2 ++
 .../nmaas/portal/service/impl/DomainGroupServiceImpl.java   | 6 ++++++
 .../geant/nmaas/portal/service/impl/UserServiceImpl.java    | 5 +++++
 3 files changed, 13 insertions(+)

diff --git a/src/main/java/net/geant/nmaas/portal/service/DomainGroupService.java b/src/main/java/net/geant/nmaas/portal/service/DomainGroupService.java
index a95e5c3c1..1bf54f5fc 100644
--- a/src/main/java/net/geant/nmaas/portal/service/DomainGroupService.java
+++ b/src/main/java/net/geant/nmaas/portal/service/DomainGroupService.java
@@ -3,6 +3,7 @@ package net.geant.nmaas.portal.service;
 import net.geant.nmaas.portal.api.domain.DomainGroupView;
 import net.geant.nmaas.portal.persistent.entity.ApplicationBase;
 import net.geant.nmaas.portal.persistent.entity.Domain;
+import net.geant.nmaas.portal.persistent.entity.User;
 
 import java.util.List;
 
@@ -26,4 +27,5 @@ public interface DomainGroupService {
 
     void deleteAppBaseFromAllAppState(ApplicationBase base);
 
+    void deleteUserFromAllDomainsGroups(User user);
 }
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceImpl.java
index 32c7e7598..3dbc1baf9 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceImpl.java
@@ -143,4 +143,10 @@ public class DomainGroupServiceImpl implements DomainGroupService {
         });
     }
 
+    @Override
+    public void deleteUserFromAllDomainsGroups(User user) {
+        domainGroupRepository.findAll().forEach(d -> {
+            d.getManagers().remove(user);
+        });
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/UserServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/UserServiceImpl.java
index a4b8e58a3..71f881256 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/UserServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/UserServiceImpl.java
@@ -17,12 +17,14 @@ import net.geant.nmaas.portal.api.exception.ProcessingException;
 import net.geant.nmaas.portal.api.exception.SignupException;
 import net.geant.nmaas.portal.api.security.JWTTokenService;
 import net.geant.nmaas.portal.persistent.entity.Domain;
+import net.geant.nmaas.portal.persistent.entity.DomainGroup;
 import net.geant.nmaas.portal.persistent.entity.Role;
 import net.geant.nmaas.portal.persistent.entity.User;
 import net.geant.nmaas.portal.persistent.entity.UserRole;
 import net.geant.nmaas.portal.persistent.repositories.UserRepository;
 import net.geant.nmaas.portal.persistent.repositories.UserRoleRepository;
 import net.geant.nmaas.portal.service.ConfigurationManager;
+import net.geant.nmaas.portal.service.DomainGroupService;
 import net.geant.nmaas.portal.service.UserService;
 import org.apache.commons.lang3.RandomStringUtils;
 import org.modelmapper.ModelMapper;
@@ -58,6 +60,7 @@ public class UserServiceImpl implements UserService {
 
 	private final ApplicationEventPublisher eventPublisher;
 	private final JWTTokenService jwtTokenService;
+	private final DomainGroupService domainGroupService;
 
 	@Value("${portal.address}")
 	@Setter
@@ -215,12 +218,14 @@ public class UserServiceImpl implements UserService {
 	public void delete(User user) {
 		checkParam(user);
 		checkParam(user.getId());
+		domainGroupService.deleteUserFromAllDomainsGroups(user);
 		userRepository.delete(user);
 	}
 
 	@Override
 	public void deleteById(Long userId) {
 		checkParam(userId);
+		domainGroupService.deleteUserFromAllDomainsGroups(userRepository.getReferenceById(userId));
 		userRepository.deleteById(userId);
 	}
 
-- 
GitLab


From 3602da60731906634b9977e47a6d8869ab962eb3 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Tue, 8 Oct 2024 12:38:59 +0200
Subject: [PATCH 34/50] update test

---
 .../nmaas/portal/service/impl/UserServiceImplTest.java      | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/UserServiceImplTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/UserServiceImplTest.java
index 9ee91a674..81f94466b 100644
--- a/src/test/java/net/geant/nmaas/portal/service/impl/UserServiceImplTest.java
+++ b/src/test/java/net/geant/nmaas/portal/service/impl/UserServiceImplTest.java
@@ -14,6 +14,7 @@ import net.geant.nmaas.portal.persistent.entity.UserRole;
 import net.geant.nmaas.portal.persistent.repositories.UserRepository;
 import net.geant.nmaas.portal.persistent.repositories.UserRoleRepository;
 import net.geant.nmaas.portal.service.ConfigurationManager;
+import net.geant.nmaas.portal.service.DomainGroupService;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -57,11 +58,14 @@ public class UserServiceImplTest {
     @Mock
     JWTTokenService jwtTokenService;
 
+    @Mock
+    DomainGroupService domainGroupService;
+
     UserServiceImpl userService;
 
     @BeforeEach
     void setup() {
-        userService = new UserServiceImpl(userRepository, userRoleRepository, new BCryptPasswordEncoder(), configurationManager, new ModelMapper(), eventPublisher, jwtTokenService);
+        userService = new UserServiceImpl(userRepository, userRoleRepository, new BCryptPasswordEncoder(), configurationManager, new ModelMapper(), eventPublisher, jwtTokenService,domainGroupService);
         userService.setPortalAddress("portalAddress");
     }
 
-- 
GitLab


From accca020c1846eacf472eba4a743a5b72d673ece Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Tue, 8 Oct 2024 12:39:57 +0200
Subject: [PATCH 35/50] change name assign

---
 .../net/geant/nmaas/portal/service/impl/UserServiceImpl.java  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/UserServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/UserServiceImpl.java
index 71f881256..eca382c54 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/UserServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/UserServiceImpl.java
@@ -183,6 +183,8 @@ public class UserServiceImpl implements UserService {
 		newUser.setSelectedLanguage(configurationManager.getConfiguration().getDefaultLanguage());
 		newUser.setTermsOfUseAccepted(true);
 		newUser.setPrivacyPolicyAccepted(true);
+		newUser.setFirstname(csvUser.getAdminUserName());
+		newUser.setLastname(csvUser.getAdminUserName());
 		if (domain != null) {
 			newUser.setNewRoles(ImmutableSet.of(new UserRole(newUser, domain, ROLE_DOMAIN_ADMIN)));
 		}
@@ -191,8 +193,6 @@ public class UserServiceImpl implements UserService {
 		if (configurationManager.getConfiguration().isBulkDomainsAllowForSsoAccounts()) {
 			if (csvUser.getSsoEnabled() != null && csvUser.getSsoEnabled()) {
 				newUser.setSamlToken(csvUser.getEmail());
-				newUser.setFirstname(csvUser.getAdminUserName());
-				newUser.setLastname(csvUser.getAdminUserName());
 				if(sendMails) this.sendMail(newUser, MailType.NEW_BULK_SSO_LOGIN);
 			}else {
 				if(sendMails) this.sendMail(newUser, MailType.NEW_BULK_LOGIN);
-- 
GitLab


From 62a8a6ecd152b3fd63b8ce039ecb4d3e2f2fd1d8 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Thu, 10 Oct 2024 12:57:29 +0200
Subject: [PATCH 36/50] Add exception on templates

---
 .../templates/TemplateController.java         |  9 +++++++++
 .../templates/TemplateService.java            | 18 ++++++++++++-----
 .../exceptions/DataConflictException.java     | 20 +++++++++++++++++++
 3 files changed, 42 insertions(+), 5 deletions(-)
 create mode 100644 src/main/java/net/geant/nmaas/portal/exceptions/DataConflictException.java

diff --git a/src/main/java/net/geant/nmaas/notifications/templates/TemplateController.java b/src/main/java/net/geant/nmaas/notifications/templates/TemplateController.java
index eb7360265..9483df6b8 100644
--- a/src/main/java/net/geant/nmaas/notifications/templates/TemplateController.java
+++ b/src/main/java/net/geant/nmaas/notifications/templates/TemplateController.java
@@ -2,9 +2,12 @@ package net.geant.nmaas.notifications.templates;
 
 import lombok.RequiredArgsConstructor;
 import net.geant.nmaas.notifications.templates.api.MailTemplateView;
+import net.geant.nmaas.portal.exceptions.ConfigurationNotFoundException;
+import net.geant.nmaas.portal.exceptions.DataConflictException;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PatchMapping;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -69,4 +72,10 @@ public class TemplateController {
     public void updateHtmlTemplate(@RequestBody MultipartFile file){
         this.templateService.updateHTMLTemplate(file);
     }
+
+    @ExceptionHandler(DataConflictException.class)
+    @ResponseStatus(code = HttpStatus.CONFLICT)
+    public String handleDataConfigException(DataConflictException e){
+        return e.getMessage();
+    }
 }
diff --git a/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java b/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java
index bd5e37907..a94b007b3 100644
--- a/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java
+++ b/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java
@@ -7,6 +7,7 @@ import net.geant.nmaas.notifications.templates.api.MailTemplateView;
 import net.geant.nmaas.notifications.templates.entities.LanguageMailContent;
 import net.geant.nmaas.notifications.templates.entities.MailTemplate;
 import net.geant.nmaas.notifications.templates.repository.MailTemplateRepository;
+import net.geant.nmaas.portal.exceptions.DataConflictException;
 import net.geant.nmaas.portal.persistent.entity.FileInfo;
 import net.geant.nmaas.portal.service.impl.LocalFileStorageService;
 import org.modelmapper.ModelMapper;
@@ -15,6 +16,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.annotation.CheckForNull;
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
@@ -22,7 +24,7 @@ import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
-import static com.google.common.base.Preconditions.checkArgument;
+//import static com.google.common.base.Preconditions.checkArgument;
 
 @Service
 public class TemplateService {
@@ -42,7 +44,7 @@ public class TemplateService {
 
     @Transactional
     public MailTemplateView getMailTemplate(MailType mailType){
-        MailTemplate mailTemplate = repository.findByMailType(mailType).orElseThrow(() -> new IllegalArgumentException("Mail template not found"));
+        MailTemplate mailTemplate = repository.findByMailType(mailType).orElseThrow(() -> new DataConflictException(String.format("Mail template %s not found", mailType.name() )));
         return modelMapper.map(mailTemplate, MailTemplateView.class);
     }
 
@@ -53,13 +55,13 @@ public class TemplateService {
     }
 
     void saveMailTemplate(MailTemplateView mailTemplate) {
-        checkArgument(!repository.existsByMailType(mailTemplate.getMailType()),"Mail template already exists");
+        checkArgument(!repository.existsByMailType(mailTemplate.getMailType()), String.format("Mail template %s already exists", mailTemplate.getMailType().name() ));
         checkArgument(mailTemplate.getTemplates() != null && !mailTemplate.getTemplates().isEmpty(), "Mail template cannot be null or empty");
         repository.save(modelMapper.map(mailTemplate, MailTemplate.class));
     }
 
     void updateMailTemplate(MailTemplateView mailTemplate) {
-        MailTemplate mailTemplateEntity = repository.findByMailType(mailTemplate.getMailType()).orElseThrow(() -> new IllegalArgumentException("Mail template not found"));
+        MailTemplate mailTemplateEntity = repository.findByMailType(mailTemplate.getMailType()).orElseThrow(() -> new DataConflictException(String.format("Mail template %s not found", mailTemplate.getMailType().name() )));
         checkArgument(mailTemplate.getTemplates() != null && !mailTemplate.getTemplates().isEmpty(), "Mail template cannot be null or empty");
         mailTemplateEntity.getTemplates().clear();
         mailTemplateEntity.getTemplates().addAll(mailTemplate.getTemplates().stream().map(template -> modelMapper.map(template, LanguageMailContent.class)).collect(Collectors.toList()));
@@ -90,7 +92,13 @@ public class TemplateService {
         if (template.size() == 1) {
             return template.get(0);
         }
-        throw new IllegalArgumentException(String.format("Exactly one html template supported (actually got %d)", template.size()));
+        throw new DataConflictException(String.format("Exactly one html template supported (actually got %d)", template.size()));
+    }
+
+    private static void checkArgument(boolean expression, String errorMessage) {
+        if (!expression) {
+            throw new DataConflictException(String.valueOf(errorMessage));
+        }
     }
 
 }
diff --git a/src/main/java/net/geant/nmaas/portal/exceptions/DataConflictException.java b/src/main/java/net/geant/nmaas/portal/exceptions/DataConflictException.java
new file mode 100644
index 000000000..e269f78cf
--- /dev/null
+++ b/src/main/java/net/geant/nmaas/portal/exceptions/DataConflictException.java
@@ -0,0 +1,20 @@
+package net.geant.nmaas.portal.exceptions;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class DataConflictException extends RuntimeException {
+
+    private String message;
+    public DataConflictException(String message)
+    {
+        super(message);
+        log.error("test ?????");
+        this.message = message;
+    }
+
+    public String getMsg() {
+        return message;
+    }
+
+}
-- 
GitLab


From 1d98751c1d9b1f3c69c88787d6ca97ac7111b8cf Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Thu, 10 Oct 2024 13:00:56 +0200
Subject: [PATCH 37/50] Add exception for contact types

---
 .../nmaas/notifications/types/FormTypeController.java  |  7 +++++++
 .../notifications/types/service/FormTypeService.java   |  3 ++-
 .../nmaas/portal/exceptions/DataConflictException.java | 10 +---------
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/main/java/net/geant/nmaas/notifications/types/FormTypeController.java b/src/main/java/net/geant/nmaas/notifications/types/FormTypeController.java
index 196e841f2..55172bac6 100644
--- a/src/main/java/net/geant/nmaas/notifications/types/FormTypeController.java
+++ b/src/main/java/net/geant/nmaas/notifications/types/FormTypeController.java
@@ -4,8 +4,10 @@ import lombok.AllArgsConstructor;
 import net.geant.nmaas.notifications.types.model.FormTypeRequest;
 import net.geant.nmaas.notifications.types.model.FormTypeView;
 import net.geant.nmaas.notifications.types.service.FormTypeService;
+import net.geant.nmaas.portal.exceptions.DataConflictException;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PutMapping;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -35,4 +37,9 @@ public class FormTypeController {
         this.service.create(request);
     }
 
+    @ExceptionHandler(DataConflictException.class)
+    @ResponseStatus(code = HttpStatus.CONFLICT)
+    public String handleDataConfigException(DataConflictException e){
+        return e.getMessage();
+    }
 }
diff --git a/src/main/java/net/geant/nmaas/notifications/types/service/FormTypeService.java b/src/main/java/net/geant/nmaas/notifications/types/service/FormTypeService.java
index 06e47911b..21426f5f5 100644
--- a/src/main/java/net/geant/nmaas/notifications/types/service/FormTypeService.java
+++ b/src/main/java/net/geant/nmaas/notifications/types/service/FormTypeService.java
@@ -6,6 +6,7 @@ import net.geant.nmaas.notifications.types.model.FormTypeView;
 import net.geant.nmaas.notifications.types.persistence.entity.FormType;
 import net.geant.nmaas.notifications.types.persistence.repository.FormTypeRepository;
 import net.geant.nmaas.portal.api.exception.ProcessingException;
+import net.geant.nmaas.portal.exceptions.DataConflictException;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
@@ -32,7 +33,7 @@ public class FormTypeService {
         if(!this.typeRepository.existsById(ent.getKeyValue())) {
             this.typeRepository.save(ent);
         } else {
-            throw new ProcessingException("Form type already exists");
+            throw new DataConflictException(String.format("Form type %s already exists", ent.getTemplateName()));
         }
     }
 
diff --git a/src/main/java/net/geant/nmaas/portal/exceptions/DataConflictException.java b/src/main/java/net/geant/nmaas/portal/exceptions/DataConflictException.java
index e269f78cf..aadb9107d 100644
--- a/src/main/java/net/geant/nmaas/portal/exceptions/DataConflictException.java
+++ b/src/main/java/net/geant/nmaas/portal/exceptions/DataConflictException.java
@@ -5,16 +5,8 @@ import lombok.extern.slf4j.Slf4j;
 @Slf4j
 public class DataConflictException extends RuntimeException {
 
-    private String message;
-    public DataConflictException(String message)
-    {
+    public DataConflictException(String message) {
         super(message);
-        log.error("test ?????");
-        this.message = message;
-    }
-
-    public String getMsg() {
-        return message;
     }
 
 }
-- 
GitLab


From 7e3a3f94788e29bf400baeea8698168fd3f0b6e5 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Thu, 10 Oct 2024 14:16:00 +0200
Subject: [PATCH 38/50] Add new logic for translations

---
 .../api/auth/SSOAuthControllerIntTest.java    |  2 +-
 .../i18n/InternationalizationController.java  | 30 +++++++++-----
 .../exceptions/IllegalExceptionHandler.java   | 31 ---------------
 .../service/InternationalizationService.java  |  2 +-
 .../impl/InternationalizationServiceImpl.java | 39 ++++++++++++++++++-
 .../impl/InternationalizationServiceTest.java | 10 ++---
 6 files changed, 65 insertions(+), 49 deletions(-)
 delete mode 100644 src/main/java/net/geant/nmaas/portal/exceptions/IllegalExceptionHandler.java

diff --git a/src/integrationTest/java/net/geant/nmaas/portal/api/auth/SSOAuthControllerIntTest.java b/src/integrationTest/java/net/geant/nmaas/portal/api/auth/SSOAuthControllerIntTest.java
index 46ff082c6..16867a493 100644
--- a/src/integrationTest/java/net/geant/nmaas/portal/api/auth/SSOAuthControllerIntTest.java
+++ b/src/integrationTest/java/net/geant/nmaas/portal/api/auth/SSOAuthControllerIntTest.java
@@ -149,7 +149,7 @@ public class SSOAuthControllerIntTest extends BaseControllerTestSetup {
 
     private void addLanguage(){
         if(!intService.getEnabledLanguages().contains("en")) {
-            intService.addNewLanguage(new InternationalizationView("en", true, "{\"content\":\"content\"}"));
+            intService.addNewLanguage(new InternationalizationView("en", true, "{\"content\":\"content\"}"), false);
         }
     }
 
diff --git a/src/main/java/net/geant/nmaas/portal/api/i18n/InternationalizationController.java b/src/main/java/net/geant/nmaas/portal/api/i18n/InternationalizationController.java
index b15cbed12..2fb0cca6e 100644
--- a/src/main/java/net/geant/nmaas/portal/api/i18n/InternationalizationController.java
+++ b/src/main/java/net/geant/nmaas/portal/api/i18n/InternationalizationController.java
@@ -1,13 +1,23 @@
 package net.geant.nmaas.portal.api.i18n;
 
-import java.util.List;
 import lombok.AllArgsConstructor;
 import net.geant.nmaas.portal.api.i18n.api.InternationalizationBriefView;
 import net.geant.nmaas.portal.api.i18n.api.InternationalizationView;
 import net.geant.nmaas.portal.service.InternationalizationService;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
 
 @RestController
 @AllArgsConstructor
@@ -19,35 +29,37 @@ public class InternationalizationController {
     @PostMapping("/{language}")
     @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN')")
     @ResponseStatus(HttpStatus.ACCEPTED)
-    public void saveLanguageContent(@PathVariable("language") String language, @RequestParam(value = "enabled") boolean enabled, @RequestBody String content) {
-        this.internationalizationService.addNewLanguage(new InternationalizationView(language, enabled, content));
+    public void saveLanguageContent(@PathVariable("language") String language, @RequestParam(value = "enabled") boolean enabled,
+                                    @RequestParam(value = "force", required = false, defaultValue = "false") Boolean force, @RequestBody String content) {
+        boolean isForce = force != null && force;
+        this.internationalizationService.addNewLanguage(new InternationalizationView(language, enabled, content), isForce);
     }
 
     @PatchMapping("/{language}")
     @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_TOOL_MANAGER')")
     @ResponseStatus(HttpStatus.ACCEPTED)
-    public void updateLanguageContent(@PathVariable("language") String language, @RequestBody String content){
+    public void updateLanguageContent(@PathVariable("language") String language, @RequestBody String content) {
         this.internationalizationService.updateLanguage(language, content);
     }
 
     @GetMapping("/all")
     @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_TOOL_MANAGER')")
     @ResponseStatus(HttpStatus.OK)
-    public List<InternationalizationBriefView> getAllSupportedLanguages(){
+    public List<InternationalizationBriefView> getAllSupportedLanguages() {
         return this.internationalizationService.getAllSupportedLanguages();
     }
 
     @GetMapping("/{language}")
     @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN')")
     @ResponseStatus(HttpStatus.OK)
-    public InternationalizationView getLanguage(@PathVariable String language){
+    public InternationalizationView getLanguage(@PathVariable String language) {
         return this.internationalizationService.getLanguage(language);
     }
 
     @PutMapping("/state")
     @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN')")
     @ResponseStatus(HttpStatus.NO_CONTENT)
-    public void changeSupportedLanguageState(@RequestBody InternationalizationBriefView language){
+    public void changeSupportedLanguageState(@RequestBody InternationalizationBriefView language) {
         this.internationalizationService.changeLanguageState(language);
     }
 
@@ -59,7 +71,7 @@ public class InternationalizationController {
 
     @GetMapping("/all/enabled")
     @ResponseStatus(HttpStatus.OK)
-    public List<String> getEnabledLanguages(){
+    public List<String> getEnabledLanguages() {
         return this.internationalizationService.getEnabledLanguages();
     }
 }
diff --git a/src/main/java/net/geant/nmaas/portal/exceptions/IllegalExceptionHandler.java b/src/main/java/net/geant/nmaas/portal/exceptions/IllegalExceptionHandler.java
deleted file mode 100644
index 983d4b3b9..000000000
--- a/src/main/java/net/geant/nmaas/portal/exceptions/IllegalExceptionHandler.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package net.geant.nmaas.portal.exceptions;
-
-import lombok.extern.slf4j.Slf4j;
-import net.bytebuddy.pool.TypePool;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.ControllerAdvice;
-import org.springframework.web.bind.annotation.ExceptionHandler;
-
-@ControllerAdvice
-@Slf4j
-public class IllegalExceptionHandler {
-
-    @ExceptionHandler(IllegalArgumentException.class)
-    public ResponseEntity<?> handle(IllegalArgumentException ex) {
-        log.error("Test throw ? ");
-        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_ACCEPTABLE);
-    }
-
-    @ExceptionHandler(IllegalStateException.class)
-    public ResponseEntity<?> handle(IllegalStateException ex) {
-        log.error("Test throw 2 ? ");
-        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_ACCEPTABLE);
-    }
-
-    @ExceptionHandler(RuntimeException.class)
-    public ResponseEntity<?> handle(RuntimeException ex) {
-        log.error("Test throw 3 ? ");
-        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_ACCEPTABLE);
-    }
-}
diff --git a/src/main/java/net/geant/nmaas/portal/service/InternationalizationService.java b/src/main/java/net/geant/nmaas/portal/service/InternationalizationService.java
index 66a3e9a37..7806750f2 100644
--- a/src/main/java/net/geant/nmaas/portal/service/InternationalizationService.java
+++ b/src/main/java/net/geant/nmaas/portal/service/InternationalizationService.java
@@ -5,7 +5,7 @@ import net.geant.nmaas.portal.api.i18n.api.InternationalizationBriefView;
 import net.geant.nmaas.portal.api.i18n.api.InternationalizationView;
 
 public interface InternationalizationService {
-    void addNewLanguage(InternationalizationView newLanguage);
+    void addNewLanguage(InternationalizationView newLanguage, Boolean force);
     void updateLanguage(String language, String content);
     List<InternationalizationBriefView> getAllSupportedLanguages();
     InternationalizationView getLanguage(String language);
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceImpl.java
index b114ad877..6916a916b 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceImpl.java
@@ -2,8 +2,10 @@ package net.geant.nmaas.portal.service.impl;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import net.geant.nmaas.portal.api.i18n.api.InternationalizationBriefView;
 import net.geant.nmaas.portal.api.i18n.api.InternationalizationView;
+import net.geant.nmaas.portal.exceptions.DataConflictException;
 import net.geant.nmaas.portal.persistent.entity.InternationalizationSimple;
 import net.geant.nmaas.portal.persistent.repositories.InternationalizationSimpleRepository;
 import net.geant.nmaas.portal.service.ConfigurationManager;
@@ -14,11 +16,14 @@ import org.springframework.stereotype.Service;
 
 import javax.transaction.Transactional;
 import java.io.IOException;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 @Service
 @RequiredArgsConstructor
+@Slf4j
 public class InternationalizationServiceImpl implements InternationalizationService {
 
     private final InternationalizationSimpleRepository repository;
@@ -26,9 +31,39 @@ public class InternationalizationServiceImpl implements InternationalizationServ
     private final ModelMapper modelMapper;
 
     @Override
-    public void addNewLanguage(InternationalizationView newLanguage) {
+    public void addNewLanguage(InternationalizationView newLanguage, Boolean force) {
         checkRequest(newLanguage);
-        repository.save(newLanguage.getAsInternationalizationSimple());
+        if (repository.findByLanguageOrderByIdDesc(newLanguage.getLanguage()).isEmpty()) {
+            repository.save(newLanguage.getAsInternationalizationSimple());
+        } else {
+            //add empty or override
+            InternationalizationSimple is = repository.findByLanguageOrderByIdDesc(newLanguage.getLanguage()).orElseThrow(() -> new DataConflictException("Language not found"));
+            InternationalizationView iv = is.getAsInternationalizationView();
+
+            if (!force) {
+                // only add new once, not override existed
+                Map<String, String> keyMap = new HashMap<>();
+                is.getLanguageNodes().forEach(node -> {
+                    keyMap.put(node.getKey(), node.getContent());
+                });
+                InternationalizationSimple simple = newLanguage.getAsInternationalizationSimple();
+                simple.getLanguageNodes().forEach(updatedNode -> {
+                    if (!keyMap.containsKey(updatedNode.getKey())) {
+                        is.getLanguageNodes().add(updatedNode);
+                    }
+                });
+                log.debug("New added {}", simple.getLanguageNodes().size());
+                repository.save(is);
+            } else {
+                //force update whole content
+                log.debug("force update, override all");
+                updateLanguage(newLanguage.getLanguage(), newLanguage.getContent());
+            }
+        }
+    }
+
+    private void overrideContent() {
+
     }
 
     private void checkRequest(InternationalizationView newLanguage) {
diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceTest.java
index 541e75c99..8306ed1ba 100644
--- a/src/test/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceTest.java
+++ b/src/test/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceTest.java
@@ -46,20 +46,20 @@ public class InternationalizationServiceTest {
 
     @Test
     public void shouldSaveLanguageContent(){
-        internationalizationService.addNewLanguage(this.language);
+        internationalizationService.addNewLanguage(this.language, false);
         verify(repository, times(1)).save(any());
     }
 
     @Test
     public void shouldNotSaveNullRequest(){
-        assertThrows(IllegalArgumentException.class, ()-> internationalizationService.addNewLanguage(null));
+        assertThrows(IllegalArgumentException.class, ()-> internationalizationService.addNewLanguage(null, false));
     }
 
     @Test
     public void shouldNotSaveWithEmptyLanguageId(){
         assertThrows(IllegalArgumentException.class, ()-> {
             this.language.setLanguage("");
-            internationalizationService.addNewLanguage(this.language);
+            internationalizationService.addNewLanguage(this.language, false);
         });
     }
 
@@ -67,7 +67,7 @@ public class InternationalizationServiceTest {
     public void shouldNotSaveWithEmptyContent(){
         assertThrows(IllegalArgumentException.class, ()-> {
             this.language.setContent("");
-            internationalizationService.addNewLanguage(this.language);
+            internationalizationService.addNewLanguage(this.language, false);
         });
     }
 
@@ -75,7 +75,7 @@ public class InternationalizationServiceTest {
     public void shouldNotSaveWithInvalidJsonContent(){
         assertThrows(IllegalArgumentException.class, ()-> {
             this.language.setContent("{invalid]");
-            internationalizationService.addNewLanguage(this.language);
+            internationalizationService.addNewLanguage(this.language, false);
         });
     }
 
-- 
GitLab


From 7491f7de1b41edde71c0c888b014b072b437dff1 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Thu, 10 Oct 2024 14:37:19 +0200
Subject: [PATCH 39/50] fix test

---
 .../notifications/templates/TemplateServiceTest.java  | 11 ++++++-----
 .../types/service/FormTypeServiceTest.java            |  3 ++-
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/src/test/java/net/geant/nmaas/notifications/templates/TemplateServiceTest.java b/src/test/java/net/geant/nmaas/notifications/templates/TemplateServiceTest.java
index ae36fb831..6f50f1e03 100644
--- a/src/test/java/net/geant/nmaas/notifications/templates/TemplateServiceTest.java
+++ b/src/test/java/net/geant/nmaas/notifications/templates/TemplateServiceTest.java
@@ -3,6 +3,7 @@ package net.geant.nmaas.notifications.templates;
 import net.geant.nmaas.notifications.MailTemplateElements;
 import net.geant.nmaas.notifications.templates.entities.MailTemplate;
 import net.geant.nmaas.notifications.templates.repository.MailTemplateRepository;
+import net.geant.nmaas.portal.exceptions.DataConflictException;
 import net.geant.nmaas.portal.service.impl.LocalFileStorageService;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -33,7 +34,7 @@ public class TemplateServiceTest {
 
     @Test
     void shouldThrowExceptionOnMissingHtmlTemplate() {
-        assertThrows(IllegalArgumentException.class, () -> {
+        assertThrows(DataConflictException.class, () -> {
             templateService.getHTMLTemplate();
         });
     }
@@ -52,17 +53,17 @@ public class TemplateServiceTest {
         when(repository.findByMailType(MailType.ACCOUNT_ACTIVATED))
                 .thenReturn(Optional.of(new MailTemplate(1L, MailType.ACCOUNT_ACTIVATED, null, null)));
         assertNotNull(templateService.getMailTemplate(MailType.ACCOUNT_ACTIVATED));
-        assertThrows(IllegalArgumentException.class, () -> {
+        assertThrows(DataConflictException.class, () -> {
             templateService.getMailTemplate(MailType.ACCOUNT_BLOCKED);
         });
     }
 
     @Test
     void shouldHandleHtmlTemplateStorage() {
-        assertThrows(IllegalArgumentException.class, () -> {
+        assertThrows(DataConflictException.class, () -> {
             templateService.storeHTMLTemplate(null);
         });
-        assertThrows(IllegalArgumentException.class, () -> {
+        assertThrows(DataConflictException.class, () -> {
             templateService.storeHTMLTemplate(new MockMultipartFile("name", "content".getBytes()));
         });
         assertDoesNotThrow(() -> {
@@ -72,7 +73,7 @@ public class TemplateServiceTest {
 
     @Test
     void shouldUpdateHtmlTemplate() {
-        assertThrows(IllegalArgumentException.class, () -> {
+        assertThrows(DataConflictException.class, () -> {
             templateService.updateHTMLTemplate(null);
         });
     }
diff --git a/src/test/java/net/geant/nmaas/notifications/types/service/FormTypeServiceTest.java b/src/test/java/net/geant/nmaas/notifications/types/service/FormTypeServiceTest.java
index 2f78bed75..550c228e7 100644
--- a/src/test/java/net/geant/nmaas/notifications/types/service/FormTypeServiceTest.java
+++ b/src/test/java/net/geant/nmaas/notifications/types/service/FormTypeServiceTest.java
@@ -4,6 +4,7 @@ import net.geant.nmaas.notifications.types.model.FormTypeRequest;
 import net.geant.nmaas.notifications.types.persistence.entity.FormType;
 import net.geant.nmaas.notifications.types.persistence.repository.FormTypeRepository;
 import net.geant.nmaas.portal.api.exception.ProcessingException;
+import net.geant.nmaas.portal.exceptions.DataConflictException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -44,7 +45,7 @@ public class FormTypeServiceTest {
         when(repository.existsById(anyString())).thenReturn(true);
         FormTypeRequest ftr = new FormTypeRequest("CONTACT", "", "", new ArrayList<>(), "");
 
-        assertThrows(ProcessingException.class, () -> this.underTest.create(ftr));
+        assertThrows(DataConflictException.class, () -> this.underTest.create(ftr));
 
         verify(repository, times(0)).save(any(FormType.class));
     }
-- 
GitLab


From 9ff30c19c95cd6469f07d902fae714bf669809e3 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Thu, 10 Oct 2024 15:03:29 +0200
Subject: [PATCH 40/50] delete imports

---
 .../notifications/templates/TemplateService.java | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java b/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java
index a94b007b3..0007dc83e 100644
--- a/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java
+++ b/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java
@@ -16,7 +16,6 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.multipart.MultipartFile;
 
-import javax.annotation.CheckForNull;
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
@@ -24,7 +23,6 @@ import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
-//import static com.google.common.base.Preconditions.checkArgument;
 
 @Service
 public class TemplateService {
@@ -36,32 +34,32 @@ public class TemplateService {
     private LocalFileStorageService fileStorageService;
 
     @Autowired
-    public TemplateService(MailTemplateRepository repository, LocalFileStorageService fileStorageService, ModelMapper modelMapper){
+    public TemplateService(MailTemplateRepository repository, LocalFileStorageService fileStorageService, ModelMapper modelMapper) {
         this.modelMapper = modelMapper;
         this.repository = repository;
         this.fileStorageService = fileStorageService;
     }
 
     @Transactional
-    public MailTemplateView getMailTemplate(MailType mailType){
-        MailTemplate mailTemplate = repository.findByMailType(mailType).orElseThrow(() -> new DataConflictException(String.format("Mail template %s not found", mailType.name() )));
+    public MailTemplateView getMailTemplate(MailType mailType) {
+        MailTemplate mailTemplate = repository.findByMailType(mailType).orElseThrow(() -> new DataConflictException(String.format("Mail template %s not found", mailType.name())));
         return modelMapper.map(mailTemplate, MailTemplateView.class);
     }
 
-    List<MailTemplateView> getMailTemplates(){
+    List<MailTemplateView> getMailTemplates() {
         return this.repository.findAll().stream()
                 .map(mailTemplate -> modelMapper.map(mailTemplate, MailTemplateView.class))
                 .collect(Collectors.toList());
     }
 
     void saveMailTemplate(MailTemplateView mailTemplate) {
-        checkArgument(!repository.existsByMailType(mailTemplate.getMailType()), String.format("Mail template %s already exists", mailTemplate.getMailType().name() ));
+        checkArgument(!repository.existsByMailType(mailTemplate.getMailType()), String.format("Mail template %s already exists", mailTemplate.getMailType().name()));
         checkArgument(mailTemplate.getTemplates() != null && !mailTemplate.getTemplates().isEmpty(), "Mail template cannot be null or empty");
         repository.save(modelMapper.map(mailTemplate, MailTemplate.class));
     }
 
     void updateMailTemplate(MailTemplateView mailTemplate) {
-        MailTemplate mailTemplateEntity = repository.findByMailType(mailTemplate.getMailType()).orElseThrow(() -> new DataConflictException(String.format("Mail template %s not found", mailTemplate.getMailType().name() )));
+        MailTemplate mailTemplateEntity = repository.findByMailType(mailTemplate.getMailType()).orElseThrow(() -> new DataConflictException(String.format("Mail template %s not found", mailTemplate.getMailType().name())));
         checkArgument(mailTemplate.getTemplates() != null && !mailTemplate.getTemplates().isEmpty(), "Mail template cannot be null or empty");
         mailTemplateEntity.getTemplates().clear();
         mailTemplateEntity.getTemplates().addAll(mailTemplate.getTemplates().stream().map(template -> modelMapper.map(template, LanguageMailContent.class)).collect(Collectors.toList()));
@@ -75,7 +73,7 @@ public class TemplateService {
         fileStorageService.store(file);
     }
 
-    void updateHTMLTemplate(MultipartFile file){
+    void updateHTMLTemplate(MultipartFile file) {
         FileInfo fileInfo = getHTMLTemplateFileInfo();
         if (fileStorageService.remove(fileInfo)) {
             storeHTMLTemplate(file);
-- 
GitLab


From 5303a41a5cf9ad9f0283066300cc5f19a6b663df Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Wed, 23 Oct 2024 11:19:15 +0200
Subject: [PATCH 41/50] update exception type

---
 .../nmaas/notifications/templates/TemplateService.java     | 6 +++---
 .../service/impl/InternationalizationServiceImpl.java      | 7 ++-----
 2 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java b/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java
index 0007dc83e..632c57c83 100644
--- a/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java
+++ b/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java
@@ -42,7 +42,7 @@ public class TemplateService {
 
     @Transactional
     public MailTemplateView getMailTemplate(MailType mailType) {
-        MailTemplate mailTemplate = repository.findByMailType(mailType).orElseThrow(() -> new DataConflictException(String.format("Mail template %s not found", mailType.name())));
+        MailTemplate mailTemplate = repository.findByMailType(mailType).orElseThrow(() -> new IllegalArgumentException("Mail template not found"));
         return modelMapper.map(mailTemplate, MailTemplateView.class);
     }
 
@@ -59,7 +59,7 @@ public class TemplateService {
     }
 
     void updateMailTemplate(MailTemplateView mailTemplate) {
-        MailTemplate mailTemplateEntity = repository.findByMailType(mailTemplate.getMailType()).orElseThrow(() -> new DataConflictException(String.format("Mail template %s not found", mailTemplate.getMailType().name())));
+        MailTemplate mailTemplateEntity = repository.findByMailType(mailTemplate.getMailType()).orElseThrow(() -> new IllegalArgumentException("Mail template not found"));
         checkArgument(mailTemplate.getTemplates() != null && !mailTemplate.getTemplates().isEmpty(), "Mail template cannot be null or empty");
         mailTemplateEntity.getTemplates().clear();
         mailTemplateEntity.getTemplates().addAll(mailTemplate.getTemplates().stream().map(template -> modelMapper.map(template, LanguageMailContent.class)).collect(Collectors.toList()));
@@ -90,7 +90,7 @@ public class TemplateService {
         if (template.size() == 1) {
             return template.get(0);
         }
-        throw new DataConflictException(String.format("Exactly one html template supported (actually got %d)", template.size()));
+        throw new IllegalArgumentException(String.format("Exactly one html template supported (actually got %d)", template.size()));
     }
 
     private static void checkArgument(boolean expression, String errorMessage) {
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceImpl.java
index 6916a916b..9b76a37eb 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceImpl.java
@@ -15,6 +15,7 @@ import org.modelmapper.ModelMapper;
 import org.springframework.stereotype.Service;
 
 import javax.transaction.Transactional;
+import javax.ws.rs.NotFoundException;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.List;
@@ -37,7 +38,7 @@ public class InternationalizationServiceImpl implements InternationalizationServ
             repository.save(newLanguage.getAsInternationalizationSimple());
         } else {
             //add empty or override
-            InternationalizationSimple is = repository.findByLanguageOrderByIdDesc(newLanguage.getLanguage()).orElseThrow(() -> new DataConflictException("Language not found"));
+            InternationalizationSimple is = repository.findByLanguageOrderByIdDesc(newLanguage.getLanguage()).orElseThrow(() -> new NotFoundException("Language not found"));
             InternationalizationView iv = is.getAsInternationalizationView();
 
             if (!force) {
@@ -62,10 +63,6 @@ public class InternationalizationServiceImpl implements InternationalizationServ
         }
     }
 
-    private void overrideContent() {
-
-    }
-
     private void checkRequest(InternationalizationView newLanguage) {
         if (newLanguage == null) {
             throw new IllegalArgumentException("Language cannot be null");
-- 
GitLab


From 4d60186ef4c577b081d084e6bf1df196e612fae2 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Wed, 23 Oct 2024 12:05:07 +0200
Subject: [PATCH 42/50] Add constraint on user on BulkDeployment

---
 .../geant/nmaas/portal/api/bulk/BulkController.java  | 12 ++++++------
 .../portal/persistent/entity/BulkDeployment.java     |  6 +++++-
 .../service/impl/BulkApplicationServiceImpl.java     |  6 ++++--
 .../portal/service/impl/BulkDomainServiceImpl.java   |  4 ++--
 ...23_1200__addContraintOnUserIdInBulkDeployment.sql |  4 ++++
 5 files changed, 21 insertions(+), 11 deletions(-)
 create mode 100644 src/main/resources/db/migration/common/V1.7.0_20241023_1200__addContraintOnUserIdInBulkDeployment.sql

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 dc3629c55..517827788 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
@@ -110,7 +110,7 @@ public class BulkController {
         log.info("Processing bulk application deployment details request");
         BulkDeployment bulk = bulkDeploymentRepository.findById(id).orElseThrow();
         BulkDeploymentView bulkView = modelMapper.map(bulk, BulkDeploymentView.class);
-        bulkView.setCreator(getUserView(bulk.getCreatorId()));
+        bulkView.setCreator(getUserView(bulk.getCreator().getId()));
         mapDetails(bulk, bulkView);
         List<BulkAppDetails> details = bulkApplicationService.getAppsBulkDetails(bulkView);
         InputStreamResource inputStreamResource = bulkApplicationService.getInputStreamAppBulkDetails(details);
@@ -165,7 +165,7 @@ public class BulkController {
             return ResponseEntity.notFound().build();
         }
 
-        if(bulk.get().getCreatorId().equals(user.getId()) ) {
+        if(bulk.get().getCreator().getId().equals(user.getId()) ) {
             throw new PermissionDeniedDataAccessException("User doesnt have access to this bulk deployment", new Throwable());
         }
         if (removeApps) {
@@ -196,9 +196,9 @@ public class BulkController {
     private <T extends BulkDeploymentViewS> T mapToView(BulkDeployment bulk, Class<T> viewType) {
         T bulkView = modelMapper.map(bulk, viewType);
         try {
-            bulkView.setCreator(getUserView(bulk.getCreatorId()));
+            bulkView.setCreator(getUserView(bulk.getCreator().getId()));
         } catch (Exception ex) {
-            log.error("Can not find creator for {} - creatorId:  {}", bulk.getId(), bulk.getCreatorId());
+            log.error("Can not find creator for {} - creatorId:  {}", bulk.getId(), bulk.getCreator().getId());
             return null;
         }
         mapDetails(bulk, bulkView);
@@ -208,10 +208,10 @@ public class BulkController {
     private BulkDeploymentView mapToView(BulkDeployment deployment) {
         BulkDeploymentView bulkView = modelMapper.map(deployment, BulkDeploymentView.class);
         try {
-            bulkView.setCreator(getUserView(deployment.getCreatorId()));
+            bulkView.setCreator(getUserView(deployment.getCreator().getId()));
             return null;
         } catch (Exception ex) {
-            log.error("Can not find creator for {} - creatorId:  {}", deployment.getId(), deployment.getCreatorId());
+            log.error("Can not find creator for {} - creatorId:  {}", deployment.getId(), deployment.getCreator().getId());
         }
         mapDetails(deployment, bulkView);
         return bulkView;
diff --git a/src/main/java/net/geant/nmaas/portal/persistent/entity/BulkDeployment.java b/src/main/java/net/geant/nmaas/portal/persistent/entity/BulkDeployment.java
index 9d3357fc9..06a9a0b9f 100644
--- a/src/main/java/net/geant/nmaas/portal/persistent/entity/BulkDeployment.java
+++ b/src/main/java/net/geant/nmaas/portal/persistent/entity/BulkDeployment.java
@@ -15,6 +15,8 @@ import javax.persistence.Enumerated;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import java.time.OffsetDateTime;
 import java.util.ArrayList;
@@ -32,7 +34,9 @@ public class BulkDeployment {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
 
-    private Long creatorId;
+    @ManyToOne
+    @JoinColumn(name = "creator_id")
+    private User creator;
 
     private OffsetDateTime creationDate;
 
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 c7e5ae6c4..2e4dd2e92 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
@@ -37,6 +37,7 @@ import net.geant.nmaas.portal.service.ApplicationService;
 import net.geant.nmaas.portal.service.ApplicationSubscriptionService;
 import net.geant.nmaas.portal.service.BulkApplicationService;
 import net.geant.nmaas.portal.service.DomainService;
+import net.geant.nmaas.portal.service.UserService;
 import org.apache.commons.collections4.MultiValuedMap;
 import org.jetbrains.annotations.NotNull;
 import org.modelmapper.ModelMapper;
@@ -84,6 +85,7 @@ public class BulkApplicationServiceImpl implements BulkApplicationService {
     private final ApplicationService applicationService;
     private final DomainService domainService;
     private final ApplicationSubscriptionService applicationSubscriptionService;
+    private final UserService userService;
 
     private final ApplicationInstanceService instanceService;
     private final AppDeploymentMonitor appDeploymentMonitor;
@@ -372,11 +374,11 @@ public class BulkApplicationServiceImpl implements BulkApplicationService {
         }
     }
 
-    private static BulkDeployment createBulkDeployment(UserViewMinimal creator) {
+    private  BulkDeployment createBulkDeployment(UserViewMinimal creator) {
         BulkDeployment bulkDeployment = new BulkDeployment();
         bulkDeployment.setType(BulkType.APPLICATION);
         bulkDeployment.setState(BulkDeploymentState.PROCESSING);
-        bulkDeployment.setCreatorId(creator.getId());
+        bulkDeployment.setCreator(userService.findById(creator.getId()).orElseThrow(() -> new MissingElementException("User with this ID not found")));
         bulkDeployment.setCreationDate(OffsetDateTime.now());
         bulkDeployment.setEntries(new ArrayList<>());
         return bulkDeployment;
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceImpl.java
index 5c9315950..fa2512fbd 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceImpl.java
@@ -238,11 +238,11 @@ public class BulkDomainServiceImpl implements BulkDomainService {
         }
     }
 
-    private static BulkDeployment createBulkDeployment(UserViewMinimal creator) {
+    private  BulkDeployment createBulkDeployment(UserViewMinimal creator) {
         BulkDeployment bulkDeployment = new BulkDeployment();
         bulkDeployment.setType(DOMAIN);
         bulkDeployment.setState(PENDING);
-        bulkDeployment.setCreatorId(creator.getId());
+        bulkDeployment.setCreator(userService.findById(creator.getId()).orElseThrow(() ->new MissingElementException("User with this ID not found")));
         bulkDeployment.setCreationDate(OffsetDateTime.now());
         return bulkDeployment;
     }
diff --git a/src/main/resources/db/migration/common/V1.7.0_20241023_1200__addContraintOnUserIdInBulkDeployment.sql b/src/main/resources/db/migration/common/V1.7.0_20241023_1200__addContraintOnUserIdInBulkDeployment.sql
new file mode 100644
index 000000000..df08df98f
--- /dev/null
+++ b/src/main/resources/db/migration/common/V1.7.0_20241023_1200__addContraintOnUserIdInBulkDeployment.sql
@@ -0,0 +1,4 @@
+alter table bulk_deployment 
+       add constraint FKqd4ff6vy2u2wu2d8aug322bc0 
+       foreign key (creator_id) 
+       references users;
\ No newline at end of file
-- 
GitLab


From f16b167215c4df4a48bf90bffb602c054c6e21d0 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Wed, 23 Oct 2024 12:24:24 +0200
Subject: [PATCH 43/50] Update test

---
 .../service/impl/BulkDomainServiceIntTest.java  |  6 +++---
 .../portal/api/bulk/BulkControllerTest.java     |  6 +++---
 .../impl/BulkApplicationServiceImplTest.java    | 17 +++++++++++++----
 .../service/impl/BulkDomainServiceImplTest.java | 11 +++++++----
 4 files changed, 26 insertions(+), 14 deletions(-)

diff --git a/src/integrationTest/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceIntTest.java b/src/integrationTest/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceIntTest.java
index 391174b49..80ab22b6c 100644
--- a/src/integrationTest/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceIntTest.java
+++ b/src/integrationTest/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceIntTest.java
@@ -80,7 +80,7 @@ public class BulkDomainServiceIntTest {
         List<BulkDeployment> bulkDeployments = bulkDeploymentRepository.findAll();
         assertEquals(1, bulkDeployments.size());
         BulkDeployment bulkDeployment = bulkDeployments.get(0);
-        assertEquals(1, bulkDeployment.getCreatorId());
+        assertEquals(1, bulkDeployment.getCreator().getId());
         assertEquals(BulkType.DOMAIN, bulkDeployment.getType());
         assertEquals(6, bulkDeployment.getEntries().size());
         assertThat(userRepository.findByEmail("user1@test.com").orElseThrow().getSamlToken()).isEqualTo("user1@test.com");
@@ -105,7 +105,7 @@ public class BulkDomainServiceIntTest {
         List<BulkDeployment> bulkDeployments = bulkDeploymentRepository.findAll();
         assertEquals(1, bulkDeployments.size());
         BulkDeployment bulkDeployment = bulkDeployments.get(0);
-        assertEquals(1, bulkDeployment.getCreatorId());
+        assertEquals(1, bulkDeployment.getCreator().getId());
         assertEquals(BulkType.DOMAIN, bulkDeployment.getType());
         assertEquals(4, bulkDeployment.getEntries().size());
         assertThat(userRepository.findByEmail("user1@test.com").orElseThrow().getSamlToken()).isNull();
@@ -128,7 +128,7 @@ public class BulkDomainServiceIntTest {
         List<BulkDeployment> bulkDeployments = bulkDeploymentRepository.findAll();
         assertEquals(1, bulkDeployments.size());
         BulkDeployment bulkDeployment = bulkDeployments.get(0);
-        assertEquals(1, bulkDeployment.getCreatorId());
+        assertEquals(1, bulkDeployment.getCreator().getId());
         assertEquals(BulkType.DOMAIN, bulkDeployment.getType());
         assertEquals(6, bulkDeployment.getEntries().size());
         assertEquals("testdomain10", bulkDeployment.getEntries().get(0).getDetails().get("domainCodename"));
diff --git a/src/test/java/net/geant/nmaas/portal/api/bulk/BulkControllerTest.java b/src/test/java/net/geant/nmaas/portal/api/bulk/BulkControllerTest.java
index 55fe03f48..02fa1407e 100644
--- a/src/test/java/net/geant/nmaas/portal/api/bulk/BulkControllerTest.java
+++ b/src/test/java/net/geant/nmaas/portal/api/bulk/BulkControllerTest.java
@@ -105,7 +105,7 @@ public class BulkControllerTest {
         List<BulkDeployment> bulks = new ArrayList<>();
         BulkDeployment viewS = new BulkDeployment();
         viewS.setType(BulkType.DOMAIN);
-        viewS.setCreatorId(10L);
+        viewS.setCreator(user);
         bulks.add(viewS);
         when(bulkDeploymentRepository.findByType(BulkType.DOMAIN)).thenReturn(List.of(viewS));
         assertEquals(1, Objects.requireNonNull(bulkController.getDomainDeploymentRecordsRestrictedToOwner(principalMock).getBody()).size());
@@ -126,11 +126,11 @@ public class BulkControllerTest {
         List<BulkDeployment> bulks = new ArrayList<>();
         BulkDeployment base1 = new BulkDeployment();
         base1.setType(BulkType.DOMAIN);
-        base1.setCreatorId(10L);
+        base1.setCreator(user);
         bulks.add(base1);
         BulkDeployment base2 = new BulkDeployment();
         base2.setType(BulkType.DOMAIN);
-        base2.setCreatorId(20L);
+        base2.setCreator(user2);
         bulks.add(base2);
         when(bulkDeploymentRepository.findByType(BulkType.APPLICATION)).thenReturn(bulks);
         assertEquals(1, Objects.requireNonNull(bulkController.getAppDeploymentRecordsRestrictedToOwner(principalMock).getBody()).size());
diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/BulkApplicationServiceImplTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/BulkApplicationServiceImplTest.java
index 02909dad0..1d9f08617 100644
--- a/src/test/java/net/geant/nmaas/portal/service/impl/BulkApplicationServiceImplTest.java
+++ b/src/test/java/net/geant/nmaas/portal/service/impl/BulkApplicationServiceImplTest.java
@@ -19,6 +19,7 @@ import net.geant.nmaas.portal.persistent.entity.BulkDeployment;
 import net.geant.nmaas.portal.persistent.entity.BulkDeploymentEntry;
 import net.geant.nmaas.portal.persistent.entity.BulkDeploymentState;
 import net.geant.nmaas.portal.persistent.entity.Domain;
+import net.geant.nmaas.portal.persistent.entity.User;
 import net.geant.nmaas.portal.persistent.repositories.BulkDeploymentEntryRepository;
 import net.geant.nmaas.portal.persistent.repositories.BulkDeploymentRepository;
 import net.geant.nmaas.portal.service.ApplicationBaseService;
@@ -27,6 +28,7 @@ import net.geant.nmaas.portal.service.ApplicationService;
 import net.geant.nmaas.portal.service.ApplicationSubscriptionService;
 import net.geant.nmaas.portal.service.BulkApplicationService;
 import net.geant.nmaas.portal.service.DomainService;
+import net.geant.nmaas.portal.service.UserService;
 import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
 import org.junit.jupiter.api.Test;
 import org.mockito.AdditionalAnswers;
@@ -70,11 +72,13 @@ public class BulkApplicationServiceImplTest {
     private final AppLifecycleManager appLifecycleManager = mock(AppLifecycleManager.class);
     private final BulkDeploymentRepository bulkDeploymentRepository = mock(BulkDeploymentRepository.class);
     private final BulkDeploymentEntryRepository bulkDeploymentEntryRepository = mock(BulkDeploymentEntryRepository.class);
+
+    private final UserService userService = mock(UserService.class);
     private final ModelMapper modelMapper = new ModelMapper();
     private final ApplicationEventPublisher eventPublisher = mock(ApplicationEventPublisher.class);
 
     final BulkApplicationService bulkApplicationService = new BulkApplicationServiceImpl(applicationBaseService, applicationService,
-            domainService, applicationSubscriptionService, applicationInstanceService, appDeploymentMonitor, appLifecycleManager,
+            domainService, applicationSubscriptionService,userService,  applicationInstanceService, appDeploymentMonitor, appLifecycleManager,
             bulkDeploymentRepository, bulkDeploymentEntryRepository, modelMapper, eventPublisher);
 
     @Test
@@ -98,6 +102,9 @@ public class BulkApplicationServiceImplTest {
         when(bulkDeploymentEntryRepository.save(any(BulkDeploymentEntry.class))).then(AdditionalAnswers.returnsFirstArg());
         when(bulkDeploymentRepository.save(any(BulkDeployment.class))).thenReturn(new BulkDeployment());
         doNothing().when(eventPublisher).publishEvent(any(ApplicationEvent.class));
+        User user =new User("Test");
+        user.setId(1L);
+        when(userService.findById(any())).thenReturn(Optional.of(user));
 
         bulkApplicationService.handleBulkDeployment(TEST_APP_NAME, List.of(csvApplication), testUser());
 
@@ -116,7 +123,7 @@ public class BulkApplicationServiceImplTest {
         BulkDeployment bulkDeployment = bulkDeploymentArgumentCaptor.getValue();
         assertEquals(PROCESSING, bulkDeployment.getState());
         assertEquals(APPLICATION, bulkDeployment.getType());
-        assertEquals(testUser().getId(), bulkDeployment.getCreatorId());
+        assertEquals(testUser().getId(), bulkDeployment.getCreator().getId());
         assertEquals(1, bulkDeployment.getEntries().size());
         assertEquals(PROCESSING, bulkDeployment.getEntries().get(0).getState());
     }
@@ -175,11 +182,13 @@ public class BulkApplicationServiceImplTest {
     @Test
     void shouldHandleDeploymentReview() {
         AppAutoDeploymentReviewEvent event = new AppAutoDeploymentReviewEvent(this);
+        User user = new User("Test");
+        user.setId(1L);
         BulkDeployment bAppToBeCompleted = new BulkDeployment(
-                1L, 1L, OffsetDateTime.now(), PROCESSING, APPLICATION,
+                1L, user, OffsetDateTime.now(), PROCESSING, APPLICATION,
                 new ArrayList<>(List.of(new BulkDeploymentEntry(10L, APPLICATION, COMPLETED, true, null))));
         BulkDeployment bAppProcessing = new BulkDeployment(
-                2L, 1L, OffsetDateTime.now(), PROCESSING, APPLICATION,
+                2L, user, OffsetDateTime.now(), PROCESSING, APPLICATION,
                 new ArrayList<>(List.of(new BulkDeploymentEntry(11L, APPLICATION, PROCESSING, true, null))));
         when(bulkDeploymentRepository.findByTypeAndState(APPLICATION, PROCESSING))
                 .thenReturn(List.of(bAppToBeCompleted, bAppProcessing));
diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceImplTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceImplTest.java
index 1b6b7b62f..a110e99b8 100644
--- a/src/test/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceImplTest.java
+++ b/src/test/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceImplTest.java
@@ -66,6 +66,7 @@ public class BulkDomainServiceImplTest {
         when(userService.hasPrivilege((User) any(),any(),any())).thenReturn(true);
         when(bulkDeploymentRepository.save(any())).thenReturn(new BulkDeployment());
         when(configurationManager.getConfiguration()).thenReturn(new ConfigurationView());
+        when(userService.findById(any())).thenReturn(Optional.of(user));
 
         bulkDomainService.handleBulkCreation(List.of(csvDomain), testUser());
 
@@ -77,7 +78,7 @@ public class BulkDomainServiceImplTest {
         assertEquals(2, bulkDeployment.getEntries().size());
         assertThat(bulkDeployment.getEntries().stream().filter(e -> e.getType() == BulkType.USER).findAny().orElseThrow().getCreated()).isFalse();
         assertThat(bulkDeployment.getEntries().stream().filter(e -> e.getType() == BulkType.DOMAIN).findAny().orElseThrow().getCreated()).isFalse();
-        assertEquals(testUser().getId(), bulkDeployment.getCreatorId());
+        assertEquals(testUser().getId(), bulkDeployment.getCreator().getId());
     }
 
     @Test
@@ -99,6 +100,7 @@ public class BulkDomainServiceImplTest {
         when(userService.hasPrivilege((User) any(),any(),any())).thenReturn(true);
         when(bulkDeploymentRepository.save(any())).thenReturn(new BulkDeployment());
         when(configurationManager.getConfiguration()).thenReturn(new ConfigurationView());
+        when(userService.findById(any())).thenReturn(Optional.of(user));
 
         bulkDomainService.handleBulkCreation(List.of(csvDomain), testUser());
 
@@ -110,7 +112,7 @@ public class BulkDomainServiceImplTest {
         assertEquals(2, bulkDeployment.getEntries().size());
         assertThat(bulkDeployment.getEntries().stream().filter(e -> e.getType() == BulkType.USER).findAny().orElseThrow().getCreated()).isFalse();
         assertThat(bulkDeployment.getEntries().stream().filter(e -> e.getType() == BulkType.DOMAIN).findAny().orElseThrow().getCreated()).isTrue();
-        assertEquals(testUser().getId(), bulkDeployment.getCreatorId());
+        assertEquals(testUser().getId(), bulkDeployment.getCreator().getId());
     }
 
     @Test
@@ -131,10 +133,11 @@ public class BulkDomainServiceImplTest {
         when(userService.existsByUsername("user2")).thenReturn(false);
         when(userService.existsByEmail("user2@test.com")).thenReturn(Boolean.FALSE);
         User user = new User("user1", true);
-        user.setId(10L);
+        user.setId(1L);
         user.setEmail("user1@test.com");
         when(userService.registerBulk(any(), any(), any())).thenReturn(user);
         when(bulkDeploymentRepository.save(any())).thenReturn(new BulkDeployment());
+        when(userService.findById(any())).thenReturn(Optional.of(user));
 
         bulkDomainService.handleBulkCreation(List.of(csvDomain1, csvDomain2), testUser());
 
@@ -146,7 +149,7 @@ public class BulkDomainServiceImplTest {
         assertEquals(4, bulkDeployment.getEntries().size());
         assertThat(bulkDeployment.getEntries().stream().filter(e -> e.getType() == BulkType.USER)).allMatch(BulkDeploymentEntry::getCreated);
         assertThat(bulkDeployment.getEntries().stream().filter(e -> e.getType() == BulkType.DOMAIN)).noneMatch(BulkDeploymentEntry::getCreated);
-        assertEquals(testUser().getId(), bulkDeployment.getCreatorId());
+        assertEquals(testUser().getId(), bulkDeployment.getCreator().getId());
     }
 
     private static UserViewMinimal testUser() {
-- 
GitLab


From 563bde8aa10b04ee38bcff75a1386a0f26f30076 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Thu, 24 Oct 2024 12:14:41 +0200
Subject: [PATCH 44/50] Update domain groups

---
 .../portal/api/market/DomainController.java   | 14 ++++++++++++++
 .../nmaas/portal/service/DomainService.java   |  3 +++
 .../service/impl/DomainServiceImpl.java       | 19 +++++++++++++++++++
 3 files changed, 36 insertions(+)

diff --git a/src/main/java/net/geant/nmaas/portal/api/market/DomainController.java b/src/main/java/net/geant/nmaas/portal/api/market/DomainController.java
index 382d56837..36707803c 100644
--- a/src/main/java/net/geant/nmaas/portal/api/market/DomainController.java
+++ b/src/main/java/net/geant/nmaas/portal/api/market/DomainController.java
@@ -16,6 +16,7 @@ import net.geant.nmaas.portal.api.domain.DomainRequest;
 import net.geant.nmaas.portal.api.domain.DomainView;
 import net.geant.nmaas.portal.api.domain.Id;
 import net.geant.nmaas.portal.api.domain.KeyValueView;
+import net.geant.nmaas.portal.api.domain.UserViewMinimal;
 import net.geant.nmaas.portal.api.exception.MissingElementException;
 import net.geant.nmaas.portal.api.exception.ProcessingException;
 import net.geant.nmaas.portal.exceptions.ObjectNotFoundException;
@@ -310,6 +311,19 @@ public class DomainController extends AppBaseController {
 		}
 	}
 
+	@PutMapping("/group/members/{domainGroupId}")
+	@Transactional
+	@PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_VL_MANAGER')")
+	public DomainGroupView updateDomainGroupMembers(@PathVariable Long domainGroupId, @RequestBody List<UserViewMinimal> members, Principal principal) throws AccessDeniedException {
+		DomainGroupView domainGroup = domainGroupService.getDomainGroup(domainGroupId);
+		if (getUser(principal.getName()).getRoles().stream().anyMatch(userRole -> userRole.getRole().equals(Role.ROLE_SYSTEM_ADMIN)) ||
+				domainGroup.getManagers().stream().anyMatch(user -> user.getUsername().equalsIgnoreCase(principal.getName()))) {
+			return domainService.updateMembers(members, domainGroup);
+		} else {
+			throw new AccessDeniedException("You have no access to this domain group");
+		}
+	}
+
 	@GetMapping("/annotations")
 	@Transactional
 	@PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN')")
diff --git a/src/main/java/net/geant/nmaas/portal/service/DomainService.java b/src/main/java/net/geant/nmaas/portal/service/DomainService.java
index 70ff8d5da..a26f3b60c 100644
--- a/src/main/java/net/geant/nmaas/portal/service/DomainService.java
+++ b/src/main/java/net/geant/nmaas/portal/service/DomainService.java
@@ -7,6 +7,7 @@ import net.geant.nmaas.portal.api.domain.DomainGroupView;
 import net.geant.nmaas.portal.api.domain.DomainRequest;
 import net.geant.nmaas.portal.api.domain.KeyValueView;
 import net.geant.nmaas.portal.api.domain.UserView;
+import net.geant.nmaas.portal.api.domain.UserViewMinimal;
 import net.geant.nmaas.portal.persistent.entity.ApplicationBase;
 import net.geant.nmaas.portal.persistent.entity.Domain;
 import net.geant.nmaas.portal.persistent.entity.DomainAnnotation;
@@ -70,6 +71,8 @@ public interface DomainService {
 
 	void updateRolesInDomainGroupByUsers(DomainGroupView view);
 
+	DomainGroupView updateMembers(List<UserViewMinimal> newMembers, DomainGroupView view);
+
 	void addAnnotation(KeyValueView annotation);
 	boolean checkIfAnnotationExist(String key);
 	void deleteAnnotation(Long id);
diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/DomainServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/DomainServiceImpl.java
index 0caa03568..98c8a5391 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/DomainServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/DomainServiceImpl.java
@@ -12,6 +12,7 @@ import net.geant.nmaas.portal.api.domain.DomainGroupView;
 import net.geant.nmaas.portal.api.domain.DomainRequest;
 import net.geant.nmaas.portal.api.domain.KeyValueView;
 import net.geant.nmaas.portal.api.domain.UserView;
+import net.geant.nmaas.portal.api.domain.UserViewMinimal;
 import net.geant.nmaas.portal.api.exception.MissingElementException;
 import net.geant.nmaas.portal.api.exception.ProcessingException;
 import net.geant.nmaas.portal.events.DomainCreatedEvent;
@@ -494,6 +495,24 @@ public class DomainServiceImpl implements DomainService {
         });
     }
 
+    @Override
+    public DomainGroupView updateMembers(List<UserViewMinimal> newMembers, DomainGroupView view) {
+        //delete roles
+
+        List<UserViewMinimal> toDeleteRole = new ArrayList<>(view.getManagers());
+        toDeleteRole.removeAll(newMembers);
+
+        toDeleteRole.forEach(user -> {
+            view.getDomains().forEach(domain -> {
+                this.removeMemberRole(domain.getId(), user.getId(), Role.ROLE_VL_DOMAIN_ADMIN);
+            });
+        });
+
+        view.setManagers(newMembers);
+        updateRolesInDomainGroupByUsers(view);
+        return domainGroupService.updateDomainGroup(view.getId(), view);
+    }
+
     // Domain annotations
 
     @Override
-- 
GitLab


From 83be806aef53c58ee31be3c4fc3c95de9a692073 Mon Sep 17 00:00:00 2001
From: llopat <llopat@man.poznan.pl>
Date: Thu, 24 Oct 2024 12:37:08 +0200
Subject: [PATCH 45/50] Fix after merge

---
 .../geant/nmaas/portal/service/BulkApplicationService.java    | 4 ----
 1 file changed, 4 deletions(-)

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 080c4f27e..53dc4336c 100644
--- a/src/main/java/net/geant/nmaas/portal/service/BulkApplicationService.java
+++ b/src/main/java/net/geant/nmaas/portal/service/BulkApplicationService.java
@@ -29,8 +29,4 @@ public interface BulkApplicationService {
 
     BulkDeployment updateState(Long bulkId);
 
-    void deleteAppInstancesFromBulk(BulkDeploymentView bulk);
-
-    BulkDeployment updateState(Long bulkId);
-
 }
\ No newline at end of file
-- 
GitLab


From d186804132719e7fe8447ba2ba26a040f4184556 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Thu, 24 Oct 2024 13:14:16 +0200
Subject: [PATCH 46/50] update thrown exception

---
 .../nmaas/notifications/templates/TemplateService.java |  8 +++++---
 .../notifications/templates/TemplateServiceTest.java   | 10 +++++-----
 2 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java b/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java
index 632c57c83..8f8cb8d92 100644
--- a/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java
+++ b/src/main/java/net/geant/nmaas/notifications/templates/TemplateService.java
@@ -23,6 +23,8 @@ import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
 
 @Service
 public class TemplateService {
@@ -53,7 +55,7 @@ public class TemplateService {
     }
 
     void saveMailTemplate(MailTemplateView mailTemplate) {
-        checkArgument(!repository.existsByMailType(mailTemplate.getMailType()), String.format("Mail template %s already exists", mailTemplate.getMailType().name()));
+        checkArgumentConflict(!repository.existsByMailType(mailTemplate.getMailType()), String.format("Mail template %s already exists", mailTemplate.getMailType().name()));
         checkArgument(mailTemplate.getTemplates() != null && !mailTemplate.getTemplates().isEmpty(), "Mail template cannot be null or empty");
         repository.save(modelMapper.map(mailTemplate, MailTemplate.class));
     }
@@ -69,7 +71,7 @@ public class TemplateService {
     void storeHTMLTemplate(MultipartFile file) {
         checkArgument(file != null && !file.isEmpty(), "HTML template cannot be null or empty");
         checkArgument(Objects.equals(file.getContentType(), MailTemplateElements.HTML_TYPE), "HTML template must be in html format");
-        checkArgument(fileStorageService.getFileInfoByContentType(MailTemplateElements.HTML_TYPE).isEmpty(), "Only one HTML template is supported.");
+        checkArgumentConflict(fileStorageService.getFileInfoByContentType(MailTemplateElements.HTML_TYPE).isEmpty(), "Only one HTML template is supported.");
         fileStorageService.store(file);
     }
 
@@ -93,7 +95,7 @@ public class TemplateService {
         throw new IllegalArgumentException(String.format("Exactly one html template supported (actually got %d)", template.size()));
     }
 
-    private static void checkArgument(boolean expression, String errorMessage) {
+    private static void checkArgumentConflict(boolean expression, String errorMessage) {
         if (!expression) {
             throw new DataConflictException(String.valueOf(errorMessage));
         }
diff --git a/src/test/java/net/geant/nmaas/notifications/templates/TemplateServiceTest.java b/src/test/java/net/geant/nmaas/notifications/templates/TemplateServiceTest.java
index 6f50f1e03..02b79cdc8 100644
--- a/src/test/java/net/geant/nmaas/notifications/templates/TemplateServiceTest.java
+++ b/src/test/java/net/geant/nmaas/notifications/templates/TemplateServiceTest.java
@@ -34,7 +34,7 @@ public class TemplateServiceTest {
 
     @Test
     void shouldThrowExceptionOnMissingHtmlTemplate() {
-        assertThrows(DataConflictException.class, () -> {
+        assertThrows(IllegalArgumentException.class, () -> {
             templateService.getHTMLTemplate();
         });
     }
@@ -53,17 +53,17 @@ public class TemplateServiceTest {
         when(repository.findByMailType(MailType.ACCOUNT_ACTIVATED))
                 .thenReturn(Optional.of(new MailTemplate(1L, MailType.ACCOUNT_ACTIVATED, null, null)));
         assertNotNull(templateService.getMailTemplate(MailType.ACCOUNT_ACTIVATED));
-        assertThrows(DataConflictException.class, () -> {
+        assertThrows(IllegalArgumentException.class, () -> {
             templateService.getMailTemplate(MailType.ACCOUNT_BLOCKED);
         });
     }
 
     @Test
     void shouldHandleHtmlTemplateStorage() {
-        assertThrows(DataConflictException.class, () -> {
+        assertThrows(IllegalArgumentException.class, () -> {
             templateService.storeHTMLTemplate(null);
         });
-        assertThrows(DataConflictException.class, () -> {
+        assertThrows(IllegalArgumentException.class, () -> {
             templateService.storeHTMLTemplate(new MockMultipartFile("name", "content".getBytes()));
         });
         assertDoesNotThrow(() -> {
@@ -73,7 +73,7 @@ public class TemplateServiceTest {
 
     @Test
     void shouldUpdateHtmlTemplate() {
-        assertThrows(DataConflictException.class, () -> {
+        assertThrows(IllegalArgumentException.class, () -> {
             templateService.updateHTMLTemplate(null);
         });
     }
-- 
GitLab


From cd74d1753280b0e8139f01f56c5bebc5e02d5184 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Wed, 30 Oct 2024 15:45:16 +0100
Subject: [PATCH 47/50] Add test for members

---
 .../service/impl/DomainServiceTest.java       | 104 ++++++++++++++++++
 1 file changed, 104 insertions(+)

diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java
index e9e2e9922..f0cbfabbf 100644
--- a/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java
+++ b/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java
@@ -8,10 +8,13 @@ import net.geant.nmaas.dcn.deployment.entities.DomainDcnDetails;
 import net.geant.nmaas.dcn.deployment.repositories.DomainDcnDetailsRepository;
 import net.geant.nmaas.orchestration.entities.DomainTechDetails;
 import net.geant.nmaas.orchestration.repositories.DomainTechDetailsRepository;
+import net.geant.nmaas.portal.api.domain.DomainBase;
 import net.geant.nmaas.portal.api.domain.DomainDcnDetailsView;
+import net.geant.nmaas.portal.api.domain.DomainGroupView;
 import net.geant.nmaas.portal.api.domain.DomainRequest;
 import net.geant.nmaas.portal.api.domain.DomainTechDetailsView;
 import net.geant.nmaas.portal.api.domain.UserView;
+import net.geant.nmaas.portal.api.domain.UserViewMinimal;
 import net.geant.nmaas.portal.api.exception.ProcessingException;
 import net.geant.nmaas.portal.events.DomainCreatedEvent;
 import net.geant.nmaas.portal.persistent.entity.ApplicationBase;
@@ -488,7 +491,108 @@ public class DomainServiceTest {
         assertEquals(0, domainGroup.getApplicationStatePerDomain().size());
         assertEquals(0, domainGroup2.getApplicationStatePerDomain().size());
 
+    }
+
+    @Test
+    void shouldChangeMembersFromGroupView() {
+        DomainBase domain1 = new DomainBase();
+        domain1.setId(1L);
+        domain1.setName("dom1");
+        domain1.setCodename("dom1");
+        Domain domain = new Domain(1L,"dom1", "dom1");
+
+        when(domainRepository.findById(1L)).thenReturn(Optional.of(domain));
+        when(userService.findById(1L)).thenReturn(Optional.of(new User("test")));
+        when(userService.findById(2L)).thenReturn(Optional.of(new User("test2")));
+
+        UserViewMinimal userView = new UserViewMinimal();
+        userView.setId(1L);
+
+        UserViewMinimal userView2 = new UserViewMinimal();
+        userView2.setId(2L);
+
+        User user = new User("user");
+
+
+        DomainGroupView domainGroupView = new DomainGroupView(1l, "test", "test1",List.of(domain1), null, List.of(userView) );
+        DomainGroup domainGroup = new DomainGroup(1l, "test", "test1");
+        domainGroup.setManagers(List.of(user));
+
+        when(domainGroupRepository.findById(1L)).thenReturn(Optional.of(domainGroup));
+
+
+        DomainGroupView result = domainService.updateMembers(List.of(userView2), domainGroupView );
+
+        assertEquals(1, result.getManagers().size());
+        assertEquals(2L , result.getManagers().get(0).getId());
+
+
+    }
+
+    @Test
+    void shouldAddMembersFromGroupView() {
+        DomainBase domain1 = new DomainBase();
+        domain1.setId(1L);
+        domain1.setName("dom1");
+        domain1.setCodename("dom1");
+        Domain domain = new Domain(1L,"dom1", "dom1");
+
+        when(domainRepository.findById(1L)).thenReturn(Optional.of(domain));
+        when(userService.findById(1L)).thenReturn(Optional.of(new User("test")));
+        when(userService.findById(2L)).thenReturn(Optional.of(new User("test2")));
+
+        UserViewMinimal userView = new UserViewMinimal();
+        userView.setId(1L);
+
+        UserViewMinimal userView2 = new UserViewMinimal();
+        userView2.setId(2L);
+
+        User user = new User("user");
+
+
+        DomainGroupView domainGroupView = new DomainGroupView(1l, "test", "test1",List.of(domain1), null, List.of(userView) );
+        DomainGroup domainGroup = new DomainGroup(1l, "test", "test1");
+        domainGroup.setManagers(List.of(user));
+
+        when(domainGroupRepository.findById(1L)).thenReturn(Optional.of(domainGroup));
+
+
+        DomainGroupView result = domainService.updateMembers(List.of(userView2, userView), domainGroupView );
+
+        assertEquals(2, result.getManagers().size());
+    }
+
+    @Test
+    void shouldDeleteMembersFromGroupView() {
+        DomainBase domain1 = new DomainBase();
+        domain1.setId(1L);
+        domain1.setName("dom1");
+        domain1.setCodename("dom1");
+        Domain domain = new Domain(1L,"dom1", "dom1");
+
+        when(domainRepository.findById(1L)).thenReturn(Optional.of(domain));
+        when(userService.findById(1L)).thenReturn(Optional.of(new User("test")));
+        when(userService.findById(2L)).thenReturn(Optional.of(new User("test2")));
+
+        UserViewMinimal userView = new UserViewMinimal();
+        userView.setId(1L);
+
+        UserViewMinimal userView2 = new UserViewMinimal();
+        userView2.setId(2L);
+
+        User user = new User("user");
+
+
+        DomainGroupView domainGroupView = new DomainGroupView(1l, "test", "test1",List.of(domain1), null, List.of(userView) );
+        DomainGroup domainGroup = new DomainGroup(1l, "test", "test1");
+        domainGroup.setManagers(List.of(user));
+
+        when(domainGroupRepository.findById(1L)).thenReturn(Optional.of(domainGroup));
+
+
+        DomainGroupView result = domainService.updateMembers(List.of(), domainGroupView );
 
+        assertEquals(0, result.getManagers().size());
     }
 
 }
-- 
GitLab


From 4834295a874fb379104c7475af59888eba2e31c6 Mon Sep 17 00:00:00 2001
From: llopat <llopat@man.poznan.pl>
Date: Thu, 31 Oct 2024 14:36:58 +0100
Subject: [PATCH 48/50] Minor updates in tests

---
 .../impl/InternationalizationServiceImpl.java |  1 -
 .../impl/ApplicationBaseServiceTest.java      |  4 +-
 .../service/impl/DomainServiceTest.java       | 39 +++++--------
 .../impl/InternationalizationServiceTest.java | 58 +++++++++----------
 .../service/impl/SSHKeyServiceTest.java       | 29 +++++-----
 5 files changed, 57 insertions(+), 74 deletions(-)

diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceImpl.java
index 9b76a37eb..bd27ed1ff 100644
--- a/src/main/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceImpl.java
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceImpl.java
@@ -5,7 +5,6 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import net.geant.nmaas.portal.api.i18n.api.InternationalizationBriefView;
 import net.geant.nmaas.portal.api.i18n.api.InternationalizationView;
-import net.geant.nmaas.portal.exceptions.DataConflictException;
 import net.geant.nmaas.portal.persistent.entity.InternationalizationSimple;
 import net.geant.nmaas.portal.persistent.repositories.InternationalizationSimpleRepository;
 import net.geant.nmaas.portal.service.ConfigurationManager;
diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceTest.java
index 30f8865e9..e62ab1503 100644
--- a/src/test/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceTest.java
+++ b/src/test/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceTest.java
@@ -33,7 +33,7 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 
-public class ApplicationBaseServiceTest {
+class ApplicationBaseServiceTest {
 
     private final ApplicationBase applicationBase1 = new ApplicationBase("name");
     private final ApplicationBase applicationBase2 = new ApplicationBase(2L, "another");
@@ -68,7 +68,7 @@ public class ApplicationBaseServiceTest {
 
         verify(appBaseRepo, times(1)).save(any());
         assertEquals(applicationBase1.getName(), result.getName());
-        assertThat(result.getVersions()).hasSize(0);
+        assertThat(result.getVersions()).isEmpty();
     }
 
     @Test
diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java
index f0cbfabbf..c997d0800 100644
--- a/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java
+++ b/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java
@@ -1,6 +1,5 @@
 package net.geant.nmaas.portal.service.impl;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import net.geant.nmaas.dcn.deployment.DcnDeploymentType;
 import net.geant.nmaas.dcn.deployment.DcnRepositoryManager;
@@ -58,7 +57,7 @@ import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 import static org.mockito.internal.verification.VerificationModeFactory.times;
 
-public class DomainServiceTest {
+class DomainServiceTest {
 
     DomainServiceImpl.CodenameValidator validator;
     DomainServiceImpl.CodenameValidator namespaceValidator;
@@ -249,7 +248,9 @@ public class DomainServiceTest {
         when(userRoleRepo.findByDomainAndUserAndRole(domain, user, role)).thenReturn(null);
         when(userService.findById(userId)).thenReturn(Optional.of(user));
         when(domainRepository.findById(domainId)).thenReturn(Optional.of(domain));
-        this.domainService.addMemberRole(domainId, userId, role);
+
+        domainService.addMemberRole(domainId, userId, role);
+
         verify(userRoleRepo, times(1)).save(any());
     }
 
@@ -382,12 +383,12 @@ public class DomainServiceTest {
         Domain domain = new Domain(1L, "testdom", "testdom");
 
         User user1 = new User("user1");
-        user1.setRoles(ImmutableList.of(new UserRole(user1, domain, Role.ROLE_DOMAIN_ADMIN), new UserRole(user1, domain, Role.ROLE_OPERATOR)));
+        user1.setRoles(List.of(new UserRole(user1, domain, Role.ROLE_DOMAIN_ADMIN), new UserRole(user1, domain, Role.ROLE_OPERATOR)));
 
         User user2 = new User("user2");
-        user2.setRoles(ImmutableList.of(new UserRole(user2, domain, Role.ROLE_DOMAIN_ADMIN)));
+        user2.setRoles(List.of(new UserRole(user2, domain, Role.ROLE_DOMAIN_ADMIN)));
 
-        List<User> users = ImmutableList.of(user1, user2);
+        List<User> users = List.of(user1, user2);
         when(userRoleRepo.findDomainMembers(anyString())).thenReturn(users);
         List<UserView> filteredUsers = domainService.findUsersWithDomainAdminRole(domain.getCodename());
         assertThat("Result mismatch", filteredUsers.size() == 2);
@@ -490,7 +491,6 @@ public class DomainServiceTest {
 
         assertEquals(0, domainGroup.getApplicationStatePerDomain().size());
         assertEquals(0, domainGroup2.getApplicationStatePerDomain().size());
-
     }
 
     @Test
@@ -510,23 +510,18 @@ public class DomainServiceTest {
 
         UserViewMinimal userView2 = new UserViewMinimal();
         userView2.setId(2L);
+        DomainGroupView domainGroupView = new DomainGroupView(1L, "test", "test1", List.of(domain1), null, List.of(userView) );
 
         User user = new User("user");
-
-
-        DomainGroupView domainGroupView = new DomainGroupView(1l, "test", "test1",List.of(domain1), null, List.of(userView) );
-        DomainGroup domainGroup = new DomainGroup(1l, "test", "test1");
+        DomainGroup domainGroup = new DomainGroup(1L, "test", "test1");
         domainGroup.setManagers(List.of(user));
 
         when(domainGroupRepository.findById(1L)).thenReturn(Optional.of(domainGroup));
 
-
         DomainGroupView result = domainService.updateMembers(List.of(userView2), domainGroupView );
 
         assertEquals(1, result.getManagers().size());
         assertEquals(2L , result.getManagers().get(0).getId());
-
-
     }
 
     @Test
@@ -547,16 +542,12 @@ public class DomainServiceTest {
         UserViewMinimal userView2 = new UserViewMinimal();
         userView2.setId(2L);
 
+        DomainGroupView domainGroupView = new DomainGroupView(1L, "test", "test1",List.of(domain1), null, List.of(userView) );
         User user = new User("user");
-
-
-        DomainGroupView domainGroupView = new DomainGroupView(1l, "test", "test1",List.of(domain1), null, List.of(userView) );
-        DomainGroup domainGroup = new DomainGroup(1l, "test", "test1");
+        DomainGroup domainGroup = new DomainGroup(1L, "test", "test1");
         domainGroup.setManagers(List.of(user));
-
         when(domainGroupRepository.findById(1L)).thenReturn(Optional.of(domainGroup));
 
-
         DomainGroupView result = domainService.updateMembers(List.of(userView2, userView), domainGroupView );
 
         assertEquals(2, result.getManagers().size());
@@ -580,16 +571,12 @@ public class DomainServiceTest {
         UserViewMinimal userView2 = new UserViewMinimal();
         userView2.setId(2L);
 
+        DomainGroupView domainGroupView = new DomainGroupView(1L, "test", "test1",List.of(domain1), null, List.of(userView) );
         User user = new User("user");
-
-
-        DomainGroupView domainGroupView = new DomainGroupView(1l, "test", "test1",List.of(domain1), null, List.of(userView) );
-        DomainGroup domainGroup = new DomainGroup(1l, "test", "test1");
+        DomainGroup domainGroup = new DomainGroup(1L, "test", "test1");
         domainGroup.setManagers(List.of(user));
-
         when(domainGroupRepository.findById(1L)).thenReturn(Optional.of(domainGroup));
 
-
         DomainGroupView result = domainService.updateMembers(List.of(), domainGroupView );
 
         assertEquals(0, result.getManagers().size());
diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceTest.java
index 8306ed1ba..d92954444 100644
--- a/src/test/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceTest.java
+++ b/src/test/java/net/geant/nmaas/portal/service/impl/InternationalizationServiceTest.java
@@ -26,37 +26,34 @@ import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-public class InternationalizationServiceTest {
+class InternationalizationServiceTest {
 
-    private InternationalizationSimpleRepository repository = mock(InternationalizationSimpleRepository.class);
-
-    private ConfigurationManager configurationManager = mock(ConfigurationManager.class);
-
-    private ModelMapper modelMapper = new ModelMapper();
+    private final InternationalizationSimpleRepository repository = mock(InternationalizationSimpleRepository.class);
+    private final ConfigurationManager configurationManager = mock(ConfigurationManager.class);
+    private final ModelMapper modelMapper = new ModelMapper();
 
     private InternationalizationService internationalizationService;
-
     private InternationalizationView language;
 
     @BeforeEach
-    public void setup(){
+    void setup(){
         this.internationalizationService = new InternationalizationServiceImpl(repository, configurationManager, modelMapper);
         this.language = new InternationalizationView("pl", true, "{}");
     }
 
     @Test
-    public void shouldSaveLanguageContent(){
+    void shouldSaveLanguageContent() {
         internationalizationService.addNewLanguage(this.language, false);
         verify(repository, times(1)).save(any());
     }
 
     @Test
-    public void shouldNotSaveNullRequest(){
+    void shouldNotSaveNullRequest() {
         assertThrows(IllegalArgumentException.class, ()-> internationalizationService.addNewLanguage(null, false));
     }
 
     @Test
-    public void shouldNotSaveWithEmptyLanguageId(){
+    void shouldNotSaveWithEmptyLanguageId() {
         assertThrows(IllegalArgumentException.class, ()-> {
             this.language.setLanguage("");
             internationalizationService.addNewLanguage(this.language, false);
@@ -64,7 +61,7 @@ public class InternationalizationServiceTest {
     }
 
     @Test
-    public void shouldNotSaveWithEmptyContent(){
+    void shouldNotSaveWithEmptyContent() {
         assertThrows(IllegalArgumentException.class, ()-> {
             this.language.setContent("");
             internationalizationService.addNewLanguage(this.language, false);
@@ -72,15 +69,15 @@ public class InternationalizationServiceTest {
     }
 
     @Test
-    public void shouldNotSaveWithInvalidJsonContent(){
-        assertThrows(IllegalArgumentException.class, ()-> {
+    void shouldNotSaveWithInvalidJsonContent() {
+        assertThrows(IllegalArgumentException.class, () -> {
             this.language.setContent("{invalid]");
             internationalizationService.addNewLanguage(this.language, false);
         });
     }
 
     @Test
-    public void shouldGetAllSupportedLanguages(){
+    void shouldGetAllSupportedLanguages() {
         when(repository.findAll()).thenReturn(Collections.singletonList(new InternationalizationView( "pl", true, "{\"test\":\"content\"}").getAsInternationalizationSimple()));
         List<InternationalizationBriefView> languageList = internationalizationService.getAllSupportedLanguages();
         assertEquals(1, languageList.size());
@@ -89,14 +86,14 @@ public class InternationalizationServiceTest {
     }
 
     @Test
-    public void shouldReturnEmptyList(){
+    void shouldReturnEmptyList() {
         when(repository.findAll()).thenReturn(Collections.emptyList());
         List<InternationalizationBriefView> languageList = internationalizationService.getAllSupportedLanguages();
         assertTrue(languageList.isEmpty());
     }
 
     @Test
-    public void shouldChangeLanguageState(){
+    void shouldChangeLanguageState() {
         when(configurationManager.getConfiguration()).thenReturn(new ConfigurationView(false, false, "fr", false, false, new ArrayList<>(), false, false));
         InternationalizationView internationalization = new InternationalizationView("pl", false, "{\"test\":\"content\"}");
         when(repository.findByLanguageOrderByIdDesc(language.getLanguage())).thenReturn(Optional.of(internationalization.getAsInternationalizationSimple()));
@@ -105,7 +102,7 @@ public class InternationalizationServiceTest {
     }
 
     @Test
-    public void shouldThrowAnExceptionWhenLanguageIsNotFound(){
+    void shouldThrowAnExceptionWhenLanguageIsNotFound() {
         assertThrows(IllegalArgumentException.class, () -> {
             when(repository.findByLanguageOrderByIdDesc(language.getLanguage())).thenReturn(Optional.empty());
             internationalizationService.changeLanguageState(language);
@@ -113,7 +110,7 @@ public class InternationalizationServiceTest {
     }
 
     @Test
-    public void shouldThrowAnExceptionWhenDisablingDefaultLanguage(){
+    void shouldThrowAnExceptionWhenDisablingDefaultLanguage() {
         assertThrows(IllegalStateException.class, () -> {
             InternationalizationView internationalization = new InternationalizationView("pl", false, "{\"test\":\"content\"}");
             when(repository.findByLanguageOrderByIdDesc(language.getLanguage())).thenReturn(Optional.of(internationalization.getAsInternationalizationSimple()));
@@ -123,14 +120,14 @@ public class InternationalizationServiceTest {
     }
 
     @Test
-    public void shouldGetLanguageContent(){
+    void shouldGetLanguageContent() {
         InternationalizationView internationalization = new InternationalizationView("pl", true, "{\"test\":\"content\"}");
         when(repository.findByLanguageOrderByIdDesc("pl")).thenReturn(Optional.of(internationalization.getAsInternationalizationSimple()));
         assertEquals(internationalization.getContent(), internationalizationService.getLanguageContent("pl"));
     }
 
     @Test
-    public void shouldThrowAnExceptionWhenLanguageIsNotAvailable(){
+    void shouldThrowAnExceptionWhenLanguageIsNotAvailable() {
         assertThrows(IllegalStateException.class, () -> {
             when(repository.findByLanguageOrderByIdDesc(any())).thenReturn(Optional.empty());
             internationalizationService.getLanguageContent("pl");
@@ -138,7 +135,7 @@ public class InternationalizationServiceTest {
     }
 
     @Test
-    public void shouldReturnEnabledLanguages(){
+    void shouldReturnEnabledLanguages() {
         when(repository.findAll()).thenReturn(Collections.singletonList(new InternationalizationView("pl", true, "{\"test\":\"content\"}").getAsInternationalizationSimple()));
         List<String> result = this.internationalizationService.getEnabledLanguages();
         assertEquals(1, result.size());
@@ -146,46 +143,45 @@ public class InternationalizationServiceTest {
     }
 
     @Test
-    public void shouldReturnEmptyListWhenAllLanguagesDisabled(){
+    void shouldReturnEmptyListWhenAllLanguagesDisabled() {
         when(repository.findAll()).thenReturn(Collections.singletonList(new InternationalizationView("pl", false, "{\"test\":\"content\"}").getAsInternationalizationSimple()));
         List<String> result = this.internationalizationService.getEnabledLanguages();
         assertEquals(0, result.size());
     }
 
     @Test
-    void shouldUpdateLanguage(){
+    void shouldUpdateLanguage() {
         when(repository.findByLanguageOrderByIdDesc(anyString())).thenReturn(Optional.of(new InternationalizationView("pl", true, "{\"test\":\"content\"}").getAsInternationalizationSimple()));
         this.internationalizationService.updateLanguage("pl", "{\"test\":\"new-content\"}");
         verify(repository, times(1)).save(any());
     }
 
     @Test
-    void shouldNotUpdateLanguageWhenLangIsEmpty(){
+    void shouldNotUpdateLanguageWhenLangIsEmpty() {
         when(repository.findByLanguageOrderByIdDesc(anyString())).thenReturn(Optional.of(new InternationalizationView("pl", true, "{\"test\":\"content\"}").getAsInternationalizationSimple()));
         assertThrows(IllegalArgumentException.class, () -> this.internationalizationService.updateLanguage("", "{\"test\":\"new-content\"}"));
-;
     }
 
     @Test
-    void shouldNotUpdateLanguageWhenContentIsEmpty(){
+    void shouldNotUpdateLanguageWhenContentIsEmpty() {
         when(repository.findByLanguageOrderByIdDesc(anyString())).thenReturn(Optional.of(new InternationalizationView("pl", true, "{\"test\":\"content\"}").getAsInternationalizationSimple()));
         assertThrows(IllegalArgumentException.class, () -> this.internationalizationService.updateLanguage("pl", ""));
     }
 
     @Test
-    void shouldNotUpdateLanguageWhenContentIsNotValidJson(){
+    void shouldNotUpdateLanguageWhenContentIsNotValidJson() {
         when(repository.findByLanguageOrderByIdDesc(anyString())).thenReturn(Optional.of(new InternationalizationView("pl", true, "{\"test\":\"content\"}").getAsInternationalizationSimple()));
         assertThrows(IllegalArgumentException.class, () -> this.internationalizationService.updateLanguage("pl", "{\"test\":\"new-content\""));
     }
 
     @Test
-    void shouldNotUpdateLanguageWhenLangIsNotFound(){
+    void shouldNotUpdateLanguageWhenLangIsNotFound() {
         when(repository.findByLanguageOrderByIdDesc(anyString())).thenReturn(Optional.empty());
         assertThrows(IllegalArgumentException.class, () -> this.internationalizationService.updateLanguage("pl", "{\"test\":\"new-content\"}"));
     }
 
     @Test
-    void shouldGetLanguage(){
+    void shouldGetLanguage() {
         when(repository.findByLanguageOrderByIdDesc(anyString())).thenReturn(Optional.of(new InternationalizationView("pl", true, "{\"test\":\"content\"}").getAsInternationalizationSimple()));
         InternationalizationView langView = internationalizationService.getLanguage("pl");
         assertEquals("pl", langView.getLanguage());
@@ -193,7 +189,7 @@ public class InternationalizationServiceTest {
     }
 
     @Test
-    void shouldNotGetLanguageWhenLangIsNotExists(){
+    void shouldNotGetLanguageWhenLangIsNotExists() {
         when(repository.findByLanguageOrderByIdDesc(anyString())).thenReturn(Optional.empty());
         assertThrows(IllegalArgumentException.class, () -> this.internationalizationService.getLanguage("pl"));
     }
diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/SSHKeyServiceTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/SSHKeyServiceTest.java
index 09ff0e37d..fbf9baf42 100644
--- a/src/test/java/net/geant/nmaas/portal/service/impl/SSHKeyServiceTest.java
+++ b/src/test/java/net/geant/nmaas/portal/service/impl/SSHKeyServiceTest.java
@@ -6,8 +6,8 @@ import net.geant.nmaas.portal.persistent.entity.SSHKeyEntity;
 import net.geant.nmaas.portal.persistent.entity.User;
 import net.geant.nmaas.portal.persistent.repositories.SSHKeyRepository;
 import net.geant.nmaas.portal.service.SSHKeyService;
-import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 import org.springframework.context.ApplicationEventPublisher;
 
 import java.util.ArrayList;
@@ -17,20 +17,22 @@ import java.util.Optional;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
-public class SSHKeyServiceTest {
+class SSHKeyServiceTest {
 
     private final SSHKeyRepository repository = mock(SSHKeyRepository.class);
-
     private final ApplicationEventPublisher eventPublisher = mock(ApplicationEventPublisher.class);
 
     private SSHKeyService sut;
-
     private User owner;
 
     @BeforeEach
-    public void setup() {
+    void setup() {
         this.owner = new User("owner");
         this.owner.setId(1L);
         SSHKeyEntity key = new SSHKeyEntity(owner, "name", "key long long long");
@@ -44,7 +46,7 @@ public class SSHKeyServiceTest {
     }
 
     @Test
-    public void ShouldReturnAllKeysForUser() {
+    void ShouldReturnAllKeysForUser() {
         List<SSHKeyView> result = this.sut.findAllByUser(this.owner);
 
         assertEquals(1, result.size());
@@ -52,15 +54,14 @@ public class SSHKeyServiceTest {
     }
 
     @Test
-    public void shouldDeleteKeyIfValidRequest() {
-
+    void shouldDeleteKeyIfValidRequest() {
         this.sut.invalidate(this.owner, 1L);
 
         verify(this.repository, times(1)).deleteById(1L);
     }
 
     @Test
-    public void shouldThrowExceptionWhenKeyDoesNotExist() {
+    void shouldThrowExceptionWhenKeyDoesNotExist() {
         IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> {
             this.sut.invalidate(this.owner, 2L);
         });
@@ -69,7 +70,7 @@ public class SSHKeyServiceTest {
     }
 
     @Test
-    public void shouldThrowExceptionWhenKeyDoesNotBelongToTheUser() {
+    void shouldThrowExceptionWhenKeyDoesNotBelongToTheUser() {
         User anonymous = new User("anonymous");
         anonymous.setId(31L);
         IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> {
@@ -77,11 +78,10 @@ public class SSHKeyServiceTest {
         });
 
         assertEquals("Invalid key owner", e.getMessage());
-
     }
 
     @Test
-    public void shouldCreateNewSSHKeyEntityWhenNameValid() {
+    void shouldCreateNewSSHKeyEntityWhenNameValid() {
         when(repository.existsByOwnerAndName(this.owner, "new key")).thenReturn(false);
         SSHKeyRequest request = new SSHKeyRequest("new key", "so long key");
         SSHKeyEntity res = new SSHKeyEntity(owner, "new key", "so long key");
@@ -96,7 +96,7 @@ public class SSHKeyServiceTest {
     }
 
     @Test
-    public void shouldThrowExceptionWhenNameIsNotUnique() {
+    void shouldThrowExceptionWhenNameIsNotUnique() {
         when(repository.existsByOwnerAndName(this.owner, "name")).thenReturn(true);
         SSHKeyRequest request = new SSHKeyRequest("name", "so long key");
 
@@ -106,4 +106,5 @@ public class SSHKeyServiceTest {
 
         assertEquals("Name is already taken", e.getMessage());
     }
+
 }
-- 
GitLab


From 33a21813acb5df3c874fa1fd30f406aa91d7c2bb Mon Sep 17 00:00:00 2001
From: llopat <llopat@man.poznan.pl>
Date: Thu, 31 Oct 2024 14:37:46 +0100
Subject: [PATCH 49/50] Fixed sonar coverage

---
 .gitlab-ci.yml | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a65ff76ac..f59a90d23 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -12,6 +12,18 @@ test:
     - chmod +x ./gradlew
     - ./gradlew -Dorg.gradle.daemon=false test
 
+itest:
+  stage: test
+  image: openjdk:11-jdk-slim
+  tags:
+    - docker
+  only:
+    - develop
+    - /^release/
+  script:
+    - chmod +x ./gradlew
+    - ./gradlew integrationTest jacocoTestCoverageVerification
+
 sonar:
   stage: sonar
   image: openjdk:17-jdk-slim
-- 
GitLab


From df228debdbd681fd6c9fd73c4b762ffcbbd11cb3 Mon Sep 17 00:00:00 2001
From: kbeyro <121854496+kbeyro@users.noreply.github.com>
Date: Thu, 19 Dec 2024 12:49:28 +0100
Subject: [PATCH 50/50] fix entry state processing

---
 .../portal/service/impl/BulkApplicationServiceImpl.java  | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

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 d2b6a4111..f57db19e3 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
@@ -342,9 +342,12 @@ public class BulkApplicationServiceImpl implements BulkApplicationService {
 
     @Override
     public void setBulkEntryToProcessing(Long bulkEntryId) {
-        BulkDeploymentEntry entry = bulkDeploymentEntryRepository.getReferenceById(bulkEntryId);
-        entry.setState(BulkDeploymentState.PROCESSING);
-        bulkDeploymentEntryRepository.save(entry);
+        Optional<BulkDeploymentEntry> entry = bulkDeploymentEntryRepository.findById(bulkEntryId);
+        if(entry.isPresent()) {
+            BulkDeploymentEntry ent = entry.get();
+            ent.setState(BulkDeploymentState.PROCESSING);
+            bulkDeploymentEntryRepository.save(ent);
+        }
     }
 
     @Override
-- 
GitLab