diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a65ff76acacf6388c7b6d5f71304977b3fa81a94..f59a90d239a8c030b2eec0561c5a26b719cf4e20 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
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 46ff082c69def2a9049a06cde922e83a562b46bb..16867a493aa6aba9e7530a98c65ebb98737f6de4 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/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 0000000000000000000000000000000000000000..fd403805673a5576f1b2b83d4585dab6a8feb50c
--- /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 f2f9b6b7ffaa5b3e0f43fbd100b2a2cc08fe3bd6..2610b3835da4de89dfd98af32cea7d318f198547 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/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceIntTest.java b/src/integrationTest/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceIntTest.java
index 5448d3102f6dc6e34acbb891744c41bc5544e616..9f3472bb31d66fca6069683d696c8dbfc0d24262 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/integrationTest/resources/application.properties b/src/integrationTest/resources/application.properties
index c11717acbcc34cda752d6e0fe39cf003c3ad5e0a..3c84a841647ce27a53b71534a60d0499f11ca22c 100644
--- a/src/integrationTest/resources/application.properties
+++ b/src/integrationTest/resources/application.properties
@@ -161,3 +161,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/MainConfig.java b/src/main/java/net/geant/nmaas/MainConfig.java
index 925bf1973c9d12fb69ad6b43cf55e652ca25c9bd..dfdb09578723b604d901a258d7c8fe45d4748659 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/notifications/templates/TemplateController.java b/src/main/java/net/geant/nmaas/notifications/templates/TemplateController.java
index eb7360265f043c4d7183f1fb819d1bf118241c3e..9483df6b814e2cf99cf4c76e7ebdc0bb7e2a0183 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 bd5e379074d368442afbaee550026533fdb6ba15..8f8cb8d92efd736aeeb4a8b7a837d172780e1d0e 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;
@@ -24,6 +25,7 @@ import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
+
 @Service
 public class TemplateService {
 
@@ -34,26 +36,26 @@ 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){
+    public MailTemplateView getMailTemplate(MailType mailType) {
         MailTemplate mailTemplate = repository.findByMailType(mailType).orElseThrow(() -> new IllegalArgumentException("Mail template not found"));
         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()),"Mail template already exists");
+        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,11 +71,11 @@ 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);
     }
 
-    void updateHTMLTemplate(MultipartFile file){
+    void updateHTMLTemplate(MultipartFile file) {
         FileInfo fileInfo = getHTMLTemplateFileInfo();
         if (fileStorageService.remove(fileInfo)) {
             storeHTMLTemplate(file);
@@ -93,4 +95,10 @@ public class TemplateService {
         throw new IllegalArgumentException(String.format("Exactly one html template supported (actually got %d)", template.size()));
     }
 
+    private static void checkArgumentConflict(boolean expression, String errorMessage) {
+        if (!expression) {
+            throw new DataConflictException(String.valueOf(errorMessage));
+        }
+    }
+
 }
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 196e841f29e8657362b527dc2a172d3b7faaae50..55172bac6d5d87469745f92f3d22f29e69732c33 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 06e47911bcee234f21b41bf257f6e3a4be06f91f..21426f5f5f09d240b3fe1b6ac0bc6d37589663fe 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/orchestration/entities/AppDeploymentState.java b/src/main/java/net/geant/nmaas/orchestration/entities/AppDeploymentState.java
index c2e1582545c79b3cc0530b5741f841a806405754..b35e07d6321a576a04040fb6928e7bd8cd4c0c96 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
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 6b8ba758c3a7825d5567610736b9729abcb7fce4..a709fbf62344c7da55cc57f74c52919778571c07 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
@@ -117,7 +117,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);
@@ -172,7 +172,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) {
@@ -203,9 +203,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);
@@ -215,14 +215,13 @@ public class BulkController {
     private BulkDeploymentView mapToView(BulkDeployment deployment) {
         BulkDeploymentView bulkView = modelMapper.map(deployment, BulkDeploymentView.class);
         try {
-            bulkView.setCreator(getUserView(deployment.getCreatorId()));
-        } catch (Exception ex) {
-            log.error("Can not find creator for {} - creatorId:  {}", deployment.getId(), deployment.getCreatorId());
+            bulkView.setCreator(getUserView(deployment.getCreator().getId()));
             return null;
+        } catch (Exception ex) {
+            log.error("Can not find creator for {} - creatorId:  {}", deployment.getId(), deployment.getCreator().getId());
         }
         mapDetails(deployment, bulkView);
         return bulkView;
-
     }
 
     private void mapDetails(BulkDeployment deployment, BulkDeploymentViewS view) {
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 b15cbed12ac66b8d91c60499213fb100cfe58f29..2fb0cca6e9508f36c42d2f27eca995f85188be97 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/api/market/DomainController.java b/src/main/java/net/geant/nmaas/portal/api/market/DomainController.java
index 382d56837ed860706f18722698073f05f0d90018..36707803c8f01913792daaa58f1c4850e5756b80 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/api/security/CustomAccessTokenController.java b/src/main/java/net/geant/nmaas/portal/api/security/CustomAccessTokenController.java
new file mode 100644
index 0000000000000000000000000000000000000000..66839fd385cb64d9b4750081062a1813a348a2de
--- /dev/null
+++ b/src/main/java/net/geant/nmaas/portal/api/security/CustomAccessTokenController.java
@@ -0,0 +1,52 @@
+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.RequestBody;
+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, @RequestBody String name) {
+        User user = getUser(principal);
+        return accessTokenService.createToken(user.getId(), name);
+    }
+
+    @PutMapping("/{id}")
+    public void invalidateToken(@PathVariable Long id) {
+        accessTokenService.invalidate(id);
+    }
+
+    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/exceptions/DataConflictException.java b/src/main/java/net/geant/nmaas/portal/exceptions/DataConflictException.java
new file mode 100644
index 0000000000000000000000000000000000000000..aadb9107d38765b83ff31410ef3ce3a42f346aa8
--- /dev/null
+++ b/src/main/java/net/geant/nmaas/portal/exceptions/DataConflictException.java
@@ -0,0 +1,12 @@
+package net.geant.nmaas.portal.exceptions;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class DataConflictException extends RuntimeException {
+
+    public DataConflictException(String message) {
+        super(message);
+    }
+
+}
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 0000000000000000000000000000000000000000..80c117b2a178e8aee330990a737638c0fdc42960
--- /dev/null
+++ b/src/main/java/net/geant/nmaas/portal/persistent/entity/AccessToken.java
@@ -0,0 +1,33 @@
+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 = "access_tokens")
+@NoArgsConstructor
+@Getter
+@Setter
+@AllArgsConstructor
+public class AccessToken {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    private String name;
+
+    private Long userId;
+
+    private String tokenValue;
+
+    private boolean valid;
+}
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 9d3357fc97603d12cd1fda89f5b63e52c52d3e6c..06a9a0b9fb7e4a0b0410b2d97971f33d84fb96c3 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/persistent/repositories/AccessTokenRepository.java b/src/main/java/net/geant/nmaas/portal/persistent/repositories/AccessTokenRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..7655ad0afff9c210f50cbf7c843ce22cf59fa7eb
--- /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/persistent/repositories/ApplicationBaseRepository.java b/src/main/java/net/geant/nmaas/portal/persistent/repositories/ApplicationBaseRepository.java
index f5398bb34523a769b2d28c4ea32363d345fda0e8..b4b89368be342af33ea85fe59099361792a5e927 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;
@@ -24,7 +25,8 @@ 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)
+    @Cacheable("applicationBaseS")
+    @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/BulkApplicationService.java b/src/main/java/net/geant/nmaas/portal/service/BulkApplicationService.java
index 3d3a3d7c472afe3a743cda549713c5fa235439e7..1edd067a0359bdb06ee1d218c52cd3355d683ebd 100644
--- a/src/main/java/net/geant/nmaas/portal/service/BulkApplicationService.java
+++ b/src/main/java/net/geant/nmaas/portal/service/BulkApplicationService.java
@@ -25,7 +25,7 @@ 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/CustomAccessTokenService.java b/src/main/java/net/geant/nmaas/portal/service/CustomAccessTokenService.java
new file mode 100644
index 0000000000000000000000000000000000000000..96e59e1eb7a6960580be1e8a2d86095363fa7cf2
--- /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, String name);
+    List<AccessToken> getAll(Long userId);
+
+}
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 70ff8d5da09e214176c0adb66cf87f280f79731b..a26f3b60c534c62f8ba3314438c281bcf5a1fc60 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/InternationalizationService.java b/src/main/java/net/geant/nmaas/portal/service/InternationalizationService.java
index 66a3e9a37021f8464d4eb8a6f776efab8dbcaf37..7806750f2a26ee791e1d5a6d2812018652850608 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/ApplicationBaseServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java
index d752b89635bc0e9cd666c077853de652fd1f0632..c0b95f5da87185627dd04fd29b20163599cc5117 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()
@@ -141,8 +147,6 @@ public class ApplicationBaseServiceImpl implements ApplicationBaseService {
         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());
@@ -172,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 47603ff775d7ca1f491fd82d279e6febf592d496..737e059dcf33b1fc2cb176b8f00bb6497dea64bb 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.");
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 d2b6a4111f0009d004dd26d7b0eb1f1bdb49d5a5..87cae098abd5a6e1d6ba6994883b7d3b31ae6516 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;
@@ -342,9 +344,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
@@ -429,11 +434,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 5c931595007185038b5d37254619a2b61a648870..fa2512fbd3fe755c2df5ccee00271126c5759ecb 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/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 0000000000000000000000000000000000000000..58bea7e30a7400dfc9ec715effd725f4f1cf8858
--- /dev/null
+++ b/src/main/java/net/geant/nmaas/portal/service/impl/CustomAccessTokenServiceImpl.java
@@ -0,0 +1,56 @@
+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, String name) {
+        AccessToken token = createNewToken(userId, name);
+        return accessTokenRepository.save(token);
+    }
+
+    @Override
+    public List<AccessToken> getAll(Long userId) {
+        return accessTokenRepository.findAllByUserId(userId);
+    }
+
+    private AccessToken createNewToken(Long userId, String name) {
+        AccessToken token = new AccessToken();
+        token.setName(name);
+        token.setUserId(userId);
+        token.setTokenValue(generateToken());
+        token.setValid(true);
+        return token;
+        }
+
+    private String generateToken() {
+        // uuid is a placeholder for now
+        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/main/java/net/geant/nmaas/portal/service/impl/DomainServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/DomainServiceImpl.java
index 0caa0356871627bb0839f0bdf10545168c9701b8..98c8a5391361349d7cd86beae422d226388ce29e 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
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 b114ad877816d0a6359799e971fbbd1406f3598b..bd27ed1ff631d49dc280083c18b5c336845246aa 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,6 +2,7 @@ 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.persistent.entity.InternationalizationSimple;
@@ -13,12 +14,16 @@ 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;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 @Service
 @RequiredArgsConstructor
+@Slf4j
 public class InternationalizationServiceImpl implements InternationalizationService {
 
     private final InternationalizationSimpleRepository repository;
@@ -26,9 +31,35 @@ 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 NotFoundException("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 checkRequest(InternationalizationView newLanguage) {
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 0000000000000000000000000000000000000000..999454960500d4bfcdc76b137adea5ee0929f469
--- /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
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 0000000000000000000000000000000000000000..df08df98f4ec98b66c4275a4c05768b983a38006
--- /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
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 ae36fb831a73d0052071e8cc385f4e68149d726a..02b79cdc8924ada64c050c8cf66a3973f52d8afc 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;
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 2f78bed75fd4ee132402c501f071b532c37798cd..550c228e702f2e0124e3efc5f6276a9a5c33237a 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));
     }
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 55fe03f4842dd9763a5c08197b76685d5ab07553..02fa1407e0eaa2e1cff6860a3340007a7ef64855 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/ApplicationBaseServiceTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceTest.java
index 30f8865e9d0fd7ba29d8046d6137a906373aa7dd..e62ab15033e142a87c38d3f8e1096ebe482a4ac0 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/BulkDomainServiceImplTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/BulkDomainServiceImplTest.java
index 1b6b7b62f2a5911e6271a3b1ce5de472881f93ed..a110e99b8e7679361cd748b47bbf24c679ea6d4c 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() {
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 e9e2e9922bb914f33c467d751f6318fca83e33d1..c997d0800980ffcbef7e211ffe0472e7cfec74a6 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;
@@ -8,10 +7,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;
@@ -55,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;
@@ -246,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());
     }
 
@@ -379,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);
@@ -487,8 +491,95 @@ 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);
+        DomainGroupView domainGroupView = new DomainGroupView(1L, "test", "test1", List.of(domain1), null, List.of(userView) );
+
+        User user = new User("user");
+        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);
+
+        DomainGroupView domainGroupView = new DomainGroupView(1L, "test", "test1",List.of(domain1), null, List.of(userView) );
+        User user = new User("user");
+        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);
+
+        DomainGroupView domainGroupView = new DomainGroupView(1L, "test", "test1",List.of(domain1), null, List.of(userView) );
+        User user = new User("user");
+        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/SSHKeyServiceTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/SSHKeyServiceTest.java
index 09ff0e37dfd01e8dd0898a6b61c59f56f56b6a0e..fbf9baf42ec847be5e209e0acf3178e587aa99fb 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());
     }
+
 }
diff --git a/src/test/shell/data/i18n/de.json b/src/test/shell/data/i18n/de.json
index d5214b14ab79b863fd69cbf43f54b49561bfabb1..1d2b7ced244d0fd8fca3a57e5f3d2c070177b8dc 100644
--- a/src/test/shell/data/i18n/de.json
+++ b/src/test/shell/data/i18n/de.json
@@ -1171,6 +1171,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"
+      }
+    },
+    "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/en.json b/src/test/shell/data/i18n/en.json
index da4edbfb3c1489269a8e21b9eefdb6c46f0396e7..ca170d950606da76a4567ca8ba6955517428f5b9 100644
--- a/src/test/shell/data/i18n/en.json
+++ b/src/test/shell/data/i18n/en.json
@@ -1172,6 +1172,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"
+      }
+    },
+    "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/fr.json b/src/test/shell/data/i18n/fr.json
index 1ea6be4e2858fc1def457257105a851889bbcf41..77eb1a955ef5bd58f41030c0e05588c08b8fef35 100644
--- a/src/test/shell/data/i18n/fr.json
+++ b/src/test/shell/data/i18n/fr.json
@@ -1172,6 +1172,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"
+      }
+    },
+    "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 e36774e852318adee4b558235f24644949e9a941..422adc5baf045bf8fc92086789d6c34a346721c1 100644
--- a/src/test/shell/data/i18n/pl.json
+++ b/src/test/shell/data/i18n/pl.json
@@ -1172,6 +1172,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"
+      }
+    },
+    "BUTTON_INVALIDATE": "Invalidate",
+    "NO_TOKENS": "No tokens",
+    "NEW_TOKEN": "Add new token"
+  },
   "JSON_EDIT": {
     "INVALID_JSON": "Niewłaściwy format JSON. Zmiany wprowadzone do zawartości formularza nie zostaną zapisane."
   },
@@ -1231,8 +1255,7 @@
       "CHECK_STATE" : "Sprawdź status",
       "UPLOAD_TEXT" : "lub podaj tekst w formacie CSV poniżej",
       "DOWNLOAD_CSV" : "Pobierz CSV",
-      "REFRESH" : "Odśwież status "
-
+      "REFRESH" : "Odśwież stany"
     },
     "STATE" : {
       "PENDING" :  "Oczekuje",