From 7e28e5574eb1a7149f9a85015f12ed61341bdeb6 Mon Sep 17 00:00:00 2001 From: Timo Bejan Date: Wed, 28 Jun 2023 11:39:18 +0300 Subject: [PATCH] Tenant Creation exception rework --- .../api/internal/InternalTenantsResource.java | 2 +- .../controller/ControllerAdvice.java | 21 ++++++ .../internal/InternalTenantsController.java | 4 +- .../model/ErrorMessage.java | 15 ++++ .../model/TenantUser.java | 4 ++ .../service/TenantManagementService.java | 69 +++++++++++-------- .../service/TenantPersistenceService.java | 24 +++++++ 7 files changed, 109 insertions(+), 30 deletions(-) create mode 100644 src/main/java/com/knecon/fforesight/tenantusermanagement/controller/ControllerAdvice.java create mode 100644 src/main/java/com/knecon/fforesight/tenantusermanagement/model/ErrorMessage.java create mode 100644 src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantPersistenceService.java diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/api/internal/InternalTenantsResource.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/api/internal/InternalTenantsResource.java index 7bcff53..1dc3084 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/api/internal/InternalTenantsResource.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/api/internal/InternalTenantsResource.java @@ -38,7 +38,7 @@ public interface InternalTenantsResource { @PostMapping(value = "/tenants", consumes = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Creates a new Tenant", description = "None") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")}) - void createTenant(@RequestBody TenantRequest tenant); + TenantResponse createTenant(@RequestBody TenantRequest tenant); @GetMapping(value = "/tenants", produces = MediaType.APPLICATION_JSON_VALUE) diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/ControllerAdvice.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/ControllerAdvice.java new file mode 100644 index 0000000..0c95406 --- /dev/null +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/ControllerAdvice.java @@ -0,0 +1,21 @@ +package com.knecon.fforesight.tenantusermanagement.controller; + +import javax.ws.rs.core.Response; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.server.ResponseStatusException; + +import com.knecon.fforesight.tenantusermanagement.model.ErrorMessage; + +@RestControllerAdvice +public class ControllerAdvice { + + @ExceptionHandler(ResponseStatusException.class) + public ResponseEntity handleResponseStatusException(ResponseStatusException e) { + + return new ResponseEntity<>(new ErrorMessage(e.getMessage()), e.getStatusCode()); + } + +} diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/internal/InternalTenantsController.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/internal/InternalTenantsController.java index fe595d2..fe9fd7d 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/internal/InternalTenantsController.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/internal/InternalTenantsController.java @@ -35,10 +35,10 @@ public class InternalTenantsController implements InternalTenantsResource { } - public void createTenant(@Valid @RequestBody TenantRequest tenantRequest) { + public TenantResponse createTenant(@Valid @RequestBody TenantRequest tenantRequest) { try { - tenantManagementService.createTenant(tenantRequest); + return tenantManagementService.createTenant(tenantRequest); } catch (IllegalArgumentException e) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage(), e); } diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/model/ErrorMessage.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/model/ErrorMessage.java new file mode 100644 index 0000000..d71c8e3 --- /dev/null +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/model/ErrorMessage.java @@ -0,0 +1,15 @@ +package com.knecon.fforesight.tenantusermanagement.model; + +import java.time.OffsetDateTime; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +@Data +@RequiredArgsConstructor +public class ErrorMessage { + + private OffsetDateTime timestamp = OffsetDateTime.now(); + private final String message; + +} diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/model/TenantUser.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/model/TenantUser.java index 4619f0c..33b1e1e 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/model/TenantUser.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/model/TenantUser.java @@ -3,11 +3,15 @@ package com.knecon.fforesight.tenantusermanagement.model; import java.util.HashSet; import java.util.Set; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; @Data @Builder +@NoArgsConstructor +@AllArgsConstructor public class TenantUser { private String username; diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java index 99ed2cb..fc29c53 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java @@ -13,6 +13,7 @@ import java.util.UUID; import java.util.stream.Collectors; import javax.sql.DataSource; +import javax.ws.rs.ClientErrorException; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; @@ -66,6 +67,7 @@ public class TenantManagementService implements TenantProvider { private final EncryptionDecryptionService encryptionService; private final TenantRepository tenantRepository; + private final TenantPersistenceService tenantPersistenceService; private final GeneralConfigurationService generalConfigurationService; private final KeyCloakRoleManagerService keyCloakRoleManagerService; private final KeyCloakAdminClientService keycloak; @@ -78,8 +80,9 @@ public class TenantManagementService implements TenantProvider { @SneakyThrows - public void createTenant(TenantRequest tenantRequest) { + public TenantResponse createTenant(TenantRequest tenantRequest) { + log.info("Tenants are: {}",tenantRepository.findAll().stream().map(t-> t.getTenantId()).collect(Collectors.toList())); log.info("Requested to create tenant for: {}", tenantRequest.getTenantId()); try { @@ -134,32 +137,11 @@ public class TenantManagementService implements TenantProvider { .build()); } - createRealm(tenantRequest.getTenantId(), tenantRequest.getDefaultUsers()); - log.info("Created realm for tenant: {}", tenantRequest.getTenantId()); - - var waitTime = 0; - boolean realmReady; - do { - realmReady = tryToAccessRealm(tenantRequest.getTenantId()); - if (realmReady) { - break; - } else { - Thread.sleep(1_000L); - waitTime += 1_000L; - } - - } while (waitTime < MAX_WAIT_TIME); - - if (!realmReady) { - throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to create KC realm"); - } - - generalConfigurationService.initGeneralConfiguration(tenantRequest.getTenantId()); - keyCloakRoleManagerService.updateRoles(tenantRequest.getTenantId()); + propagateTenantToKeyCloak(tenantRequest); log.info("Updated roles for tenant: {}", tenantRequest.getTenantId()); - tenantRepository.save(tenantEntity); + var saved = tenantPersistenceService.save(tenantEntity); log.info("Persisted tenant: {}", tenantRequest.getTenantId()); @@ -169,17 +151,50 @@ public class TenantManagementService implements TenantProvider { log.info("Dispatched message for tenant: {}", tenantRequest.getTenantId()); + return convert(saved); } else { throw new ResponseStatusException(HttpStatus.CONFLICT, "Tenant exists"); } + } catch (ClientErrorException e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Tenant creation failed: " + e.getMessage(), e); + } catch (Exception e) { - log.error("Tenant creation failed, rolling back", e); - tenantRepository.deleteById(tenantRequest.getTenantId()); - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Tenant creation failed", e); + if (!(e instanceof ResponseStatusException)) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Tenant creation failed: " + e.getMessage(), e); + }else{ + throw e; + } } } + private void propagateTenantToKeyCloak(TenantRequest tenantRequest) throws InterruptedException { + + createRealm(tenantRequest.getTenantId(), tenantRequest.getDefaultUsers()); + log.info("Created realm for tenant: {}", tenantRequest.getTenantId()); + + var waitTime = 0; + boolean realmReady; + do { + realmReady = tryToAccessRealm(tenantRequest.getTenantId()); + if (realmReady) { + break; + } else { + Thread.sleep(1_000L); + waitTime += 1_000L; + } + + } while (waitTime < MAX_WAIT_TIME); + + if (!realmReady) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to create KC realm"); + } + + generalConfigurationService.initGeneralConfiguration(tenantRequest.getTenantId()); + keyCloakRoleManagerService.updateRoles(tenantRequest.getTenantId()); + } + + @SneakyThrows private void validateJdbcUrl(String jdbcUrl) { diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantPersistenceService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantPersistenceService.java new file mode 100644 index 0000000..e9f86dd --- /dev/null +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantPersistenceService.java @@ -0,0 +1,24 @@ +package com.knecon.fforesight.tenantusermanagement.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.knecon.fforesight.tenantusermanagement.entity.TenantEntity; +import com.knecon.fforesight.tenantusermanagement.repository.TenantRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class TenantPersistenceService { + + private final TenantRepository tenantRepository; + + + @Transactional + public TenantEntity save(TenantEntity tenant) { + + return tenantRepository.save(tenant); + } + +}