Pull request #560: RED-5293: 500 for invalid tenant

Merge in RED/persistence-service from RED-5293 to master

* commit '9b7d1536ebb8bb2056c4e5519c0428f1f865a81e':
  RED-5293: Changed output to client to explain which argument is broken
  RED-5293: Changed output to client to explain which argument is broken
  RED-5293: Changed error message for empty parameters to show client which parameter is broken
  RED-5293: 500 for invalid tenant
  RED-5293: 500 for invalid tenant
  RED-5293: 500 for invalid tenant
  RED-5293: 500 for invalid tenant
  RED-5293: 500 for invalid tenant
  RED-5293: 500 for invalid tenant
  RED-5293: 500 for invalid tenant
This commit is contained in:
Ali Oezyetimoglu 2022-11-07 16:47:52 +01:00
commit 6333d9e087
3 changed files with 48 additions and 7 deletions

View File

@ -14,11 +14,12 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
public class TenantRequest {
@NotNull
@NotBlank
private String tenantId;
@NotBlank
private String displayName;
private String guid;
@NotBlank
private String jdbcUrl;
@NotBlank
private String user;

View File

@ -1,8 +1,10 @@
package com.iqser.red.service.peristence.v1.server.controller;
import java.sql.SQLException;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.stream.Collectors;
import org.postgresql.util.PSQLException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
@ -83,8 +85,8 @@ public class ControllerAdvice {
@ResponseBody
@ExceptionHandler(value = PSQLException.class)
public ResponseEntity<ErrorMessage> handleSQLException(PSQLException e) {
@ExceptionHandler(value = SQLException.class)
public ResponseEntity<ErrorMessage> handleSQLException(SQLException e) {
if (e.getMessage().contains("violates unique constraint")) {
return new ResponseEntity<>(new ErrorMessage(OffsetDateTime.now(), "Unique constraint violation"), HttpStatus.CONFLICT);
@ -99,8 +101,9 @@ public class ControllerAdvice {
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public ErrorMessage handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
var errorList = e.getFieldErrors();
String errorListAsString = errorList.stream().map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage()).collect(Collectors.joining(", "));
return new ErrorMessage(OffsetDateTime.now(), String.format("You have empty/wrong formatted parameters: %s", errorListAsString));
}
}

View File

@ -1,8 +1,10 @@
package com.iqser.red.service.peristence.v1.server.service;
import java.net.URI;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
@ -16,7 +18,6 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.jdbc.datasource.SingleConnectionDataSource;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.TenantEntity;
@ -28,11 +29,15 @@ import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.Tenan
import liquibase.exception.LiquibaseException;
import liquibase.integration.spring.SpringLiquibase;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@EnableConfigurationProperties(LiquibaseProperties.class)
public class TenantManagementService {
private static final Set<String> SQL_CONNECTION_ERROR_CODES = Set.of("08000", "08003", "08006");
private final EncryptionDecryptionService encryptionService;
private final LiquibaseProperties liquibaseProperties;
@ -58,11 +63,16 @@ public class TenantManagementService {
if (tenantRepository.findById(tenantRequest.getTenantId()).isEmpty()) {
validateJdbcUrl(tenantRequest.getJdbcUrl());
String encryptedPassword = encryptionService.encrypt(tenantRequest.getPassword());
try (Connection connection = DriverManager.getConnection(tenantRequest.getJdbcUrl(), tenantRequest.getUser(), tenantRequest.getPassword())) {
DataSource tenantDataSource = new SingleConnectionDataSource(connection, false);
runLiquibase(tenantDataSource);
} catch (PSQLException e) {
handleClientException(e);
handleInternalException(e);
}
TenantEntity tenantEntity = TenantEntity.builder()
@ -80,6 +90,25 @@ public class TenantManagementService {
}
private void handleClientException(PSQLException e) {
if (e.getSQLState().equals("28000") || e.getSQLState().equals("28P01")) {
throw new IllegalArgumentException("Database credentials are not correct. Please check them.");
}
if (SQL_CONNECTION_ERROR_CODES.contains(e.getSQLState())) {
throw new IllegalArgumentException("Error when connecting to tenant database. Please check the jdbcUrl parameter.");
}
}
private void handleInternalException(PSQLException e) {
log.error(String.format("Connection to tenant DB failed with SQL state %s. Please check if the tenant DB is still running. " + //
"If yes please check the connection configuration.", e.getSQLState()), e);
throw new RuntimeException("Could not connect to the tenant DB. This is an internal error.", e);
}
public List<TenantResponse> getTenants() {
return tenantRepository.findAll().stream().map(this::convert).collect(Collectors.toList());
@ -92,6 +121,14 @@ public class TenantManagementService {
}
@SneakyThrows
private void validateJdbcUrl(String jdbcUrl) {
// just create a URI object to check if the string is a valid URI
new URI(jdbcUrl);
}
private TenantResponse convert(TenantEntity entity) {
return TenantResponse.builder()