RED-6254: Store all connection information in master database

This commit is contained in:
deiflaender 2023-03-07 16:51:00 +01:00
parent 63741b08d6
commit ec7ad9a0e8
19 changed files with 493 additions and 60 deletions

View File

@ -0,0 +1,17 @@
package com.iqser.red.service.persistence.service.v1.api.model.multitenancy;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AzureStorageConnection {
private String connectionString;
private String containerName;
}

View File

@ -0,0 +1,28 @@
package com.iqser.red.service.persistence.service.v1.api.model.multitenancy;
import java.util.HashMap;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DatabaseConnection {
private String driver;
private String host;
private String port;
private String database;
private String schema;
private String username;
private String password;
@Builder.Default
private Map<String, String> params = new HashMap<>();
}

View File

@ -0,0 +1,21 @@
package com.iqser.red.service.persistence.service.v1.api.model.multitenancy;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class S3StorageConnection {
private String key;
private String secret;
private String signerType;
private String bucketName;
private String region;
private String endpoint;
}

View File

@ -0,0 +1,24 @@
package com.iqser.red.service.persistence.service.v1.api.model.multitenancy;
import java.util.Set;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SearchConnection {
private Set<String> hosts;
private int port = 9300;
private String scheme;
private String username;
private String password;
private String numberOfShards;
private String numberOfReplicas;
}

View File

@ -18,11 +18,10 @@ public class TenantRequest {
@NotBlank
private String displayName;
private String guid;
@NotBlank
private String jdbcUrl;
@NotBlank
private String user;
@NotBlank
private String password;
private DatabaseConnection databaseConnection;
private SearchConnection searchConnection;
private AzureStorageConnection azureStorageConnection;
private S3StorageConnection s3StorageConnection;
}

View File

@ -14,7 +14,10 @@ public class TenantResponse {
private String tenantId;
private String displayName;
private String guid;
private String jdbcUrl;
private String user;
private DatabaseConnection databaseConnection;
private SearchConnection searchConnection;
private AzureStorageConnection azureStorageConnection;
private S3StorageConnection s3StorageConnection;
}

View File

@ -29,6 +29,10 @@ public interface TenantsResource {
List<TenantResponse> getTenants();
@GetMapping(value = "/tenants" + TENANT_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
TenantResponse getTenant(@PathVariable(TENANT_ID_PARAM) String tenantId);
@GetMapping(value = "/deploymentKey" + TENANT_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
JSONPrimitive<String> getDeploymentKey(@PathVariable(TENANT_ID_PARAM) String tenantId);

View File

@ -0,0 +1,23 @@
package com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Embeddable
public class AzureStorageConnectionEntity {
@Column(name = "storage_azure_connection_string")
private String connectionString;
@Column(name = "storage_azure_container_name")
private String containerName;
}

View File

@ -1,27 +1,45 @@
package com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity;
import java.util.Map;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Embeddable;
import javax.persistence.FetchType;
import com.iqser.red.service.persistence.management.v1.processor.utils.JSONMapConverter;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Embeddable
public class DatabaseConnectionEntity {
@Column
private String jdbcDriver;
@Column
private String jdbcHost;
@Column
private String jdbcPort;
@Column
private String jdbcDatabase;
@Column
private String jdbcSchema;
@Column(name = "db_driver")
private String driver;
@Column(name = "db_host")
private String host;
@Column(name = "db_port")
private String port;
@Column(name = "db_database")
private String database;
@Column(name = "db_schema")
private String schema;
@Column(name = "db_username")
private String username;
@Column(name = "db_password")
private String password;
@Basic(fetch = FetchType.EAGER)
@Column(columnDefinition = "text", name = "db_params")
@Convert(converter = JSONMapConverter.class)
private Map<String, String> params;
}

View File

@ -0,0 +1,31 @@
package com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Embeddable
public class S3StorageConnectionEntity {
@Column(name = "storage_s3_key")
private String key;
@Column(name = "storage_s3_secret")
private String secret;
@Column(name = "storage_s3_signer_type")
private String signerType;
@Column(name = "storage_s3_bucket_name")
private String bucketName;
@Column(name = "storage_s3_region")
private String region;
@Column(name = "storage_s3_endpoint")
private String endpoint;
}

View File

@ -0,0 +1,40 @@
package com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Embeddable;
import com.iqser.red.service.persistence.management.v1.processor.utils.JSONStringSetConverter;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Embeddable
public class SearchConnectionEntity {
@Column(columnDefinition = "text", name = "search_hosts")
@Convert(converter = JSONStringSetConverter.class)
private Set<String> hosts;
@Column(name = "search_port")
private int port = 9300;
@Column(name = "search_scheme")
private String scheme;
@Column(name = "search_username")
private String username;
@Column(name = "search_password")
private String password;
@Column(name = "search_number_of_shards")
private String numberOfShards;
@Column(name = "search_number_of_replicas")
private String numberOfReplicas;
}

View File

@ -25,12 +25,17 @@ public class TenantEntity {
private String displayName;
@Column
private String guid;
@Column
private String username;
@Column
private String password;
@Embedded
private DatabaseConnectionEntity databaseConnection;
@Embedded
private SearchConnectionEntity searchConnection;
@Embedded
private AzureStorageConnectionEntity azureStorageConnection;
@Embedded
private S3StorageConnectionEntity s3StorageConnection;
}

View File

@ -18,6 +18,7 @@ import com.google.common.cache.RemovalListener;
import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.TenantEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.EncryptionDecryptionService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository.TenantRepository;
import com.iqser.red.service.persistence.management.v1.processor.utils.jdbc.JDBCUtils;
import com.zaxxer.hikari.HikariDataSource;
import lombok.RequiredArgsConstructor;
@ -85,13 +86,15 @@ public class DynamicDataSourceBasedMultiTenantConnectionProvider extends Abstrac
private DataSource createAndConfigureDataSource(TenantEntity tenant) {
String decryptedPassword = encryptionService.decrypt(tenant.getPassword());
String decryptedPassword = encryptionService.decrypt(tenant.getDatabaseConnection().getPassword());
HikariDataSource ds = masterDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
ds.setUsername(tenant.getUsername());
ds.setUsername(tenant.getDatabaseConnection().getUsername());
ds.setPassword(decryptedPassword);
ds.setJdbcUrl(tenant.getJdbcUrl());
// TODO Should not include schema
ds.setJdbcUrl(JDBCUtils.buildJdbcUrl(tenant.getDatabaseConnection()));
ds.setPoolName(tenant.getTenantId() + TENANT_POOL_NAME_SUFFIX);
@ -99,4 +102,7 @@ public class DynamicDataSourceBasedMultiTenantConnectionProvider extends Abstrac
return ds;
}
}

View File

@ -0,0 +1,48 @@
package com.iqser.red.service.persistence.management.v1.processor.utils.jdbc;
import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.DatabaseConnectionEntity;
import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.DatabaseConnection;
import lombok.experimental.UtilityClass;
@UtilityClass
public class JDBCUtils {
public String buildJdbcUrl(DatabaseConnectionEntity databaseConnectionEntity){
StringBuilder sb = new StringBuilder("jdbc:")
.append(databaseConnectionEntity.getDriver())
.append("://")
.append(databaseConnectionEntity.getHost())
.append(':')
.append(databaseConnectionEntity.getPort())
.append('/')
.append(databaseConnectionEntity.getDatabase())
.append('?')
.append("currentSchema=")
.append(databaseConnectionEntity.getSchema());
if(databaseConnectionEntity.getParams() != null) {
databaseConnectionEntity.getParams().forEach((k, v) -> sb.append('&').append(k).append(v));
}
return sb.toString();
}
public String buildJdbcUrl(DatabaseConnection databaseConnection){
StringBuilder sb = new StringBuilder("jdbc:")
.append(databaseConnection.getDriver())
.append("://")
.append(databaseConnection.getHost())
.append(':')
.append(databaseConnection.getPort())
.append('/')
.append(databaseConnection.getDatabase())
.append('?')
.append("currentSchema=")
.append(databaseConnection.getSchema());
if(databaseConnection.getParams() != null) {
databaseConnection.getParams().forEach((k, v) -> sb.append('&').append(k).append(v));
}
return sb.toString();
}
}

View File

@ -42,6 +42,12 @@ public class TenantsController implements TenantsResource {
}
public TenantResponse getTenant(@PathVariable(TENANT_ID_PARAM) String tenantId) {
return tenantManagementService.getTenant(tenantId);
}
public JSONPrimitive<String> getDeploymentKey(@PathVariable(TENANT_ID_PARAM) String tenantId) {
return JSONPrimitive.of(deploymentKeyService.getDeploymentKey(tenantId));

View File

@ -16,6 +16,7 @@ import org.springframework.jdbc.datasource.SingleConnectionDataSource;
import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.TenantEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.EncryptionDecryptionService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository.TenantRepository;
import com.iqser.red.service.persistence.management.v1.processor.utils.jdbc.JDBCUtils;
import liquibase.integration.spring.SpringLiquibase;
import lombok.RequiredArgsConstructor;
@ -51,7 +52,9 @@ public class TenantSpringLiquibaseExecutor implements InitializingBean, Resource
for (TenantEntity tenant : tenants) {
log.info("Initializing Liquibase for tenant " + tenant.getTenantId());
try (Connection connection = DriverManager.getConnection(tenant.getJdbcUrl(), tenant.getUsername(), encryptionService.decrypt(tenant.getPassword()))) {
try (Connection connection = DriverManager.getConnection(JDBCUtils.buildJdbcUrl(tenant.getDatabaseConnection()),
tenant.getDatabaseConnection().getUsername(),
encryptionService.decrypt(tenant.getDatabaseConnection().getPassword()))) {
DataSource tenantDataSource = new SingleConnectionDataSource(connection, false);
SpringLiquibase liquibase = this.getSpringLiquibase(tenantDataSource);
liquibase.afterPropertiesSet();

View File

@ -22,9 +22,18 @@ import org.springframework.stereotype.Service;
import com.iqser.red.service.peristence.v1.server.migration.AsyncMigrationStarterService;
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.AzureStorageConnectionEntity;
import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.DatabaseConnectionEntity;
import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.S3StorageConnectionEntity;
import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.SearchConnectionEntity;
import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.TenantEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.EncryptionDecryptionService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository.TenantRepository;
import com.iqser.red.service.persistence.management.v1.processor.utils.jdbc.JDBCUtils;
import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.AzureStorageConnection;
import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.DatabaseConnection;
import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.S3StorageConnection;
import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.SearchConnection;
import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.TenantRequest;
import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.TenantResponse;
@ -60,7 +69,8 @@ public class TenantManagementService {
public TenantManagementService(EncryptionDecryptionService encryptionService,
@Qualifier("tenantLiquibaseProperties") LiquibaseProperties liquibaseProperties,
ResourceLoader resourceLoader,
TenantRepository tenantRepository, AsyncMigrationStarterService asyncMigrationStarterService) {
TenantRepository tenantRepository,
AsyncMigrationStarterService asyncMigrationStarterService) {
this.encryptionService = encryptionService;
this.liquibaseProperties = liquibaseProperties;
@ -75,11 +85,12 @@ public class TenantManagementService {
if (tenantRepository.findById(tenantRequest.getTenantId()).isEmpty()) {
validateJdbcUrl(tenantRequest.getJdbcUrl());
var jdbcUrl = JDBCUtils.buildJdbcUrl(tenantRequest.getDatabaseConnection());
validateJdbcUrl(jdbcUrl);
String encryptedPassword = encryptionService.encrypt(tenantRequest.getPassword());
try (Connection connection = DriverManager.getConnection(tenantRequest.getJdbcUrl(), tenantRequest.getUser(), tenantRequest.getPassword())) {
try (Connection connection = DriverManager.getConnection(jdbcUrl,
tenantRequest.getDatabaseConnection().getUsername(),
tenantRequest.getDatabaseConnection().getPassword())) {
DataSource tenantDataSource = new SingleConnectionDataSource(connection, false);
runLiquibase(tenantDataSource);
} catch (PSQLException e) {
@ -91,10 +102,44 @@ public class TenantManagementService {
.tenantId(tenantRequest.getTenantId())
.displayName(tenantRequest.getDisplayName())
.guid(UUID.randomUUID().toString())
.username(tenantRequest.getUser())
.jdbcUrl(tenantRequest.getJdbcUrl())
.password(encryptedPassword)
.databaseConnection(DatabaseConnectionEntity.builder()
.driver(tenantRequest.getDatabaseConnection().getDriver())
.host(tenantRequest.getDatabaseConnection().getHost())
.port(tenantRequest.getDatabaseConnection().getPort())
.database(tenantRequest.getDatabaseConnection().getDatabase())
.schema(tenantRequest.getDatabaseConnection().getSchema())
.username(tenantRequest.getDatabaseConnection().getUsername())
.password(encryptionService.encrypt(tenantRequest.getDatabaseConnection().getPassword()))
.build())
.searchConnection(SearchConnectionEntity.builder()
.hosts(tenantRequest.getSearchConnection().getHosts())
.port(tenantRequest.getSearchConnection().getPort())
.scheme(tenantRequest.getSearchConnection().getScheme())
.username(tenantRequest.getSearchConnection().getUsername())
.password(encryptionService.encrypt(tenantRequest.getSearchConnection().getPassword()))
.numberOfShards(tenantRequest.getSearchConnection().getNumberOfShards())
.numberOfReplicas(tenantRequest.getSearchConnection().getNumberOfReplicas())
.build())
.build();
if (tenantRequest.getAzureStorageConnection() != null) {
tenantEntity.setAzureStorageConnection(AzureStorageConnectionEntity.builder()
.connectionString(encryptionService.encrypt(tenantRequest.getAzureStorageConnection().getConnectionString()))
.containerName(tenantRequest.getAzureStorageConnection().getContainerName())
.build());
}
if (tenantRequest.getS3StorageConnection() != null) {
tenantEntity.setS3StorageConnection(S3StorageConnectionEntity.builder()
.key(tenantRequest.getS3StorageConnection().getKey())
.secret(encryptionService.encrypt(tenantRequest.getS3StorageConnection().getSecret()))
.signerType(tenantRequest.getS3StorageConnection().getSignerType())
.bucketName(tenantRequest.getS3StorageConnection().getBucketName())
.region(tenantRequest.getS3StorageConnection().getRegion())
.endpoint(tenantRequest.getS3StorageConnection().getEndpoint())
.build());
}
tenantRepository.save(tenantEntity);
asyncMigrationStarterService.runForTenant(tenantRequest.getTenantId());
@ -156,13 +201,50 @@ public class TenantManagementService {
private TenantResponse convert(TenantEntity entity) {
return TenantResponse.builder()
var tenantResponse = TenantResponse.builder()
.tenantId(entity.getTenantId())
.displayName(entity.getDisplayName())
.guid(entity.getGuid())
.jdbcUrl(entity.getJdbcUrl())
.user(entity.getUsername())
.databaseConnection(DatabaseConnection.builder()
.driver(entity.getDatabaseConnection().getDriver())
.host(entity.getDatabaseConnection().getHost())
.port(entity.getDatabaseConnection().getPort())
.database(entity.getDatabaseConnection().getDatabase())
.schema(entity.getDatabaseConnection().getSchema())
.username(entity.getDatabaseConnection().getUsername())
.password(entity.getDatabaseConnection().getPassword())
.params(entity.getDatabaseConnection().getParams())
.build())
.searchConnection(SearchConnection.builder()
.hosts(entity.getSearchConnection().getHosts())
.port(entity.getSearchConnection().getPort())
.scheme(entity.getSearchConnection().getScheme())
.username(entity.getSearchConnection().getUsername())
.password(entity.getSearchConnection().getPassword())
.numberOfShards(entity.getSearchConnection().getNumberOfShards())
.numberOfReplicas(entity.getSearchConnection().getNumberOfReplicas())
.build())
.build();
if (entity.getAzureStorageConnection() != null) {
tenantResponse.setAzureStorageConnection(AzureStorageConnection.builder()
.connectionString(entity.getAzureStorageConnection().getConnectionString())
.containerName(entity.getAzureStorageConnection().getContainerName())
.build());
}
if (entity.getS3StorageConnection() != null) {
tenantResponse.setS3StorageConnection(S3StorageConnection.builder()
.key(entity.getS3StorageConnection().getKey())
.secret(entity.getS3StorageConnection().getSecret())
.signerType(entity.getS3StorageConnection().getSignerType())
.bucketName(entity.getS3StorageConnection().getBucketName())
.region(entity.getS3StorageConnection().getRegion())
.endpoint(entity.getS3StorageConnection().getEndpoint())
.build());
}
return tenantResponse;
}

View File

@ -1,7 +1,7 @@
databaseChangeLog:
- changeSet:
id: detailed-db-connection
author: viktorseifert
author: dom
changes:
- deleteColumn:
columns:
@ -11,33 +11,72 @@ databaseChangeLog:
- addColumn:
columns:
- column:
name: jdbc_driver
name: db_driver
type: VARCHAR(255)
constraints:
unique: true
uniqueConstraintName: UC_TENANT_CONNECTION
- column:
name: jdbc_host
name: db_host
type: VARCHAR(255)
constraints:
unique: true
uniqueConstraintName: UC_TENANT_CONNECTION
- column:
name: jdbc_port
name: db_port
type: VARCHAR(255)
constraints:
unique: true
uniqueConstraintName: UC_TENANT_CONNECTION
- column:
name: jdbc_database
name: db_database
type: VARCHAR(255)
constraints:
unique: true
uniqueConstraintName: UC_TENANT_CONNECTION
- column:
name: jdbc_schema
name: db_schema
type: VARCHAR(255)
- column:
name: db_params
type: VARCHAR(255)
- column:
name: db_username
type: VARCHAR(255)
- column:
name: db_password
type: VARCHAR(255)
- column:
name: search_hosts
type: VARCHAR(255)
- column:
name: search_port
type: VARCHAR(255)
- column:
name: search_scheme
type: VARCHAR(255)
- column:
name: search_username
type: VARCHAR(255)
- column:
name: search_password
type: VARCHAR(255)
- column:
name: search_number_of_shards
type: VARCHAR(255)
- column:
name: search_number_of_replicas
type: VARCHAR(255)
- column:
name: storage_azure_connection_string
type: VARCHAR(255)
- column:
name: storage_azure_container_name
type: VARCHAR(255)
- column:
name: storage_s3_key
type: VARCHAR(255)
- column:
name: storage_s3_secret
type: VARCHAR(255)
- column:
name: storage_s3_signer_type
type: VARCHAR(255)
- column:
name: storage_s3_bucket_name
type: VARCHAR(255)
- column:
name: storage_s3_region
type: VARCHAR(255)
- column:
name: storage_s3_endpoint
type: VARCHAR(255)
constraints:
unique: true
uniqueConstraintName: UC_TENANT_CONNECTION
tableName: tenant

View File

@ -5,6 +5,7 @@ import static org.mockito.Mockito.when;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Set;
import java.util.UUID;
import javax.sql.DataSource;
@ -78,6 +79,9 @@ import com.iqser.red.service.persistence.management.v1.processor.service.persist
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.WatermarkRepository;
import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext;
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.ApplicationConfig;
import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.DatabaseConnection;
import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.S3StorageConnection;
import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.SearchConnection;
import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.TenantRequest;
import com.iqser.red.service.redaction.v1.model.RedactionLog;
import com.iqser.red.service.redaction.v1.model.RedactionResult;
@ -182,7 +186,6 @@ public abstract class AbstractPersistenceServerServiceTest {
protected PrometheusMeterRegistry prometheusMeterRegistry;
@BeforeEach
public void setupOptimize() {
@ -215,11 +218,44 @@ public abstract class AbstractPersistenceServerServiceTest {
var postgreSQLContainerMaster = SpringPostgreSQLTestContainer.getInstance().withDatabaseName("integration-tests-db-master").withUsername("sa").withPassword("sa");
var jdbcUrl = postgreSQLContainerMaster.getJdbcUrl().substring(0, postgreSQLContainerMaster.getJdbcUrl().lastIndexOf('/') + 1) + "redaction?currentSchema=myschema";
var port = postgreSQLContainerMaster.getJdbcUrl().substring(0, postgreSQLContainerMaster.getJdbcUrl().lastIndexOf('/')).split(":")[3];
createDatabase("redaction", "redaction");
createSchema(jdbcUrl, "redaction", "redaction");
tenantsClient.createTenant(new TenantRequest("redaction", "Redaction default", UUID.randomUUID().toString(), jdbcUrl, "redaction", "redaction"));
var tenantRequest = TenantRequest.builder()
.tenantId("redaction")
.displayName("Redaction default")
.guid(UUID.randomUUID().toString())
.databaseConnection(DatabaseConnection.builder()
.driver("postgresql")
.host("localhost")
.port(port)
.database("redaction")
.schema("myschema")
.username("redaction")
.password("redaction")
.build())
.searchConnection(SearchConnection.builder()
.hosts(Set.of("elasticsearchHost"))
.port(9200)
.scheme("https")
.username("elastic")
.password("changeMe")
.numberOfShards("1")
.numberOfReplicas("5")
.build())
.s3StorageConnection(S3StorageConnection.builder()
.key("key")
.secret("secret")
.signerType("signerType")
.bucketName("bucketName")
.region("eu")
.endpoint("endpoint")
.build())
.build();
tenantsClient.createTenant(tenantRequest);
}
}