diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/Tenant.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/Tenant.java new file mode 100644 index 000000000..124ed10eb --- /dev/null +++ b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/Tenant.java @@ -0,0 +1,19 @@ +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 +@AllArgsConstructor +@NoArgsConstructor +public class Tenant { + + private String tenantId; + private String jdbcUrl; + private String user; + private String password; + +} diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/TenantsResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/TenantsResource.java new file mode 100644 index 000000000..f967dabec --- /dev/null +++ b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/TenantsResource.java @@ -0,0 +1,17 @@ +package com.iqser.red.service.persistence.service.v1.api.resources; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.Tenant; + +public interface TenantsResource { + + @ResponseStatus(value = HttpStatus.OK) + @PostMapping("/tenants") + void createTenant(@RequestBody Tenant tenant); + + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/PersistenceServiceProcessorConfiguration.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/PersistenceServiceProcessorConfiguration.java index 00a561fe1..d3f74bcff 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/PersistenceServiceProcessorConfiguration.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/PersistenceServiceProcessorConfiguration.java @@ -1,29 +1,16 @@ package com.iqser.red.service.persistence.management.v1.processor; -import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.support.PageJacksonModule; import org.springframework.cloud.openfeign.support.SortJacksonModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import com.iqser.red.service.persistence.management.v1.processor.client.PDFTronRedactionClient; -import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.CommentEntity; -import com.iqser.red.service.persistence.management.v1.processor.entity.audit.AuditEntity; -import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.ColorsEntity; -import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity; -import com.iqser.red.service.persistence.management.v1.processor.entity.download.DownloadStatusEntity; -import com.iqser.red.service.persistence.management.v1.processor.entity.index.IndexInformationEntity; -import com.iqser.red.service.persistence.management.v1.processor.entity.migration.MigrationEntity; -import com.iqser.red.service.persistence.management.v1.processor.entity.notification.NotificationEntity; -import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ColorsRepository; @Configuration @ComponentScan -@EntityScan(basePackageClasses = {CommentEntity.class, AuditEntity.class, NotificationEntity.class, ColorsEntity.class, DossierEntity.class, DownloadStatusEntity.class, MigrationEntity.class, IndexInformationEntity.class}) -@EnableJpaRepositories(basePackageClasses = ColorsRepository.class) @EnableFeignClients(basePackageClasses = {PDFTronRedactionClient.class}) public class PersistenceServiceProcessorConfiguration { diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/TenantEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/TenantEntity.java new file mode 100644 index 000000000..f586d8f87 --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/TenantEntity.java @@ -0,0 +1,29 @@ +package com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Entity +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "tenant") +public class TenantEntity { + + @Id + private String tenantId; + @Column + private String jdbcUrl; + @Column(name = "username") + private String user; + @Column + private String password; +} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/mulitenancy/CurrentTenantIdentifierResolverImpl.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/mulitenancy/CurrentTenantIdentifierResolverImpl.java new file mode 100644 index 000000000..7ea18277b --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/mulitenancy/CurrentTenantIdentifierResolverImpl.java @@ -0,0 +1,26 @@ +package com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy; + +import org.hibernate.context.spi.CurrentTenantIdentifierResolver; +import org.springframework.stereotype.Component; + +import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext; + +@Component("currentTenantIdentifierResolver") +public class CurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver { + + @Override + public String resolveCurrentTenantIdentifier() { + String tenantId = TenantContext.getTenantId(); + if (tenantId != null && !tenantId.isEmpty()) { + return tenantId; + } else { + // Allow bootstrapping the EntityManagerFactory, in which case no tenant is needed + return "BOOTSTRAP"; + } + } + + @Override + public boolean validateExistingCurrentSessions() { + return true; + } +} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/mulitenancy/DynamicDataSourceBasedMultiTenantConnectionProvider.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/mulitenancy/DynamicDataSourceBasedMultiTenantConnectionProvider.java new file mode 100644 index 000000000..d4aac826c --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/mulitenancy/DynamicDataSourceBasedMultiTenantConnectionProvider.java @@ -0,0 +1,105 @@ +package com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import javax.annotation.PostConstruct; +import javax.sql.DataSource; + +import org.hibernate.engine.jdbc.connections.spi.AbstractDataSourceBasedMultiTenantConnectionProviderImpl; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.stereotype.Component; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +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.zaxxer.hikari.HikariDataSource; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +@RequiredArgsConstructor +public class DynamicDataSourceBasedMultiTenantConnectionProvider extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl { + + private static final String TENANT_POOL_NAME_SUFFIX = "DataSource"; + + private final DataSource masterDataSource; + private final DataSourceProperties masterDataSourceProperties; + private final TenantRepository masterTenantRepository; + private final EncryptionDecryptionService encryptionService; + + @Value("${multitenancy.datasource-cache.maximumSize:100}") + private Long maximumSize; + + @Value("${multitenancy.datasource-cache.expireAfterAccess:10}") + private Integer expireAfterAccess; + + private LoadingCache tenantDataSources; + + + @PostConstruct + protected void createCache() { + + tenantDataSources = CacheBuilder.newBuilder() + .maximumSize(maximumSize) + .expireAfterAccess(expireAfterAccess, TimeUnit.MINUTES) + .removalListener((RemovalListener) removal -> { + HikariDataSource ds = (HikariDataSource) removal.getValue(); + ds.close(); // tear down properly + log.info("Closed datasource: {}", ds.getPoolName()); + }) + .build(new CacheLoader() { + public DataSource load(String key) { + + TenantEntity tenant = masterTenantRepository.findByTenantId(key) + .orElseThrow(() -> new RuntimeException("No such tenant: " + key)); + return createAndConfigureDataSource(tenant); + } + }); + } + + + @Override + protected DataSource selectAnyDataSource() { + + return masterDataSource; + } + + + @Override + public DataSource selectDataSource(String tenantIdentifier) { + + try { + return tenantDataSources.get(tenantIdentifier); + } catch (ExecutionException e) { + throw new RuntimeException("Failed to load DataSource for tenant: " + tenantIdentifier); + } + } + + + private DataSource createAndConfigureDataSource(TenantEntity tenant) { + + String decryptedPassword = encryptionService.decrypt(tenant.getPassword()); + + HikariDataSource ds = masterDataSourceProperties.initializeDataSourceBuilder() + .type(HikariDataSource.class) + .build(); + + ds.setUsername(tenant.getUser()); + ds.setPassword(decryptedPassword); + ds.setJdbcUrl(tenant.getJdbcUrl()); + + ds.setPoolName(tenant.getTenantId() + TENANT_POOL_NAME_SUFFIX); + + log.info("Configured datasource: {}", ds.getPoolName()); + return ds; + } + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/mulitenancy/repository/TenantRepository.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/mulitenancy/repository/TenantRepository.java new file mode 100644 index 000000000..948d39732 --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/mulitenancy/repository/TenantRepository.java @@ -0,0 +1,15 @@ +package com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository; + +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.TenantEntity; + +public interface TenantRepository extends JpaRepository { + + @Query("select t from TenantEntity t where t.tenantId = :tenantId") + Optional findByTenantId(@Param("tenantId") String tenantId); +} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/jdbc/JDBCWriteUtils.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/jdbc/JDBCWriteUtils.java index 8fc9549a8..c40fac081 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/jdbc/JDBCWriteUtils.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/jdbc/JDBCWriteUtils.java @@ -1,17 +1,7 @@ package com.iqser.red.service.persistence.management.v1.processor.utils.jdbc; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import org.apache.commons.lang3.StringUtils; -import org.springframework.jdbc.core.BatchPreparedStatementSetter; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.stereotype.Service; +import static org.apache.commons.lang3.StringUtils.capitalize; -import javax.persistence.Column; -import javax.persistence.EntityManager; -import javax.persistence.Table; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.sql.PreparedStatement; @@ -21,7 +11,22 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import static org.apache.commons.lang3.StringUtils.capitalize; +import javax.persistence.Column; +import javax.persistence.EntityManager; +import javax.persistence.Table; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.DynamicDataSourceBasedMultiTenantConnectionProvider; +import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; @Service @RequiredArgsConstructor @@ -35,7 +40,12 @@ public class JDBCWriteUtils { private final EntityManager entityManager; + private final DynamicDataSourceBasedMultiTenantConnectionProvider dynamicDataSourceBasedMultiTenantConnectionProvider; + public void saveBatch(final List entities) { + + jdbcTemplate.setDataSource(dynamicDataSourceBasedMultiTenantConnectionProvider.selectDataSource(TenantContext.getTenantId())); + if (entities.isEmpty()) { return; } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/multitenancy/TenantContext.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/multitenancy/TenantContext.java new file mode 100644 index 000000000..bfd50c90f --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/multitenancy/TenantContext.java @@ -0,0 +1,29 @@ +package com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public final class TenantContext { + + private static InheritableThreadLocal currentTenant = new InheritableThreadLocal<>(); + + + public static void setTenantId(String tenantId) { + + log.debug("Setting tenantId to " + tenantId); + currentTenant.set(tenantId); + } + + + public static String getTenantId() { + + return currentTenant.get(); + } + + + public static void clear() { + + currentTenant.remove(); + } + +} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/Application.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/Application.java index 5c2fa4daa..6260b1992 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/Application.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/Application.java @@ -1,15 +1,11 @@ package com.iqser.red.service.peristence.v1.server; -import com.iqser.red.commons.spring.DefaultWebMvcConfiguration; -import com.iqser.red.service.peristence.v1.server.client.RedactionClient; -import com.iqser.red.service.peristence.v1.server.configuration.CleanupDownloadSchedulerConfiguration; -import com.iqser.red.service.peristence.v1.server.configuration.MessagingConfiguration; -import com.iqser.red.service.peristence.v1.server.settings.FileManagementServiceSettings; -import com.iqser.red.service.persistence.management.v1.processor.PersistenceServiceProcessorConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.openfeign.EnableFeignClients; @@ -22,12 +18,20 @@ import org.springframework.retry.support.RetryTemplate; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; +import com.iqser.red.service.peristence.v1.server.client.RedactionClient; +import com.iqser.red.service.peristence.v1.server.configuration.CleanupDownloadSchedulerConfiguration; +import com.iqser.red.service.peristence.v1.server.configuration.MessagingConfiguration; +import com.iqser.red.service.peristence.v1.server.multitenancy.AsyncConfig; +import com.iqser.red.service.peristence.v1.server.multitenancy.MultiTenancyWebConfiguration; +import com.iqser.red.service.peristence.v1.server.settings.FileManagementServiceSettings; +import com.iqser.red.service.persistence.management.v1.processor.PersistenceServiceProcessorConfiguration; + @EnableAsync @EnableRetry @EnableScheduling @EnableConfigurationProperties(FileManagementServiceSettings.class) -@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, CassandraAutoConfiguration.class}) -@Import({DefaultWebMvcConfiguration.class, PersistenceServiceProcessorConfiguration.class, MessagingConfiguration.class, CleanupDownloadSchedulerConfiguration.class}) +@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, CassandraAutoConfiguration.class, DataSourceAutoConfiguration.class, LiquibaseAutoConfiguration.class}) +@Import({MultiTenancyWebConfiguration.class, PersistenceServiceProcessorConfiguration.class, MessagingConfiguration.class, CleanupDownloadSchedulerConfiguration.class, AsyncConfig.class}) @EnableFeignClients(basePackageClasses = {RedactionClient.class}) public class Application { diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/controller/TenantsController.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/controller/TenantsController.java new file mode 100644 index 000000000..b30be275d --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/controller/TenantsController.java @@ -0,0 +1,26 @@ +package com.iqser.red.service.peristence.v1.server.controller; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import com.iqser.red.service.peristence.v1.server.service.TenantManagementService; +import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.Tenant; +import com.iqser.red.service.persistence.service.v1.api.resources.TenantsResource; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +public class TenantsController implements TenantsResource { + + private final TenantManagementService tenantManagementService; + + + @PostMapping("/tenants") + public void createTenant(@RequestBody Tenant tenant) { + + tenantManagementService.createTenant(tenant); + } + +} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationController.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationController.java index caef196df..31cdcc3f7 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationController.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationController.java @@ -1,14 +1,14 @@ package com.iqser.red.service.peristence.v1.server.migration; +import java.util.List; -import lombok.Data; -import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.util.List; +import lombok.Data; +import lombok.RequiredArgsConstructor; @Data @RestController diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationStarterService.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationStarterService.java index acb3ec590..ef0415d79 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationStarterService.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationStarterService.java @@ -11,6 +11,8 @@ import org.springframework.stereotype.Service; import com.iqser.red.service.peristence.v1.server.settings.FileManagementServiceSettings; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.MigrationPersistenceService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository.TenantRepository; +import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -24,26 +26,39 @@ public class MigrationStarterService { private final FileManagementServiceSettings settings; private final ApplicationContext ctx; private final MigrationPersistenceService migrationPersistenceService; + private final TenantRepository tenantRepository; @EventListener(ApplicationReadyEvent.class) public void migrate() { - //This is always running on startup - seedMigration(); + tenantRepository.findAll().forEach(tenant -> { + + TenantContext.setTenantId(tenant.getTenantId()); + + //This is always running on startup + seedMigration(); + }); // This should only run in post upgrade hook if (settings.isMigrateOnly()) { - log.info("Start migration"); - migrations.sort(Comparator.comparing(Migration::getVersion)); + tenantRepository.findAll().forEach(tenant -> { - for (var migration : migrations) { - migration.run(); - } + TenantContext.setTenantId(tenant.getTenantId()); + + log.info("Start migration for tentant: " + tenant); + + migrations.sort(Comparator.comparing(Migration::getVersion)); + + for (var migration : migrations) { + migration.run(); + } + }); log.info("Migration is finished"); System.exit(SpringApplication.exit(ctx, () -> 0)); + } } diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/DeleteRemovedManualAddRedactions7.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/DeleteRemovedManualAddRedactions7.java index 062f3001f..dd0f8e12a 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/DeleteRemovedManualAddRedactions7.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/DeleteRemovedManualAddRedactions7.java @@ -1,18 +1,20 @@ package com.iqser.red.service.peristence.v1.server.migration.migrations; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + import com.iqser.red.service.peristence.v1.server.migration.Migration; import com.iqser.red.service.peristence.v1.server.service.ManualRedactionProviderService; import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.IdRemovalEntity; import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualRedactionEntryEntity; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.AddRedactionPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.RemoveRedactionPersistenceService; + import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.List; @Slf4j @Setter diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/DictionaryToEntityMigration2.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/DictionaryToEntityMigration2.java index df66cb859..37df3a725 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/DictionaryToEntityMigration2.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/DictionaryToEntityMigration2.java @@ -1,5 +1,10 @@ package com.iqser.red.service.peristence.v1.server.migration.migrations; +import java.util.ArrayList; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.iqser.red.service.peristence.v1.server.migration.Migration; @@ -9,12 +14,9 @@ import com.iqser.red.service.persistence.management.v1.processor.service.persist import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService; import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileType; import com.iqser.red.service.redaction.v1.model.RedactionLogEntry; + import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; @Slf4j @Setter diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/EntityTypesMigration4.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/EntityTypesMigration4.java index 2d1e3ce8a..5f7668180 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/EntityTypesMigration4.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/EntityTypesMigration4.java @@ -1,18 +1,20 @@ package com.iqser.red.service.peristence.v1.server.migration.migrations; +import java.util.List; +import java.util.Set; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + import com.iqser.red.service.peristence.v1.server.migration.Migration; import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.BaseDictionaryEntry; import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.EntryPersistenceService; import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionaryEntryType; + import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Set; @Slf4j @Setter diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/IndexMigration1.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/IndexMigration1.java index 560e1d8e6..c1523c7e0 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/IndexMigration1.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/IndexMigration1.java @@ -1,11 +1,13 @@ package com.iqser.red.service.peristence.v1.server.migration.migrations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + import com.iqser.red.service.peristence.v1.server.migration.Migration; import com.iqser.red.service.peristence.v1.server.service.IndexingService; + import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; @Slf4j @Setter diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/MigrateHighlights3.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/MigrateHighlights3.java index fb8b907b1..c68679510 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/MigrateHighlights3.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/MigrateHighlights3.java @@ -1,15 +1,17 @@ package com.iqser.red.service.peristence.v1.server.migration.migrations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + import com.iqser.red.service.peristence.v1.server.migration.Migration; import com.iqser.red.service.peristence.v1.server.service.FileManagementStorageService; import com.iqser.red.service.peristence.v1.server.service.FileStatusService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService; import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileType; + import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; import java.io.ByteArrayInputStream; diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/RemoveFalsePositiveManualRedactions6.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/RemoveFalsePositiveManualRedactions6.java index eed84cff6..f160bca2a 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/RemoveFalsePositiveManualRedactions6.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/RemoveFalsePositiveManualRedactions6.java @@ -1,16 +1,18 @@ package com.iqser.red.service.peristence.v1.server.migration.migrations; +import java.util.ArrayList; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + import com.iqser.red.service.peristence.v1.server.controller.ManualRedactionController; import com.iqser.red.service.peristence.v1.server.migration.Migration; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.AddRedactionPersistenceService; + import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; @Slf4j @Setter diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/TypeToEntityMigration5.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/TypeToEntityMigration5.java index e28283485..b5867ea61 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/TypeToEntityMigration5.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/migrations/TypeToEntityMigration5.java @@ -1,5 +1,15 @@ package com.iqser.red.service.peristence.v1.server.migration.migrations; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + import com.iqser.red.service.peristence.v1.server.controller.DictionaryController; import com.iqser.red.service.peristence.v1.server.migration.Migration; import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.BaseDictionaryEntry; @@ -7,17 +17,9 @@ import com.iqser.red.service.persistence.management.v1.processor.service.persist import com.iqser.red.service.persistence.management.v1.processor.service.persistence.EntryPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierTemplateRepository; import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionaryEntryType; + import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Locale; -import java.util.Set; -import java.util.regex.Pattern; -import java.util.stream.Collectors; @Slf4j @Setter diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/AsyncConfig.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/AsyncConfig.java new file mode 100644 index 000000000..3ebfe3b30 --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/AsyncConfig.java @@ -0,0 +1,27 @@ +package com.iqser.red.service.peristence.v1.server.multitenancy; + +import java.util.concurrent.Executor; + +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurerSupport; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@Configuration +public class AsyncConfig extends AsyncConfigurerSupport { + + @Override + public Executor getAsyncExecutor() { + + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + + executor.setCorePoolSize(7); + executor.setMaxPoolSize(42); + executor.setQueueCapacity(11); + executor.setThreadNamePrefix("TenantAwareTaskExecutor-"); + executor.setTaskDecorator(new TenantAwareTaskDecorator()); + executor.initialize(); + + return executor; + } + +} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/MultiTenancyWebConfiguration.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/MultiTenancyWebConfiguration.java new file mode 100644 index 000000000..a50c05623 --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/MultiTenancyWebConfiguration.java @@ -0,0 +1,28 @@ +package com.iqser.red.service.peristence.v1.server.multitenancy; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; + +import com.iqser.red.commons.spring.DefaultWebMvcConfiguration; + +@Configuration +public class MultiTenancyWebConfiguration extends DefaultWebMvcConfiguration { + + private final TenantInterceptor tenantInterceptor; + + + @Autowired + public MultiTenancyWebConfiguration(TenantInterceptor tenantInterceptor) { + + this.tenantInterceptor = tenantInterceptor; + } + + + @Override + public void addInterceptors(InterceptorRegistry registry) { + + registry.addWebRequestInterceptor(tenantInterceptor); + } + +} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/TenantAwareTaskDecorator.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/TenantAwareTaskDecorator.java new file mode 100644 index 000000000..519483347 --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/TenantAwareTaskDecorator.java @@ -0,0 +1,25 @@ +package com.iqser.red.service.peristence.v1.server.multitenancy; + +import org.springframework.core.task.TaskDecorator; +import org.springframework.lang.NonNull; + +import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext; + +public class TenantAwareTaskDecorator implements TaskDecorator { + + @Override + @NonNull + public Runnable decorate(@NonNull Runnable runnable) { + + String tenantId = TenantContext.getTenantId(); + return () -> { + try { + TenantContext.setTenantId(tenantId); + runnable.run(); + } finally { + TenantContext.setTenantId(null); + } + }; + } + +} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/TenantInterceptor.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/TenantInterceptor.java new file mode 100644 index 000000000..187702836 --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/TenantInterceptor.java @@ -0,0 +1,41 @@ +package com.iqser.red.service.peristence.v1.server.multitenancy; + +import org.springframework.stereotype.Component; +import org.springframework.ui.ModelMap; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.context.request.WebRequestInterceptor; + +import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext; + +@Component +public class TenantInterceptor implements WebRequestInterceptor { + + public static final String DEFAULT_TENANT = "redaction"; + + + @Override + public void preHandle(WebRequest request) { + + String tenantId = null; + if (request.getHeader("X-TENANT-ID") != null) { + tenantId = request.getHeader("X-TENANT-ID"); + } else { + tenantId = DEFAULT_TENANT; // TODO Remove if multitenancy is fully integrated. + } + TenantContext.setTenantId(tenantId); + } + + + @Override + public void postHandle(WebRequest request, ModelMap model) { + + TenantContext.clear(); + } + + + @Override + public void afterCompletion(WebRequest request, Exception ex) { + + } + +} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/persistence/MasterPersistenceConfig.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/persistence/MasterPersistenceConfig.java new file mode 100644 index 000000000..2ca07e6a4 --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/persistence/MasterPersistenceConfig.java @@ -0,0 +1,115 @@ +package com.iqser.red.service.peristence.v1.server.multitenancy.persistence; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; + +import org.hibernate.cfg.AvailableSettings; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseDataSource; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; +import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.orm.hibernate5.SpringBeanContainer; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; + +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository.TenantRepository; +import com.zaxxer.hikari.HikariDataSource; + +import liquibase.integration.spring.SpringLiquibase; +import lombok.RequiredArgsConstructor; + +@Configuration +@EnableJpaRepositories(basePackageClasses = TenantRepository.class, entityManagerFactoryRef = "masterEntityManagerFactory", transactionManagerRef = "masterTransactionManager") +@EnableConfigurationProperties({DataSourceProperties.class, JpaProperties.class}) +@RequiredArgsConstructor +public class MasterPersistenceConfig { + + private final ConfigurableListableBeanFactory beanFactory; + private final JpaProperties jpaProperties; + private final String entityPackages = "com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity"; + + + @Bean + public LocalContainerEntityManagerFactoryBean masterEntityManagerFactory( + @Qualifier("masterDataSource") DataSource dataSource) { + + LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + + em.setPersistenceUnitName("master-persistence-unit"); + em.setPackagesToScan(entityPackages); + em.setDataSource(dataSource); + em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + + Map properties = new HashMap<>(this.jpaProperties.getProperties()); + properties.put(AvailableSettings.PHYSICAL_NAMING_STRATEGY, "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy"); + properties.put(AvailableSettings.IMPLICIT_NAMING_STRATEGY, "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy"); + properties.put(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(this.beanFactory)); + em.setJpaPropertyMap(properties); + + return em; + } + + + @Bean + public JpaTransactionManager masterTransactionManager( + @Qualifier("masterEntityManagerFactory") EntityManagerFactory emf) { + + JpaTransactionManager transactionManager = new JpaTransactionManager(); + transactionManager.setEntityManagerFactory(emf); + return transactionManager; + } + + + @Bean + @ConfigurationProperties("multitenancy.master.liquibase") + public LiquibaseProperties masterLiquibaseProperties() { + + return new LiquibaseProperties(); + } + + + @Bean + public SpringLiquibase masterLiquibase(@LiquibaseDataSource ObjectProvider liquibaseDataSource) { + + LiquibaseProperties liquibaseProperties = masterLiquibaseProperties(); + SpringLiquibase liquibase = new SpringLiquibase(); + liquibase.setDataSource(liquibaseDataSource.getIfAvailable()); + liquibase.setChangeLog(liquibaseProperties.getChangeLog()); + liquibase.setContexts(liquibaseProperties.getContexts()); + return liquibase; + } + + + @Bean + @ConfigurationProperties("multitenancy.master.datasource") + public DataSourceProperties masterDataSourceProperties() { + + return new DataSourceProperties(); + } + + + @Bean + @LiquibaseDataSource + @ConfigurationProperties("multitenancy.master.datasource.hikari") + public DataSource masterDataSource() { + + HikariDataSource dataSource = masterDataSourceProperties().initializeDataSourceBuilder() + .type(HikariDataSource.class) + .build(); + dataSource.setPoolName("masterDataSource"); + return dataSource; + } + +} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/persistence/TenantPersistenceConfig.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/persistence/TenantPersistenceConfig.java new file mode 100644 index 000000000..7c53cfabf --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/persistence/TenantPersistenceConfig.java @@ -0,0 +1,97 @@ +package com.iqser.red.service.peristence.v1.server.multitenancy.persistence; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.EntityManagerFactory; + +import org.hibernate.MultiTenancyStrategy; +import org.hibernate.cfg.AvailableSettings; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; +import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import org.springframework.context.annotation.Primary; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.orm.hibernate5.SpringBeanContainer; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; + +import com.iqser.red.service.persistence.management.v1.processor.service.EncryptionDecryptionService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.CurrentTenantIdentifierResolverImpl; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.DynamicDataSourceBasedMultiTenantConnectionProvider; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository.TenantRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ColorsRepository; + +import lombok.RequiredArgsConstructor; + +@Configuration +@EnableJpaRepositories(basePackageClasses = ColorsRepository.class, entityManagerFactoryRef = "tenantEntityManagerFactory", transactionManagerRef = "tenantTransactionManager") +@EnableConfigurationProperties(JpaProperties.class) +@RequiredArgsConstructor +public class TenantPersistenceConfig { + + private final ConfigurableListableBeanFactory beanFactory; + private final JpaProperties jpaProperties; + private final String entityPackages = "com.iqser.red.service.persistence.management.v1.processor.entity"; + + + @Primary + @Bean + public LocalContainerEntityManagerFactoryBean tenantEntityManagerFactory( + DynamicDataSourceBasedMultiTenantConnectionProvider connectionProvider, + CurrentTenantIdentifierResolverImpl tenantResolver) { + + LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean(); + emfBean.setPersistenceUnitName("tenant-persistence-unit"); + emfBean.setPackagesToScan(entityPackages); + emfBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + + Map properties = new HashMap<>(this.jpaProperties.getProperties()); + properties.put(AvailableSettings.PHYSICAL_NAMING_STRATEGY, "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy"); + properties.put(AvailableSettings.IMPLICIT_NAMING_STRATEGY, "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy"); + properties.put(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(this.beanFactory)); + properties.put(AvailableSettings.MULTI_TENANT, MultiTenancyStrategy.DATABASE); + properties.put(AvailableSettings.MULTI_TENANT_CONNECTION_PROVIDER, connectionProvider); + properties.put(AvailableSettings.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantResolver); + emfBean.setJpaPropertyMap(properties); + + return emfBean; + } + + + @Primary + @Bean + public JpaTransactionManager tenantTransactionManager( + @Qualifier("tenantEntityManagerFactory") EntityManagerFactory emf) { + + JpaTransactionManager tenantTransactionManager = new JpaTransactionManager(); + tenantTransactionManager.setEntityManagerFactory(emf); + return tenantTransactionManager; + } + + + @Bean + @ConfigurationProperties("multitenancy.tenant.liquibase") + public LiquibaseProperties tenantLiquibaseProperties() { + + return new LiquibaseProperties(); + } + + + @Bean + @DependsOn("masterLiquibase") + public TenantSpringLiquibaseExecutor tenantLiquibase(EncryptionDecryptionService encryptionService, + TenantRepository tenantRepository, + LiquibaseProperties tenantLiquibaseProperties) { + + return new TenantSpringLiquibaseExecutor(encryptionService, tenantRepository, tenantLiquibaseProperties); + } + +} diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/persistence/TenantSpringLiquibaseExecutor.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/persistence/TenantSpringLiquibaseExecutor.java new file mode 100644 index 000000000..452ca1939 --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/persistence/TenantSpringLiquibaseExecutor.java @@ -0,0 +1,74 @@ +package com.iqser.red.service.peristence.v1.server.multitenancy.persistence; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.util.List; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; +import org.springframework.context.ResourceLoaderAware; +import org.springframework.core.io.ResourceLoader; +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 liquibase.integration.spring.SpringLiquibase; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RequiredArgsConstructor +public class TenantSpringLiquibaseExecutor implements InitializingBean, ResourceLoaderAware { + + private final EncryptionDecryptionService encryptionService; + private final TenantRepository tenantRepository; + + @Qualifier("tenantLiquibaseProperties") + private final LiquibaseProperties tenantLiquibaseProperties; + + @Setter + private ResourceLoader resourceLoader; + + + @Override + public void afterPropertiesSet() { + + log.info("DynamicDataSources based multitenancy enabled"); + this.runOnAllTenants(tenantRepository.findAll()); + } + + + @SneakyThrows + protected void runOnAllTenants(List tenants) { + + for (TenantEntity tenant : tenants) { + + log.info("Initializing Liquibase for tenant " + tenant.getTenantId()); + try (Connection connection = DriverManager.getConnection(tenant.getJdbcUrl(), tenant.getUser(), encryptionService.decrypt(tenant.getPassword()))) { + DataSource tenantDataSource = new SingleConnectionDataSource(connection, false); + SpringLiquibase liquibase = this.getSpringLiquibase(tenantDataSource); + liquibase.afterPropertiesSet(); + } + log.info("Liquibase ran for tenant " + tenant.getTenantId()); + } + } + + + protected SpringLiquibase getSpringLiquibase(DataSource dataSource) { + + SpringLiquibase liquibase = new SpringLiquibase(); + liquibase.setResourceLoader(resourceLoader); + liquibase.setDataSource(dataSource); + liquibase.setChangeLog(tenantLiquibaseProperties.getChangeLog()); + liquibase.setContexts(tenantLiquibaseProperties.getContexts()); + return liquibase; + } + +} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/DossierTemplateStatsService.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/DossierTemplateStatsService.java index 39e52eaf0..464d2ed88 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/DossierTemplateStatsService.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/DossierTemplateStatsService.java @@ -1,5 +1,15 @@ package com.iqser.red.service.peristence.v1.server.service; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Service; + import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity; import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService; @@ -12,12 +22,9 @@ import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.Do import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.DossierTemplateStatus; import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionarySummary; import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionarySummaryResponse; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.*; -import java.util.stream.Collectors; @Slf4j @Service @@ -49,7 +56,7 @@ public class DossierTemplateStatsService { List templates = allUnDeletedTemplates.stream().filter(dt -> !DossierTemplateStatus.INCOMPLETE.equals(dt.getDossierTemplateStatus())).collect(Collectors.toList()); final List dossierTemplateStatsList = Collections.synchronizedList(new ArrayList<>()); - templates.parallelStream().forEach(dt -> dossierTemplateStatsList.add(getDossierTemplateStats(dt.getId(), dt.getName(), dt.getDossierTemplateStatus()))); + templates.stream().forEach(dt -> dossierTemplateStatsList.add(getDossierTemplateStats(dt.getId(), dt.getName(), dt.getDossierTemplateStatus()))); List result = dossierTemplateStatsList.stream().filter(stat -> !(DossierTemplateStatus.INACTIVE.equals(stat.getDossierTemplateStatus()) && stat.getNumberOfActiveDossiers() == 0 && stat.getNumberOfArchivedDossiers() == 0)).collect(Collectors.toList()); diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/TenantManagementService.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/TenantManagementService.java new file mode 100644 index 000000000..71d258570 --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/TenantManagementService.java @@ -0,0 +1,87 @@ +package com.iqser.red.service.peristence.v1.server.service; + +import java.sql.Connection; +import java.sql.DriverManager; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +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.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.service.v1.api.model.multitenancy.Tenant; + +import liquibase.exception.LiquibaseException; +import liquibase.integration.spring.SpringLiquibase; +import lombok.SneakyThrows; + +@Service +@EnableConfigurationProperties(LiquibaseProperties.class) +public class TenantManagementService { + + private final EncryptionDecryptionService encryptionService; + + private final LiquibaseProperties liquibaseProperties; + private final ResourceLoader resourceLoader; + private final TenantRepository tenantRepository; + + + public TenantManagementService(EncryptionDecryptionService encryptionService, + @Qualifier("tenantLiquibaseProperties") LiquibaseProperties liquibaseProperties, + ResourceLoader resourceLoader, TenantRepository tenantRepository) { + + this.encryptionService = encryptionService; + this.liquibaseProperties = liquibaseProperties; + this.resourceLoader = resourceLoader; + this.tenantRepository = tenantRepository; + + } + + + @SneakyThrows + public void createTenant(Tenant tenant) { + + if (!tenantRepository.findById(tenant.getTenantId()).isPresent()) { + + String encryptedPassword = encryptionService.encrypt(tenant.getPassword()); + + try (Connection connection = DriverManager.getConnection(tenant.getJdbcUrl(), tenant.getUser(), tenant.getPassword())) { + DataSource tenantDataSource = new SingleConnectionDataSource(connection, false); + runLiquibase(tenantDataSource); + } + + TenantEntity tenantEntity = TenantEntity.builder() + .tenantId(tenant.getTenantId()) + .user(tenant.getUser()) + .jdbcUrl(tenant.getJdbcUrl()) + .password(encryptedPassword) + .build(); + tenantRepository.save(tenantEntity); + } + } + + + private void runLiquibase(DataSource dataSource) throws LiquibaseException { + + SpringLiquibase liquibase = getSpringLiquibase(dataSource); + liquibase.afterPropertiesSet(); + } + + + protected SpringLiquibase getSpringLiquibase(DataSource dataSource) { + + SpringLiquibase liquibase = new SpringLiquibase(); + liquibase.setResourceLoader(resourceLoader); + liquibase.setDataSource(dataSource); + liquibase.setChangeLog(liquibaseProperties.getChangeLog()); + liquibase.setContexts(liquibaseProperties.getContexts()); + return liquibase; + } + +} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/AutomaticAnalysisJob.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/AutomaticAnalysisJob.java index 7d52fd0db..b6f493379 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/AutomaticAnalysisJob.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/AutomaticAnalysisJob.java @@ -1,20 +1,22 @@ package com.iqser.red.service.peristence.v1.server.service.job; -import com.iqser.red.service.peristence.v1.server.configuration.MessagingConfiguration; -import com.iqser.red.service.peristence.v1.server.service.FileStatusService; -import com.iqser.red.service.peristence.v1.server.settings.FileManagementServiceSettings; -import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileModel; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; +import java.util.Comparator; +import java.util.List; + import org.quartz.Job; import org.quartz.JobExecutionContext; import org.springframework.amqp.core.AmqpAdmin; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; +import com.iqser.red.service.peristence.v1.server.configuration.MessagingConfiguration; +import com.iqser.red.service.peristence.v1.server.service.FileStatusService; +import com.iqser.red.service.peristence.v1.server.settings.FileManagementServiceSettings; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository.TenantRepository; +import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext; +import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileModel; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @Slf4j @Service @@ -24,6 +26,7 @@ public class AutomaticAnalysisJob implements Job { private final AmqpAdmin amqpAdmin; private final FileManagementServiceSettings fileManagementServiceSettings; private final FileStatusService fileStatusService; + private final TenantRepository tenantRepository; @Override @@ -34,42 +37,48 @@ public class AutomaticAnalysisJob implements Job { return; } - var redactionQueueInfo = amqpAdmin.getQueueInfo(MessagingConfiguration.REDACTION_QUEUE); - if (redactionQueueInfo != null) { - log.info("Checking queue status to see if background analysis can happen. Currently {} holds {} elements and has {} consumers", MessagingConfiguration.REDACTION_QUEUE, redactionQueueInfo - .getMessageCount(), redactionQueueInfo.getConsumerCount()); - // only 1 file in queue - var consumerCount = redactionQueueInfo.getConsumerCount(); - if (redactionQueueInfo.getMessageCount() <= consumerCount * 5) { - // queue up 5 files - var allStatuses = getAllRelevantStatuses(); - allStatuses.sort(Comparator.comparing(FileModel::getLastUpdated)); + tenantRepository.findAll().forEach(tenant -> { - var allStatusesIterator = allStatuses.iterator(); - log.info("Files that require reanalysis: {}", allStatuses.size()); + TenantContext.setTenantId(tenant.getTenantId()); - var queuedFiles = 0; + var redactionQueueInfo = amqpAdmin.getQueueInfo(MessagingConfiguration.REDACTION_QUEUE); + if (redactionQueueInfo != null) { + log.info("Checking queue status to see if background analysis can happen. Currently {} holds {} elements and has {} consumers", MessagingConfiguration.REDACTION_QUEUE, redactionQueueInfo.getMessageCount(), redactionQueueInfo.getConsumerCount()); + // only 1 file in queue + var consumerCount = redactionQueueInfo.getConsumerCount(); + if (redactionQueueInfo.getMessageCount() <= consumerCount * 5) { + // queue up 5 files + var allStatuses = getAllRelevantStatuses(); - while (queuedFiles < (consumerCount * 5) && allStatusesIterator.hasNext()) { - var next = allStatusesIterator.next(); - // in case the file doesn't have numberOfPages set, we assume an average. + allStatuses.sort(Comparator.comparing(FileModel::getLastUpdated)); - if (next.isFullAnalysisRequired()) { - log.info("Queued file: {} for automatic full analysis! ", next.getFilename()); - fileStatusService.setStatusFullReprocess(next.getDossierId(), next.getId(), false, false); - } else if (next.isReanalysisRequired()) { - log.info("Queued file: {} for automatic reanalysis! ", next.getFilename()); - fileStatusService.setStatusReprocess(next.getDossierId(), next.getId(), false); + var allStatusesIterator = allStatuses.iterator(); + log.info("Files that require reanalysis: {}", allStatuses.size()); + + var queuedFiles = 0; + + while (queuedFiles < (consumerCount * 5) && allStatusesIterator.hasNext()) { + var next = allStatusesIterator.next(); + // in case the file doesn't have numberOfPages set, we assume an average. + + if (next.isFullAnalysisRequired()) { + log.info("Queued file: {} for automatic full analysis! ", next.getFilename()); + fileStatusService.setStatusFullReprocess(next.getDossierId(), next.getId(), false, false); + } else if (next.isReanalysisRequired()) { + log.info("Queued file: {} for automatic reanalysis! ", next.getFilename()); + fileStatusService.setStatusReprocess(next.getDossierId(), next.getId(), false); + } + + queuedFiles++; } - - queuedFiles++; } + + } else { + log.info("Failed to obtain queue info for queue: {}", MessagingConfiguration.REDACTION_QUEUE); } - } else { - log.info("Failed to obtain queue info for queue: {}", MessagingConfiguration.REDACTION_QUEUE); - } + }); } diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/DeletedFilesCleanupJob.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/DeletedFilesCleanupJob.java index 8dd17933f..44938f318 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/DeletedFilesCleanupJob.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/DeletedFilesCleanupJob.java @@ -3,7 +3,6 @@ package com.iqser.red.service.peristence.v1.server.service.job; import java.time.OffsetDateTime; import java.util.List; -import com.iqser.red.service.persistence.management.v1.processor.service.ApplicationConfigService; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.springframework.stereotype.Service; @@ -12,6 +11,9 @@ import com.iqser.red.service.peristence.v1.server.service.DossierService; import com.iqser.red.service.peristence.v1.server.service.FileService; import com.iqser.red.service.peristence.v1.server.service.FileStatusService; import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity; +import com.iqser.red.service.persistence.management.v1.processor.service.ApplicationConfigService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository.TenantRepository; +import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -25,33 +27,39 @@ public class DeletedFilesCleanupJob implements Job { private final FileStatusService fileStatusService; private final FileService fileService; private final ApplicationConfigService applicationConfigService; + private final TenantRepository tenantRepository; @Override public void execute(JobExecutionContext jobExecutionContext) { - var now = OffsetDateTime.now(); - List dossiers = dossierService.getAllDossiers(); - var applicationConfigurationEntity = applicationConfigService.getApplicationConfig(); + tenantRepository.findAll().forEach(tenant -> { - for (DossierEntity dossierEntity : dossiers) { - if (dossierEntity.getSoftDeletedTime() != null && dossierEntity.getHardDeletedTime() == null) { - if (dossierEntity.getSoftDeletedTime().isBefore(now.minusHours(applicationConfigurationEntity.getSoftDeleteCleanupTime()))) { - dossierService.hardDeleteDossier(dossierEntity.getId()); - log.info("Hard deleted dossier with dossier id {} ", dossierEntity.getId()); - } - } else { - var files = fileStatusService.getDossierStatus(dossierEntity.getId()); - for (var file : files) { - if (file.getHardDeletedTime() == null && file.getDeleted() != null && file.getDeleted() - .isBefore(now.minusHours(applicationConfigurationEntity.getSoftDeleteCleanupTime()))) { - fileService.hardDeleteFile(dossierEntity.getId(), file.getId()); - fileStatusService.setFileStatusHardDeleted(file.getId()); - log.info("Hard deleted file with dossier id {} and file id {}", dossierEntity.getId(), file.getId()); + TenantContext.setTenantId(tenant.getTenantId()); + + var now = OffsetDateTime.now(); + List dossiers = dossierService.getAllDossiers(); + var applicationConfigurationEntity = applicationConfigService.getApplicationConfig(); + + for (DossierEntity dossierEntity : dossiers) { + if (dossierEntity.getSoftDeletedTime() != null && dossierEntity.getHardDeletedTime() == null) { + if (dossierEntity.getSoftDeletedTime().isBefore(now.minusHours(applicationConfigurationEntity.getSoftDeleteCleanupTime()))) { + dossierService.hardDeleteDossier(dossierEntity.getId()); + log.info("Hard deleted dossier with dossier id {} ", dossierEntity.getId()); + } + } else { + var files = fileStatusService.getDossierStatus(dossierEntity.getId()); + for (var file : files) { + if (file.getHardDeletedTime() == null && file.getDeleted() != null && file.getDeleted().isBefore(now.minusHours(applicationConfigurationEntity.getSoftDeleteCleanupTime()))) { + fileService.hardDeleteFile(dossierEntity.getId(), file.getId()); + fileStatusService.setFileStatusHardDeleted(file.getId()); + log.info("Hard deleted file with dossier id {} and file id {}", dossierEntity.getId(), file.getId()); + } } } } - } + + }); } diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/DownloadCleanupJob.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/DownloadCleanupJob.java index a74038cff..77b5c6fea 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/DownloadCleanupJob.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/DownloadCleanupJob.java @@ -1,19 +1,23 @@ package com.iqser.red.service.peristence.v1.server.service.job; +import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; + +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.springframework.stereotype.Service; + import com.iqser.red.service.peristence.v1.server.service.DossierService; import com.iqser.red.service.persistence.management.v1.processor.entity.download.DownloadStatusEntity; import com.iqser.red.service.persistence.management.v1.processor.service.ApplicationConfigService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository.TenantRepository; +import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext; import com.iqser.red.storage.commons.service.StorageService; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.quartz.Job; -import org.quartz.JobExecutionContext; -import org.springframework.stereotype.Service; - -import java.time.OffsetDateTime; -import java.time.temporal.ChronoUnit; -import java.util.List; @Slf4j @RequiredArgsConstructor @@ -24,35 +28,39 @@ public class DownloadCleanupJob implements Job { private final StorageService storageService; private final DossierService dossierService; private final ApplicationConfigService applicationConfigService; + private final TenantRepository tenantRepository; @Override public void execute(JobExecutionContext jobExecutionContext) { - var now = OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS); - log.info("Checking for downloads to delete at {}", now); - List downloadStatusList = downloadStatusPersistenceService.getStatus(); - downloadStatusList.forEach(downloadStatus -> { + tenantRepository.findAll().forEach(tenant -> { - var dossier = downloadStatus.getDossier(); - var applicationConfigurationEntity = applicationConfigService.getApplicationConfig(); - if (downloadStatus.getLastDownload() != null && downloadStatus.getLastDownload() - .plusHours(applicationConfigurationEntity.getDownloadCleanupDownloadFilesHours()) - .isBefore(now)) { - log.info("1. Deleting download status {} because DownloadCleanupDownloadFilesHours is {} and c+h {} is after {}", downloadStatus, - applicationConfigurationEntity.getDownloadCleanupDownloadFilesHours(), downloadStatus.getCreationDate() - .plusHours(applicationConfigurationEntity.getDownloadCleanupDownloadFilesHours()), now); - deleteDownload(downloadStatus); - } else if (downloadStatus.getLastDownload() == null && downloadStatus.getCreationDate() - .plusHours(applicationConfigurationEntity.getDownloadCleanupNotDownloadFilesHours()) - .isBefore(now)) { - log.info("2. Deleting download status {} because DownloadCleanupNotDownloadFilesHours is {} and c+h {} is after {}", downloadStatus, - applicationConfigurationEntity.getDownloadCleanupNotDownloadFilesHours(), downloadStatus.getCreationDate() - .plusHours(applicationConfigurationEntity.getDownloadCleanupNotDownloadFilesHours()), now); - deleteDownload(downloadStatus); - } else if (dossierService.getDossierById(dossier.getId()).getSoftDeletedTime() != null) { - log.info("3. Deleting download {}, because dossier does not exist", downloadStatus.getStorageId()); - deleteDownload(downloadStatus); - } + TenantContext.setTenantId(tenant.getTenantId()); + + var now = OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS); + log.info("Checking for downloads to delete at {}", now); + List downloadStatusList = downloadStatusPersistenceService.getStatus(); + downloadStatusList.forEach(downloadStatus -> { + + var dossier = downloadStatus.getDossier(); + var applicationConfigurationEntity = applicationConfigService.getApplicationConfig(); + if (downloadStatus.getLastDownload() != null && downloadStatus.getLastDownload() + .plusHours(applicationConfigurationEntity.getDownloadCleanupDownloadFilesHours()) + .isBefore(now)) { + log.info("1. Deleting download status {} because DownloadCleanupDownloadFilesHours is {} and c+h {} is after {}", downloadStatus, applicationConfigurationEntity.getDownloadCleanupDownloadFilesHours(), downloadStatus.getCreationDate() + .plusHours(applicationConfigurationEntity.getDownloadCleanupDownloadFilesHours()), now); + deleteDownload(downloadStatus); + } else if (downloadStatus.getLastDownload() == null && downloadStatus.getCreationDate() + .plusHours(applicationConfigurationEntity.getDownloadCleanupNotDownloadFilesHours()) + .isBefore(now)) { + log.info("2. Deleting download status {} because DownloadCleanupNotDownloadFilesHours is {} and c+h {} is after {}", downloadStatus, applicationConfigurationEntity.getDownloadCleanupNotDownloadFilesHours(), downloadStatus.getCreationDate() + .plusHours(applicationConfigurationEntity.getDownloadCleanupNotDownloadFilesHours()), now); + deleteDownload(downloadStatus); + } else if (dossierService.getDossierById(dossier.getId()).getSoftDeletedTime() != null) { + log.info("3. Deleting download {}, because dossier does not exist", downloadStatus.getStorageId()); + deleteDownload(downloadStatus); + } + }); }); } diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/SendNotificationEmailJob.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/SendNotificationEmailJob.java index 07adc7298..98f50781b 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/SendNotificationEmailJob.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/job/SendNotificationEmailJob.java @@ -1,19 +1,23 @@ package com.iqser.red.service.peristence.v1.server.service.job; -import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationEmailService; -import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationPersistenceService; -import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationPreferencesPersistenceService; -import com.iqser.red.service.persistence.service.v1.api.model.notification.EmailNotificationType; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.quartz.Job; -import org.quartz.JobExecutionContext; -import org.springframework.stereotype.Service; - import java.time.DayOfWeek; import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.springframework.stereotype.Service; + +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationEmailService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationPersistenceService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationPreferencesPersistenceService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository.TenantRepository; +import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext; +import com.iqser.red.service.persistence.service.v1.api.model.notification.EmailNotificationType; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + @Slf4j @RequiredArgsConstructor @Service @@ -22,37 +26,41 @@ public class SendNotificationEmailJob implements Job { private final NotificationEmailService notificationEmailService; private final NotificationPersistenceService notificationPersistenceService; private final NotificationPreferencesPersistenceService notificationPreferencesPersistenceService; + private final TenantRepository tenantRepository; @Override public void execute(JobExecutionContext jobExecutionContext) { + tenantRepository.findAll().forEach(tenant -> { - var allConfiguredPreferences = notificationPreferencesPersistenceService.findAll(); - allConfiguredPreferences.forEach(preference -> { - if (preference.isEmailNotificationsEnabled() && preference.getEmailNotificationType() == EmailNotificationType.WEEKLY_SUMMARY || preference.getEmailNotificationType() == EmailNotificationType.DAILY_SUMMARY) { + TenantContext.setTenantId(tenant.getTenantId()); - var now = OffsetDateTime.now().truncatedTo(ChronoUnit.HOURS); + var allConfiguredPreferences = notificationPreferencesPersistenceService.findAll(); + allConfiguredPreferences.forEach(preference -> { + if (preference.isEmailNotificationsEnabled() && preference.getEmailNotificationType() == EmailNotificationType.WEEKLY_SUMMARY || preference.getEmailNotificationType() == EmailNotificationType.DAILY_SUMMARY) { - // Weekly summary is sent monday night - if (now.getDayOfWeek() == DayOfWeek.MONDAY && preference.getEmailNotificationType() == EmailNotificationType.WEEKLY_SUMMARY) { - var from = OffsetDateTime.now().minusDays(7).truncatedTo(ChronoUnit.HOURS).withHour(0); - var to = OffsetDateTime.now().truncatedTo(ChronoUnit.HOURS).withHour(0); - var notifications = notificationPersistenceService.getNotificationsForEmailSummary(preference.getUserId(), from, to); + var now = OffsetDateTime.now().truncatedTo(ChronoUnit.HOURS); - notificationEmailService.sendNotificationEmail(preference.getUserId(), EmailNotificationType.WEEKLY_SUMMARY, notifications); - } else { - var from = OffsetDateTime.now().minusDays(1).withHour(0).truncatedTo(ChronoUnit.HOURS); - var to = OffsetDateTime.now().minusDays(1).withHour(23).truncatedTo(ChronoUnit.HOURS); - var notifications = notificationPersistenceService.getNotificationsForEmailSummary(preference.getUserId(), from, to); + // Weekly summary is sent monday night + if (now.getDayOfWeek() == DayOfWeek.MONDAY && preference.getEmailNotificationType() == EmailNotificationType.WEEKLY_SUMMARY) { + var from = OffsetDateTime.now().minusDays(7).truncatedTo(ChronoUnit.HOURS).withHour(0); + var to = OffsetDateTime.now().truncatedTo(ChronoUnit.HOURS).withHour(0); + var notifications = notificationPersistenceService.getNotificationsForEmailSummary(preference.getUserId(), from, to); - notificationEmailService.sendNotificationEmail(preference.getUserId(), EmailNotificationType.DAILY_SUMMARY, notifications); + notificationEmailService.sendNotificationEmail(preference.getUserId(), EmailNotificationType.WEEKLY_SUMMARY, notifications); + } else { + var from = OffsetDateTime.now().minusDays(1).withHour(0).truncatedTo(ChronoUnit.HOURS); + var to = OffsetDateTime.now().minusDays(1).withHour(23).truncatedTo(ChronoUnit.HOURS); + var notifications = notificationPersistenceService.getNotificationsForEmailSummary(preference.getUserId(), from, to); + + notificationEmailService.sendNotificationEmail(preference.getUserId(), EmailNotificationType.DAILY_SUMMARY, notifications); + + } } - - } + }); }); - } } diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/application.yml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/application.yml index 9b5420755..d5c09178e 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/resources/application.yml +++ b/persistence-service-v1/persistence-service-server-v1/src/main/resources/application.yml @@ -12,17 +12,6 @@ server: spring: main: allow-circular-references: true # FIXME - datasource: - url: jdbc:postgresql://${PSQL_HOST:localhost}:${PSQL_PORT:5432}/${PSQL_DATABASE:redaction}?cachePrepStmts=true&useServerPrepStmts=true&rewriteBatchedStatements=true - driverClassName: org.postgresql.Driver - username: ${PSQL_USERNAME:redaction} - password: ${PSQL_PASSWORD:redaction} - platform: org.hibernate.dialect.PostgreSQL95Dialect - hikari: - data-source-properties: - cachePrepStmts: true - prepStmtCacheSize: 1000 - prepStmtCacheSqlLimit: 2048 jpa: database-platform: org.hibernate.dialect.PostgreSQL95Dialect hibernate: @@ -89,4 +78,31 @@ storage: endpoint: 'https://s3.amazonaws.com' backend: 's3' - +multitenancy: + datasource-cache: + maximumSize: 100 + expireAfterAccess: 1 + master: + datasource: + url: jdbc:postgresql://${PSQL_HOST:localhost}:${PSQL_PORT:5432}/${PSQL_DATABASE:master}?cachePrepStmts=true&useServerPrepStmts=true&rewriteBatchedStatements=true + driverClassName: org.postgresql.Driver + username: ${PSQL_USERNAME:redaction} + password: ${PSQL_PASSWORD:redaction} + platform: org.hibernate.dialect.PostgreSQL95Dialect + hikari: + data-source-properties: + cachePrepStmts: true + prepStmtCacheSize: 1000 + prepStmtCacheSqlLimit: 2048 + liquibase: + changeLog: classpath:db/changelog/db.changelog-master.yaml + tenant: + datasource: + driverClassName: org.postgresql.Driver + hikari: + data-source-properties: + cachePrepStmts: true + prepStmtCacheSize: 1000 + prepStmtCacheSqlLimit: 2048 + liquibase: + changeLog: classpath:db/changelog/db.changelog-tenant.yaml \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/db.changelog-master.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/db.changelog-master.yaml index cce1da3f9..5f8ea657d 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/db.changelog-master.yaml @@ -1,101 +1,3 @@ databaseChangeLog: - include: - file: db/changelog/1-initial-schema.changelog.yaml - - include: - file: db/changelog/2-ignored-hint-color.changelog.yaml - - include: - file: db/changelog/3-added-annotation-modification-date.changelog.yaml - - include: - file: db/changelog/4-archived-dossier.changelog.yaml - - include: - file: db/changelog/5-excluded-from-automatic-analysis-file-column.changelog.yaml - - include: - file: db/changelog/6-dossier-status-table.changelog.yaml - - include: - file: db/changelog/7-json-column-mapping.changelog.yaml - - include: - file: db/changelog/sql/7.1-set-json-fields.sql - - include: - file: db/changelog/sql/7.2-set-dossier-status.sql - - include: - file: db/changelog/9-changed-annotation-modification-date.changelog.yaml - - include: - file: db/changelog/10-added-file-manipulation-date.changelog.yaml - - include: - file: db/changelog/sql/10-set-file-manipulation-date.sql - - include: - file: db/changelog/11-added-dictionary_false_positive_tables.changelog.yaml - - include: - file: db/changelog/12-dossier-visibility.changelog.yaml - - include: - file: db/changelog/12-added-rank-dossier-status.changelog.yaml - - include: - file: db/changelog/13-file-manual-change-date.changelog.yaml - - include: - file: db/changelog/release-3.2.0/1-add-index-on-dictionary-entry.changelog.yaml - - include: - file: db/changelog/14-add-redaction-source-id.changelog.yaml - - include: - file: db/changelog/15-dossier-remove-dossier-state.changelog.yaml - - include: - file: db/changelog/16-added-has-dictionary-to-entities.changelog.yaml - - include: - file: db/changelog/17-digital-signature-kms.changelog.yaml - - include: - file: db/changelog/18-add-migration-table.yaml - - include: - file: db/changelog/19-added-has-highlights-to-file.changelog.yaml - - include: - file: db/changelog/20-add-index-information-table.yaml - - include: - file: db/changelog/21-added-auto-hide-skipped-to-entities.changelog.yaml - - include: - file: db/changelog/sql/22-update-file-dossier-attributes.sql - - include: - file: db/changelog/22-add-soft-delete-time-to-entity.changelog.yaml - - include: - file: db/changelog/23-quartz.changelog.yaml - - include: - file: db/changelog/sql/24.0-clean-up-duplicate-dictionary-entries.sql - - include: - file: db/changelog/24-add-unique-constraint-for-dictionary.yaml - - include: - file: db/changelog/release-3.3.12/1-fix-dossier-dictionary.sql - - include: - file: db/changelog/25-add-index-to-dictionary-entry-tables.yaml - - include: - file: db/changelog/26-application-config-table.changelog.yaml - - include: - file: db/changelog/sql/26-initiliaze-application-configuration-table.sql - - include: - file: db/changelog/sql/27-update-soft-delete-date.sql - - include: - file: db/changelog/28-add-update-dictionary-to-manual-resize-redactions.yaml - - include: - file: db/changelog/sql/30-change-bigint-to-serial.sql - - include: - file: db/changelog/31-add-file-size-column.changelog.yaml - - include: - file: db/changelog/sql/31-watermark-configuration.sql - - include: - file: db/changelog/32-added-skipped-color-type-table.changelog.yaml - - include: - file: db/changelog/33-add-file-processing-error-counter-column.changelog.yaml - - include: - file: db/changelog/sql/33-set-file-processing-error-counter.sql - - include: - file: db/changelog/34-add-reports-information-column.changelog.yaml - - include: - file: db/changelog/sql/35-update-skipped-color-existing-types.sql - - include: - file: db/changelog/36-revert-reports-information-column.changelog.yaml - - include: - file: db/changelog/sql/37-update-colors.sql - - include: - file: db/changelog/sql/38-update-soft-deleted-processed-flag.sql - - include: - file: db/changelog/sql/39-update-manual-redaction-source-id.sql - - include: - file: db/changelog/release-3.3.13/1-update-soft-deleted-processed-flag.sql - - include: - file: db/changelog/release-3.3.14/1-bump-rules-version.sql + file: db/changelog/master/1-initial-schema.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml new file mode 100644 index 000000000..3f42d0f73 --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml @@ -0,0 +1,101 @@ +databaseChangeLog: + - include: + file: db/changelog/tenant/1-initial-schema.changelog.yaml + - include: + file: db/changelog/tenant/2-ignored-hint-color.changelog.yaml + - include: + file: db/changelog/tenant/3-added-annotation-modification-date.changelog.yaml + - include: + file: db/changelog/tenant/4-archived-dossier.changelog.yaml + - include: + file: db/changelog/tenant/5-excluded-from-automatic-analysis-file-column.changelog.yaml + - include: + file: db/changelog/tenant/6-dossier-status-table.changelog.yaml + - include: + file: db/changelog/tenant/7-json-column-mapping.changelog.yaml + - include: + file: db/changelog/tenant/sql/7.1-set-json-fields.sql + - include: + file: db/changelog/tenant/sql/7.2-set-dossier-status.sql + - include: + file: db/changelog/tenant/9-changed-annotation-modification-date.changelog.yaml + - include: + file: db/changelog/tenant/10-added-file-manipulation-date.changelog.yaml + - include: + file: db/changelog/tenant/sql/10-set-file-manipulation-date.sql + - include: + file: db/changelog/tenant/11-added-dictionary_false_positive_tables.changelog.yaml + - include: + file: db/changelog/tenant/12-dossier-visibility.changelog.yaml + - include: + file: db/changelog/tenant/12-added-rank-dossier-status.changelog.yaml + - include: + file: db/changelog/tenant/13-file-manual-change-date.changelog.yaml + - include: + file: db/changelog/tenant/release-3.2.0/1-add-index-on-dictionary-entry.changelog.yaml + - include: + file: db/changelog/tenant/14-add-redaction-source-id.changelog.yaml + - include: + file: db/changelog/tenant/15-dossier-remove-dossier-state.changelog.yaml + - include: + file: db/changelog/tenant/16-added-has-dictionary-to-entities.changelog.yaml + - include: + file: db/changelog/tenant/17-digital-signature-kms.changelog.yaml + - include: + file: db/changelog/tenant/18-add-migration-table.yaml + - include: + file: db/changelog/tenant/19-added-has-highlights-to-file.changelog.yaml + - include: + file: db/changelog/tenant/20-add-index-information-table.yaml + - include: + file: db/changelog/tenant/21-added-auto-hide-skipped-to-entities.changelog.yaml + - include: + file: db/changelog/tenant/sql/22-update-file-dossier-attributes.sql + - include: + file: db/changelog/tenant/22-add-soft-delete-time-to-entity.changelog.yaml + - include: + file: db/changelog/tenant/23-quartz.changelog.yaml + - include: + file: db/changelog/tenant/sql/24.0-clean-up-duplicate-dictionary-entries.sql + - include: + file: db/changelog/tenant/24-add-unique-constraint-for-dictionary.yaml + - include: + file: db/changelog/tenant/release-3.3.12/1-fix-dossier-dictionary.sql + - include: + file: db/changelog/tenant/25-add-index-to-dictionary-entry-tables.yaml + - include: + file: db/changelog/tenant/26-application-config-table.changelog.yaml + - include: + file: db/changelog/tenant/sql/26-initiliaze-application-configuration-table.sql + - include: + file: db/changelog/tenant/sql/27-update-soft-delete-date.sql + - include: + file: db/changelog/tenant/28-add-update-dictionary-to-manual-resize-redactions.yaml + - include: + file: db/changelog/tenant/sql/30-change-bigint-to-serial.sql + - include: + file: db/changelog/tenant/31-add-file-size-column.changelog.yaml + - include: + file: db/changelog/tenant/sql/31-watermark-configuration.sql + - include: + file: db/changelog/tenant/32-added-skipped-color-type-table.changelog.yaml + - include: + file: db/changelog/tenant/33-add-file-processing-error-counter-column.changelog.yaml + - include: + file: db/changelog/tenant/sql/33-set-file-processing-error-counter.sql + - include: + file: db/changelog/tenant/34-add-reports-information-column.changelog.yaml + - include: + file: db/changelog/tenant/sql/35-update-skipped-color-existing-types.sql + - include: + file: db/changelog/tenant/36-revert-reports-information-column.changelog.yaml + - include: + file: db/changelog/tenant/sql/37-update-colors.sql + - include: + file: db/changelog/tenant/sql/38-update-soft-deleted-processed-flag.sql + - include: + file: db/changelog/tenant/sql/39-update-manual-redaction-source-id.sql + - include: + file: db/changelog/tenant/release-3.3.13/1-update-soft-deleted-processed-flag.sql + - include: + file: db/changelog/tenant/release-3.3.14/1-bump-rules-version.sql \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/master/1-initial-schema.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/master/1-initial-schema.changelog.yaml new file mode 100644 index 000000000..216485c97 --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/master/1-initial-schema.changelog.yaml @@ -0,0 +1,24 @@ +databaseChangeLog: + - changeSet: + id: create-tenants-table + author: dom + changes: + - createTable: + columns: + - column: + constraints: + nullable: false + primaryKey: true + primaryKeyName: tenant_pkey + name: tenant_id + type: VARCHAR(255) + - column: + name: username + type: VARCHAR(255) + - column: + name: password + type: VARCHAR(255) + - column: + name: jdbc_url + type: VARCHAR(255) + tableName: tenant \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/1-initial-schema.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/1-initial-schema.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/1-initial-schema.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/1-initial-schema.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/10-added-file-manipulation-date.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/10-added-file-manipulation-date.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/10-added-file-manipulation-date.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/10-added-file-manipulation-date.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/11-added-dictionary_false_positive_tables.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/11-added-dictionary_false_positive_tables.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/11-added-dictionary_false_positive_tables.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/11-added-dictionary_false_positive_tables.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/12-added-rank-dossier-status.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/12-added-rank-dossier-status.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/12-added-rank-dossier-status.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/12-added-rank-dossier-status.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/12-dossier-visibility.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/12-dossier-visibility.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/12-dossier-visibility.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/12-dossier-visibility.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/13-file-manual-change-date.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/13-file-manual-change-date.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/13-file-manual-change-date.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/13-file-manual-change-date.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/14-add-redaction-source-id.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/14-add-redaction-source-id.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/14-add-redaction-source-id.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/14-add-redaction-source-id.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/15-dossier-remove-dossier-state.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/15-dossier-remove-dossier-state.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/15-dossier-remove-dossier-state.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/15-dossier-remove-dossier-state.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/16-added-has-dictionary-to-entities.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/16-added-has-dictionary-to-entities.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/16-added-has-dictionary-to-entities.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/16-added-has-dictionary-to-entities.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/17-digital-signature-kms.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/17-digital-signature-kms.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/17-digital-signature-kms.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/17-digital-signature-kms.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/18-add-migration-table.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/18-add-migration-table.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/18-add-migration-table.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/18-add-migration-table.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/19-added-has-highlights-to-file.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/19-added-has-highlights-to-file.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/19-added-has-highlights-to-file.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/19-added-has-highlights-to-file.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/2-ignored-hint-color.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/2-ignored-hint-color.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/2-ignored-hint-color.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/2-ignored-hint-color.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/20-add-index-information-table.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/20-add-index-information-table.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/20-add-index-information-table.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/20-add-index-information-table.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/21-added-auto-hide-skipped-to-entities.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/21-added-auto-hide-skipped-to-entities.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/21-added-auto-hide-skipped-to-entities.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/21-added-auto-hide-skipped-to-entities.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/22-add-soft-delete-time-to-entity.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/22-add-soft-delete-time-to-entity.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/22-add-soft-delete-time-to-entity.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/22-add-soft-delete-time-to-entity.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/23-quartz.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/23-quartz.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/23-quartz.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/23-quartz.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/24-add-unique-constraint-for-dictionary.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/24-add-unique-constraint-for-dictionary.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/24-add-unique-constraint-for-dictionary.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/24-add-unique-constraint-for-dictionary.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/25-add-index-to-dictionary-entry-tables.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/25-add-index-to-dictionary-entry-tables.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/25-add-index-to-dictionary-entry-tables.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/25-add-index-to-dictionary-entry-tables.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/26-application-config-table.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/26-application-config-table.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/26-application-config-table.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/26-application-config-table.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/28-add-update-dictionary-to-manual-resize-redactions.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/28-add-update-dictionary-to-manual-resize-redactions.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/28-add-update-dictionary-to-manual-resize-redactions.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/28-add-update-dictionary-to-manual-resize-redactions.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/3-added-annotation-modification-date.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/3-added-annotation-modification-date.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/3-added-annotation-modification-date.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/3-added-annotation-modification-date.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/31-add-file-size-column.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/31-add-file-size-column.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/31-add-file-size-column.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/31-add-file-size-column.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/32-added-skipped-color-type-table.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/32-added-skipped-color-type-table.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/32-added-skipped-color-type-table.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/32-added-skipped-color-type-table.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/33-add-file-processing-error-counter-column.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/33-add-file-processing-error-counter-column.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/33-add-file-processing-error-counter-column.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/33-add-file-processing-error-counter-column.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/34-add-reports-information-column.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/34-add-reports-information-column.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/34-add-reports-information-column.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/34-add-reports-information-column.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/36-revert-reports-information-column.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/36-revert-reports-information-column.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/36-revert-reports-information-column.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/36-revert-reports-information-column.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/4-archived-dossier.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/4-archived-dossier.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/4-archived-dossier.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/4-archived-dossier.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/5-excluded-from-automatic-analysis-file-column.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/5-excluded-from-automatic-analysis-file-column.changelog.yaml similarity index 95% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/5-excluded-from-automatic-analysis-file-column.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/5-excluded-from-automatic-analysis-file-column.changelog.yaml index 36ed9537d..0812016b0 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/5-excluded-from-automatic-analysis-file-column.changelog.yaml +++ b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/5-excluded-from-automatic-analysis-file-column.changelog.yaml @@ -19,6 +19,5 @@ databaseChangeLog: - column: name: excluded_from_automatic_analysis value: true - schemaName: public tableName: file where: workflow_status = 'APPROVED' diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/6-dossier-status-table.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/6-dossier-status-table.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/6-dossier-status-table.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/6-dossier-status-table.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/7-json-column-mapping.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/7-json-column-mapping.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/7-json-column-mapping.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/7-json-column-mapping.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/8-remove-old-dossier-status-column.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/8-remove-old-dossier-status-column.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/8-remove-old-dossier-status-column.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/8-remove-old-dossier-status-column.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/9-changed-annotation-modification-date.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/9-changed-annotation-modification-date.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/9-changed-annotation-modification-date.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/9-changed-annotation-modification-date.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/release-3.2.0/1-add-index-on-dictionary-entry.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/release-3.2.0/1-add-index-on-dictionary-entry.changelog.yaml similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/release-3.2.0/1-add-index-on-dictionary-entry.changelog.yaml rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/release-3.2.0/1-add-index-on-dictionary-entry.changelog.yaml diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/release-3.3.12/1-fix-dossier-dictionary.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/release-3.3.12/1-fix-dossier-dictionary.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/release-3.3.12/1-fix-dossier-dictionary.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/release-3.3.12/1-fix-dossier-dictionary.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/release-3.3.13/1-update-soft-deleted-processed-flag.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/release-3.3.13/1-update-soft-deleted-processed-flag.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/release-3.3.13/1-update-soft-deleted-processed-flag.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/release-3.3.13/1-update-soft-deleted-processed-flag.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/release-3.3.14/1-bump-rules-version.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/release-3.3.14/1-bump-rules-version.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/release-3.3.14/1-bump-rules-version.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/release-3.3.14/1-bump-rules-version.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/10-set-file-manipulation-date.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/10-set-file-manipulation-date.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/10-set-file-manipulation-date.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/10-set-file-manipulation-date.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/22-update-file-dossier-attributes.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/22-update-file-dossier-attributes.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/22-update-file-dossier-attributes.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/22-update-file-dossier-attributes.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/24.0-clean-up-duplicate-dictionary-entries.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/24.0-clean-up-duplicate-dictionary-entries.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/24.0-clean-up-duplicate-dictionary-entries.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/24.0-clean-up-duplicate-dictionary-entries.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/26-initiliaze-application-configuration-table.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/26-initiliaze-application-configuration-table.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/26-initiliaze-application-configuration-table.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/26-initiliaze-application-configuration-table.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/27-update-soft-delete-date.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/27-update-soft-delete-date.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/27-update-soft-delete-date.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/27-update-soft-delete-date.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/30-change-bigint-to-serial.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/30-change-bigint-to-serial.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/30-change-bigint-to-serial.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/30-change-bigint-to-serial.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/31-watermark-configuration.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/31-watermark-configuration.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/31-watermark-configuration.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/31-watermark-configuration.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/33-set-file-processing-error-counter.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/33-set-file-processing-error-counter.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/33-set-file-processing-error-counter.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/33-set-file-processing-error-counter.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/35-update-skipped-color-existing-types.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/35-update-skipped-color-existing-types.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/35-update-skipped-color-existing-types.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/35-update-skipped-color-existing-types.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/37-update-colors.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/37-update-colors.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/37-update-colors.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/37-update-colors.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/38-update-soft-deleted-processed-flag.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/38-update-soft-deleted-processed-flag.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/38-update-soft-deleted-processed-flag.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/38-update-soft-deleted-processed-flag.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/39-update-manual-redaction-source-id.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/39-update-manual-redaction-source-id.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/39-update-manual-redaction-source-id.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/39-update-manual-redaction-source-id.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/7.1-set-json-fields.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/7.1-set-json-fields.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/7.1-set-json-fields.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/7.1-set-json-fields.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/7.2-set-dossier-status.sql b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/7.2-set-dossier-status.sql similarity index 100% rename from persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/sql/7.2-set-dossier-status.sql rename to persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/tenant/sql/7.2-set-dossier-status.sql diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/client/TenantsClient.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/client/TenantsClient.java new file mode 100644 index 000000000..5dbb2bc28 --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/client/TenantsClient.java @@ -0,0 +1,11 @@ +package com.iqser.red.service.peristence.v1.server.integration.client; + +import org.springframework.cloud.openfeign.FeignClient; + +import com.iqser.red.service.persistence.service.v1.api.resources.TenantsResource; + + +@FeignClient(name = "TenantsClient", url = "http://localhost:${server.port}") +public interface TenantsClient extends TenantsResource { + +} diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateStatsTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateStatsTest.java index 958d11585..6f881128a 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateStatsTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateStatsTest.java @@ -1,7 +1,26 @@ package com.iqser.red.service.peristence.v1.server.integration.tests; +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + import com.google.common.collect.Sets; -import com.iqser.red.service.peristence.v1.server.integration.client.*; +import com.iqser.red.service.peristence.v1.server.integration.client.DictionaryClient; +import com.iqser.red.service.peristence.v1.server.integration.client.DossierClient; +import com.iqser.red.service.peristence.v1.server.integration.client.DossierStatusClient; +import com.iqser.red.service.peristence.v1.server.integration.client.DossierTemplateClient; +import com.iqser.red.service.peristence.v1.server.integration.client.DossierTemplateStatsClient; +import com.iqser.red.service.peristence.v1.server.integration.client.FileClient; +import com.iqser.red.service.peristence.v1.server.integration.client.UploadClient; import com.iqser.red.service.peristence.v1.server.integration.service.DossierTemplateTesterAndProvider; import com.iqser.red.service.peristence.v1.server.integration.service.DossierTesterAndProvider; import com.iqser.red.service.peristence.v1.server.integration.service.FileTesterAndProvider; @@ -16,18 +35,6 @@ import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.ty import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionaryEntryType; import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionarySummary; import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.Type; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import java.time.OffsetDateTime; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.assertThat; public class DossierTemplateStatsTest extends AbstractPersistenceServerServiceTest { private static final String TYPE_ID_1 = "type1"; @@ -239,6 +246,8 @@ public class DossierTemplateStatsTest extends AbstractPersistenceServerServiceTe @Test public void testDossierTemplateStats() { + + for (int i = 0; i < 2; i++) { var template = dossierTemplateTesterAndProvider.provideTestTemplate("test template: " + i); diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/TypeTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/TypeTest.java index e2be9489e..3d28f5524 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/TypeTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/TypeTest.java @@ -1,5 +1,12 @@ package com.iqser.red.service.peristence.v1.server.integration.tests; +import static org.assertj.core.api.Assertions.assertThat; + +import org.assertj.core.util.Lists; +import org.junit.Test; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; + import com.iqser.red.service.peristence.v1.server.integration.client.DictionaryClient; import com.iqser.red.service.peristence.v1.server.integration.service.DossierTemplateTesterAndProvider; import com.iqser.red.service.peristence.v1.server.integration.service.DossierTesterAndProvider; @@ -8,12 +15,6 @@ import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPers import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.Colors; import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionaryEntryType; import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.Type; -import org.assertj.core.util.Lists; -import org.junit.Test; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.annotation.Autowired; - -import static org.assertj.core.api.Assertions.assertThat; public class TypeTest extends AbstractPersistenceServerServiceTest { diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java index a6b1fc58b..de71a7555 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java @@ -1,23 +1,13 @@ package com.iqser.red.service.peristence.v1.server.integration.utils; -import com.iqser.red.service.pdftron.redaction.v1.api.model.ByteContentDocument; -import com.iqser.red.service.pdftron.redaction.v1.api.model.DocumentRequest; -import com.iqser.red.service.pdftron.redaction.v1.api.model.UntouchedDocumentResponse; -import com.iqser.red.service.peristence.v1.server.Application; -import com.iqser.red.service.peristence.v1.server.client.RedactionClient; -import com.iqser.red.service.peristence.v1.server.client.SearchClient; -import com.iqser.red.service.peristence.v1.server.integration.client.ApplicationConfigClient; -import com.iqser.red.service.peristence.v1.server.integration.client.FileClient; -import com.iqser.red.service.peristence.v1.server.utils.MetricsPrinterService; -import com.iqser.red.service.peristence.v1.server.utils.StorageIdUtils; -import com.iqser.red.service.persistence.management.v1.processor.client.PDFTronRedactionClient; -import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.*; -import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.ApplicationConfig; -import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileType; -import com.iqser.red.service.redaction.v1.model.RedactionLog; -import com.iqser.red.service.redaction.v1.model.RedactionResult; -import com.iqser.red.storage.commons.StorageAutoConfiguration; -import com.iqser.red.storage.commons.service.StorageService; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; + +import java.sql.Connection; +import java.sql.DriverManager; + +import javax.sql.DataSource; + import org.assertj.core.util.Lists; import org.junit.After; import org.junit.Before; @@ -34,20 +24,77 @@ import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.StatementCallback; +import org.springframework.jdbc.datasource.SingleConnectionDataSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.testcontainers.shaded.org.apache.commons.io.IOUtils; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; +import com.iqser.red.service.pdftron.redaction.v1.api.model.ByteContentDocument; +import com.iqser.red.service.pdftron.redaction.v1.api.model.DocumentRequest; +import com.iqser.red.service.peristence.v1.server.Application; +import com.iqser.red.service.peristence.v1.server.client.RedactionClient; +import com.iqser.red.service.peristence.v1.server.client.SearchClient; +import com.iqser.red.service.peristence.v1.server.integration.client.ApplicationConfigClient; +import com.iqser.red.service.peristence.v1.server.integration.client.FileClient; +import com.iqser.red.service.peristence.v1.server.integration.client.TenantsClient; +import com.iqser.red.service.peristence.v1.server.utils.MetricsPrinterService; +import com.iqser.red.service.peristence.v1.server.utils.StorageIdUtils; +import com.iqser.red.service.persistence.management.v1.processor.client.PDFTronRedactionClient; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository.TenantRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ApplicationConfigRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.AuditRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DigitalSignatureRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierAttributeConfigRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierAttributeRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierStatusRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierTemplateRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DownloadStatusRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.EntryRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FalsePositiveEntryRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FalseRecommendationEntryRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileAttributeConfigRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileAttributesGeneralConfigurationRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileAttributesRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ForceRedactionRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ImageRecategorizationRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.IndexInformationRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.LegalBasisChangeRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.LegalBasisMappingRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ManualRedactionRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.NotificationPreferencesRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.NotificationRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.RemoveRedactionRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ReportTemplateRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.RuleSetRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.SMTPRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.TypeRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ViewedPagesRepository; +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.dossiertemplate.dossier.file.FileType; +import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.Tenant; +import com.iqser.red.service.redaction.v1.model.RedactionLog; +import com.iqser.red.service.redaction.v1.model.RedactionResult; +import com.iqser.red.storage.commons.StorageAutoConfiguration; +import com.iqser.red.storage.commons.service.StorageService; + +import lombok.SneakyThrows; @RunWith(SpringRunner.class) @EnableFeignClients(basePackageClasses = FileClient.class) @Import(AbstractPersistenceServerServiceTest.TestConfiguration.class) @ContextConfiguration(initializers = {AbstractPersistenceServerServiceTest.Initializer.class}) -@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, - properties = "spring-hibernate-query-utils.n-plus-one-queries-detection.error-level=INFO") +@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, properties = "spring-hibernate-query-utils.n-plus-one-queries-detection.error-level=INFO") public abstract class AbstractPersistenceServerServiceTest { @MockBean @@ -132,11 +179,26 @@ public abstract class AbstractPersistenceServerServiceTest { @Autowired protected ApplicationConfigRepository applicationConfigRepository; + @Autowired + protected TenantsClient tenantsClient; + + @Autowired + protected TenantRepository tenantRepository; + + @Autowired + private JdbcTemplate jdbcTemplate; + @Before public void setupOptimize() { + + createDefaultTenant(); + ApplicationConfig appConfig = new ApplicationConfig().builder() - .downloadCleanupDownloadFilesHours(8).downloadCleanupNotDownloadFilesHours(72).softDeleteCleanupTime(96).build(); + .downloadCleanupDownloadFilesHours(8) + .downloadCleanupNotDownloadFilesHours(72) + .softDeleteCleanupTime(96) + .build(); appConfigClient.createOrUpdateAppConfig(appConfig); // when(appConfigClient.getCurrentApplicationConfig()).thenReturn(ApplicationConfig.builder() @@ -153,94 +215,144 @@ public abstract class AbstractPersistenceServerServiceTest { DocumentRequest d = (DocumentRequest) args[0]; var untouchedObjectId = StorageIdUtils.getStorageId(d.getDossierId(), d.getFileId(), FileType.ORIGIN); - return new ByteContentDocument(IOUtils.toByteArray(storageService.getObject(untouchedObjectId).getInputStream())); + return new ByteContentDocument(IOUtils.toByteArray(storageService.getObject(untouchedObjectId) + .getInputStream())); }); when(pdfTronRedactionClient.redactionPreview(Mockito.any())).thenAnswer((answer) -> { Object[] args = answer.getArguments(); DocumentRequest d = (DocumentRequest) args[0]; var untouchedObjectId = StorageIdUtils.getStorageId(d.getDossierId(), d.getFileId(), FileType.ORIGIN); - return new ByteContentDocument(IOUtils.toByteArray(storageService.getObject(untouchedObjectId).getInputStream())); + return new ByteContentDocument(IOUtils.toByteArray(storageService.getObject(untouchedObjectId) + .getInputStream())); }); when(pdfTronRedactionClient.redactionPreviewDiff(Mockito.any())).thenAnswer((answer) -> { Object[] args = answer.getArguments(); DocumentRequest d = (DocumentRequest) args[0]; var untouchedObjectId = StorageIdUtils.getStorageId(d.getDossierId(), d.getFileId(), FileType.ORIGIN); - return new ByteContentDocument(IOUtils.toByteArray(storageService.getObject(untouchedObjectId).getInputStream())); + return new ByteContentDocument(IOUtils.toByteArray(storageService.getObject(untouchedObjectId) + .getInputStream())); }); - when(redactionClient.sections(Mockito.any())).thenReturn(new RedactionResult()); - when(redactionClient.getRedactionLog(Mockito.any())).thenReturn(new RedactionLog(1, 1, - Lists.newArrayList(), null, 0, 0, 0, 0)); + when(redactionClient.getRedactionLog(Mockito.any())).thenReturn(new RedactionLog(1, 1, Lists.newArrayList(), null, 0, 0, 0, 0)); } + + private void createDefaultTenant() { + + if(!tenantRepository.findById("redaction").isPresent()) { + 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"; + + createDatabase("redaction", "redaction"); + createSchema(jdbcUrl, "redaction", "redaction"); + + tenantsClient.createTenant(new Tenant("redaction", jdbcUrl, "redaction", "redaction")); + } + } + + + private void createDatabase(String db, String password) { + + jdbcTemplate.execute((StatementCallback) stmt -> stmt.execute("CREATE DATABASE " + db)); + jdbcTemplate.execute((StatementCallback) stmt -> stmt.execute("CREATE USER " + db + " WITH ENCRYPTED PASSWORD '" + password + "'")); + jdbcTemplate.execute((StatementCallback) stmt -> stmt.execute("GRANT ALL PRIVILEGES ON DATABASE " + db + " TO " + db)); + } + + + @SneakyThrows + public void createSchema(String jdbcUrl, String username, String password) { + + try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) { + DataSource tenantDataSource = new SingleConnectionDataSource(connection, false); + JdbcTemplate insert = new JdbcTemplate(tenantDataSource); + insert.execute((StatementCallback) stmt -> stmt.execute("CREATE SCHEMA myschema")); + insert.execute((StatementCallback) stmt -> stmt.execute("GRANT USAGE ON SCHEMA myschema TO " + username)); + } + } + + @After public void cleanupStorage() { + ((FileSystemBackedStorageService) this.storageService).clearStorage(); } @After public void printMetrics() { + this.metricsPrinterService.printMetrics(); } + @After public void afterTests() { - falsePositiveEntryRepository.deleteAll(); - falseRecommendationEntryRepository.deleteAll(); - entryRepository.deleteAll(); - dossierAttributeRepository.deleteAll(); - dossierAttributeConfigRepository.deleteAll(); - downloadStatusRepository.deleteAll(); - fileAttributesRepository.deleteAll(); - smtpRepository.deleteAll(); - digitalSignatureRepository.deleteAll(); - fileAttributesGeneralConfigurationRepository.deleteAll(); - fileAttributeConfigRepository.deleteAll(); - reportTemplateRepository.deleteAll(); - typeRepository.deleteAll(); - viewedPagesRepository.deleteAll(); - notificationRepository.deleteAll(); - auditRepository.deleteAll(); - manualRedactionRepository.deleteAll(); - forceRedactionRepository.deleteAll(); - removeRedactionRepository.deleteAll(); - legalBasisChangeRepository.deleteAll(); - imageRecategorizationRepository.deleteAll(); - legalBasisMappingRepository.deleteAll(); - ruleSetRepository.deleteAll(); - smtpRepository.deleteAll(); - fileRepository.deleteAll(); - dossierRepository.deleteAll(); - dossierStatusRepository.deleteAll(); - watermarkRepository.deleteAll(); - dossierTemplateRepository.deleteAll(); - notificationPreferencesRepository.deleteAll(); - indexInformationRepository.deleteAll(); - applicationConfigRepository.deleteAll(); + + tenantRepository.findAll().forEach(tenant -> { + + TenantContext.setTenantId(tenant.getTenantId()); + + falsePositiveEntryRepository.deleteAll(); + falseRecommendationEntryRepository.deleteAll(); + entryRepository.deleteAll(); + dossierAttributeRepository.deleteAll(); + dossierAttributeConfigRepository.deleteAll(); + downloadStatusRepository.deleteAll(); + fileAttributesRepository.deleteAll(); + smtpRepository.deleteAll(); + digitalSignatureRepository.deleteAll(); + fileAttributesGeneralConfigurationRepository.deleteAll(); + fileAttributeConfigRepository.deleteAll(); + reportTemplateRepository.deleteAll(); + typeRepository.deleteAll(); + viewedPagesRepository.deleteAll(); + notificationRepository.deleteAll(); + auditRepository.deleteAll(); + manualRedactionRepository.deleteAll(); + forceRedactionRepository.deleteAll(); + removeRedactionRepository.deleteAll(); + legalBasisChangeRepository.deleteAll(); + imageRecategorizationRepository.deleteAll(); + legalBasisMappingRepository.deleteAll(); + ruleSetRepository.deleteAll(); + smtpRepository.deleteAll(); + fileRepository.deleteAll(); + dossierRepository.deleteAll(); + dossierStatusRepository.deleteAll(); + watermarkRepository.deleteAll(); + dossierTemplateRepository.deleteAll(); + notificationPreferencesRepository.deleteAll(); + indexInformationRepository.deleteAll(); + applicationConfigRepository.deleteAll(); + + }); } + static class Initializer implements ApplicationContextInitializer { + public void initialize(ConfigurableApplicationContext configurableApplicationContext) { - var postgreSQLContainer = SpringPostgreSQLTestContainer.getInstance() - .withDatabaseName("integration-tests-db") + var postgreSQLContainerMaster = SpringPostgreSQLTestContainer.getInstance() + .withDatabaseName("integration-tests-db-master") .withUsername("sa") .withPassword("sa"); - postgreSQLContainer.start(); + postgreSQLContainerMaster.start(); var connectionStringDetails = "?serverTimezone=UTC&cachePrepStmts=true&useServerPrepStmts=true&rewriteBatchedStatements=true"; - TestPropertyValues.of( - "spring.datasource.url=" + postgreSQLContainer.getJdbcUrl() + connectionStringDetails, - "spring.datasource.username=" + postgreSQLContainer.getUsername(), - "spring.datasource.password=" + postgreSQLContainer.getPassword() - ).applyTo(configurableApplicationContext.getEnvironment()); + TestPropertyValues.of("multitenancy.master.datasource.url=" + postgreSQLContainerMaster.getJdbcUrl() + connectionStringDetails, "multitenancy.master.datasource.username=" + postgreSQLContainerMaster.getUsername(), "multitenancy.master.datasource.password=" + postgreSQLContainerMaster.getPassword()) + .applyTo(configurableApplicationContext.getEnvironment()); } + } @Configuration @@ -251,10 +363,10 @@ public abstract class AbstractPersistenceServerServiceTest { @Bean @Primary public StorageService inmemoryStorage() { + return new FileSystemBackedStorageService(); } - } } diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/resources/application.yml b/persistence-service-v1/persistence-service-server-v1/src/test/resources/application.yml index 00c905c5a..8ec4cff06 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/resources/application.yml +++ b/persistence-service-v1/persistence-service-server-v1/src/test/resources/application.yml @@ -3,14 +3,6 @@ monitoring.enabled: true spring: main: allow-circular-references: true # FIXME - datasource: - driverClassName: org.postgresql.Driver - platform: org.hibernate.dialect.PostgreSQL95Dialect - hikari: - data-source-properties: - cachePrepStmts: true - prepStmtCacheSize: 1000 - prepStmtCacheSqlLimit: 2048 jpa: database-platform: org.hibernate.dialect.PostgreSQL95Dialect hibernate: @@ -79,3 +71,30 @@ management: endpoints.web.exposure.include: prometheus, health, metrics logging.level.root: info + +multitenancy: + datasource-cache: + maximumSize: 100 + expireAfterAccess: 1 + master: + datasource: + driverClassName: org.postgresql.Driver + platform: org.hibernate.dialect.PostgreSQL95Dialect + hikari: + data-source-properties: + cachePrepStmts: true + prepStmtCacheSize: 1000 + prepStmtCacheSqlLimit: 2048 + liquibase: + changeLog: classpath:db/changelog/db.changelog-master.yaml + tenant: + datasource: + driverClassName: org.postgresql.Driver + platform: org.hibernate.dialect.PostgreSQL95Dialect + hikari: + data-source-properties: + cachePrepStmts: true + prepStmtCacheSize: 1000 + prepStmtCacheSqlLimit: 2048 + liquibase: + changeLog: classpath:db/changelog/db.changelog-tenant.yaml