diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dao/jpa/DatabaseRegistryDbaasRepositoryImpl.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dao/jpa/DatabaseRegistryDbaasRepositoryImpl.java index 5cc6b2e8..60a6777d 100644 --- a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dao/jpa/DatabaseRegistryDbaasRepositoryImpl.java +++ b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dao/jpa/DatabaseRegistryDbaasRepositoryImpl.java @@ -1,5 +1,6 @@ package com.netcracker.cloud.dbaas.dao.jpa; +import com.netcracker.cloud.dbaas.dto.backupV2.Filter; import com.netcracker.cloud.dbaas.entity.pg.Database; import com.netcracker.cloud.dbaas.entity.pg.DatabaseRegistry; import com.netcracker.cloud.dbaas.repositories.dbaas.DatabaseRegistryDbaasRepository; @@ -12,23 +13,18 @@ import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; import lombok.extern.slf4j.Slf4j; - import net.jodah.failsafe.Failsafe; import net.jodah.failsafe.RetryPolicy; import org.apache.commons.lang.StringUtils; +import javax.annotation.Nullable; import java.time.Duration; import java.util.*; import java.util.concurrent.Callable; import java.util.function.Function; import java.util.stream.Collectors; -import javax.annotation.Nullable; - -import static com.netcracker.cloud.dbaas.Constants.MICROSERVICE_NAME; -import static com.netcracker.cloud.dbaas.Constants.ROLE; -import static com.netcracker.cloud.dbaas.Constants.SCOPE; -import static com.netcracker.cloud.dbaas.Constants.SCOPE_VALUE_TENANT; +import static com.netcracker.cloud.dbaas.Constants.*; import static com.netcracker.cloud.dbaas.config.ServicesConfig.DBAAS_REPOSITORIES_MUTEX; import static jakarta.transaction.Transactional.TxType.REQUIRES_NEW; @@ -141,6 +137,11 @@ public List findAllTransactionalDatabaseRegistries(String name return databaseRegistryRepository.findAllByNamespaceAndDatabase_BgVersionNull(namespace); } + @Override + public List findAllDatabasesByFilter(List filters) { + return databaseRegistryRepository.findAllDatabasesByFilter(filters); + } + @Override public void delete(DatabaseRegistry databaseRegistry) { log.debug("Delete logical database with classifier {} and type {}", databaseRegistry.getClassifier(), databaseRegistry.getType()); diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/BackupRequest.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/BackupRequest.java index d825fb4b..ec6ec712 100644 --- a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/BackupRequest.java +++ b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/BackupRequest.java @@ -1,7 +1,7 @@ package com.netcracker.cloud.dbaas.dto.backupV2; import com.netcracker.cloud.dbaas.enums.ExternalDatabaseStrategy; -import com.netcracker.cloud.dbaas.utils.validation.BackupGroup; +import com.netcracker.cloud.dbaas.utils.validation.group.BackupGroup; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/BackupResponse.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/BackupResponse.java index b555dfbf..448a35cd 100644 --- a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/BackupResponse.java +++ b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/BackupResponse.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.netcracker.cloud.dbaas.enums.BackupStatus; import com.netcracker.cloud.dbaas.enums.ExternalDatabaseStrategy; -import com.netcracker.cloud.dbaas.utils.validation.BackupGroup; +import com.netcracker.cloud.dbaas.utils.validation.group.BackupGroup; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/Filter.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/Filter.java index 51eaf204..91811f18 100644 --- a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/Filter.java +++ b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/Filter.java @@ -1,5 +1,8 @@ package com.netcracker.cloud.dbaas.dto.backupV2; +import com.netcracker.cloud.dbaas.utils.validation.NotEmptyFilter; +import com.netcracker.cloud.dbaas.utils.validation.group.BackupGroup; +import com.netcracker.cloud.dbaas.utils.validation.group.RestoreGroup; import lombok.Data; import lombok.NoArgsConstructor; import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; @@ -9,6 +12,7 @@ import java.util.List; @Data +@NotEmptyFilter(groups = {BackupGroup.class, RestoreGroup.class}) @NoArgsConstructor @Schema(description = "Single filter criteria for backup and restore operations") public class Filter { diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/FilterCriteria.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/FilterCriteria.java index e963d383..70144f08 100644 --- a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/FilterCriteria.java +++ b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/FilterCriteria.java @@ -1,12 +1,14 @@ package com.netcracker.cloud.dbaas.dto.backupV2; -import com.netcracker.cloud.dbaas.utils.validation.BackupGroup; +import com.netcracker.cloud.dbaas.utils.validation.group.BackupGroup; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Data; import lombok.NoArgsConstructor; import org.eclipse.microprofile.openapi.annotations.media.Schema; +import java.util.ArrayList; import java.util.List; @Data @@ -23,13 +25,11 @@ public class FilterCriteria { ) @NotNull(groups = {BackupGroup.class}) @Size(min = 1, groups = {BackupGroup.class}) - private List filter; - @Schema( - description = "Include databases that match any of the filters in the list" - ) - private List include; + @Valid + private List filter = new ArrayList<>(); + @Schema( description = "Exclude databases that match any of the filters in the list" ) - private List exclude; + private List exclude = new ArrayList<>(); } diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/RestoreRequest.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/RestoreRequest.java index d44f4408..10863196 100644 --- a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/RestoreRequest.java +++ b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/RestoreRequest.java @@ -1,7 +1,7 @@ package com.netcracker.cloud.dbaas.dto.backupV2; import com.netcracker.cloud.dbaas.enums.ExternalDatabaseStrategy; -import com.netcracker.cloud.dbaas.utils.validation.RestoreGroup; +import com.netcracker.cloud.dbaas.utils.validation.group.RestoreGroup; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.groups.ConvertGroup; diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/RestoreResponse.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/RestoreResponse.java index 2c656959..333b7e17 100644 --- a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/RestoreResponse.java +++ b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/dto/backupV2/RestoreResponse.java @@ -2,7 +2,7 @@ import com.netcracker.cloud.dbaas.enums.ExternalDatabaseStrategy; import com.netcracker.cloud.dbaas.enums.RestoreStatus; -import com.netcracker.cloud.dbaas.utils.validation.RestoreGroup; +import com.netcracker.cloud.dbaas.utils.validation.group.RestoreGroup; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.groups.ConvertGroup; diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/repositories/dbaas/DatabaseRegistryDbaasRepository.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/repositories/dbaas/DatabaseRegistryDbaasRepository.java index 1978fc78..190c6f5c 100644 --- a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/repositories/dbaas/DatabaseRegistryDbaasRepository.java +++ b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/repositories/dbaas/DatabaseRegistryDbaasRepository.java @@ -1,15 +1,15 @@ package com.netcracker.cloud.dbaas.repositories.dbaas; +import com.netcracker.cloud.dbaas.dto.backupV2.Filter; import com.netcracker.cloud.dbaas.entity.pg.Database; import com.netcracker.cloud.dbaas.entity.pg.DatabaseRegistry; +import javax.annotation.Nullable; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; -import javax.annotation.Nullable; - public interface DatabaseRegistryDbaasRepository { List findAnyLogDbRegistryTypeByNamespace(String namespace); @@ -63,4 +63,5 @@ public interface DatabaseRegistryDbaasRepository { List findAllTransactionalDatabaseRegistries(String namespace); + List findAllDatabasesByFilter(List filters); } diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/repositories/pg/jpa/DatabaseRegistryRepository.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/repositories/pg/jpa/DatabaseRegistryRepository.java index e9465383..264eacd5 100644 --- a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/repositories/pg/jpa/DatabaseRegistryRepository.java +++ b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/repositories/pg/jpa/DatabaseRegistryRepository.java @@ -1,15 +1,20 @@ package com.netcracker.cloud.dbaas.repositories.pg.jpa; +import com.netcracker.cloud.dbaas.dto.backupV2.DatabaseKind; +import com.netcracker.cloud.dbaas.dto.backupV2.DatabaseType; +import com.netcracker.cloud.dbaas.dto.backupV2.Filter; import com.netcracker.cloud.dbaas.entity.pg.DatabaseRegistry; import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase; import jakarta.enterprise.context.ApplicationScoped; import jakarta.transaction.Transactional; +import lombok.extern.slf4j.Slf4j; -import java.util.List; -import java.util.Optional; -import java.util.SortedMap; -import java.util.UUID; +import java.util.*; +import static com.netcracker.cloud.dbaas.Constants.MICROSERVICE_NAME; +import static com.netcracker.cloud.dbaas.Constants.NAMESPACE; + +@Slf4j @ApplicationScoped @Transactional public class DatabaseRegistryRepository implements PanacheRepositoryBase { @@ -33,4 +38,60 @@ public List findAllByNamespaceAndDatabase_BgVersionNull(String public List findAllByNamespaceAndDatabase_BgVersionNotNull(String namespace) { return list("namespace = ?1 and database.bgVersion is not null", namespace); } + + public List findAllDatabasesByFilter(List filters) { + StringBuilder q = new StringBuilder( + "SELECT cl.* " + + "FROM classifier cl " + ); + + int index = 0; + boolean needDatabaseJoin = false; + List orBlock = new ArrayList<>(); + Map params = new HashMap<>(); + + for (Filter filter : filters) { + List query = new ArrayList<>(); + if (filter.getNamespace() != null && !filter.getNamespace().isEmpty()) { + String nsValues = "nsValues" + index; + query.add("cl.classifier #>> '{" + NAMESPACE + "}' = ANY(:" + nsValues + ")"); + params.put(nsValues, filter.getNamespace().toArray(new String[0])); + } + if (filter.getMicroserviceName() != null && !filter.getMicroserviceName().isEmpty()) { + String msValues = "msValues" + index; + query.add("cl.classifier #>> '{" + MICROSERVICE_NAME + "}' = ANY(:" + msValues + ")"); + params.put(msValues, filter.getMicroserviceName().toArray(new String[0])); + } + if (filter.getDatabaseType() != null && !filter.getDatabaseType().isEmpty()) { + String typeValues = "typeValues" + index; + query.add("cl.type = ANY(:" + typeValues + ")"); + params.put(typeValues, filter.getDatabaseType().stream().map(DatabaseType::getType).toList().toArray(new String[0])); + } + if (filter.getDatabaseKind() != null && filter.getDatabaseKind().size() == 1) { + needDatabaseJoin = true; + DatabaseKind kind = filter.getDatabaseKind().getFirst(); + if (kind == DatabaseKind.CONFIGURATION) { + query.add("d.bgversion IS NOT NULL AND d.bgversion <> '' "); + } else if (kind == DatabaseKind.TRANSACTIONAL) { + query.add("(d.bgversion IS NULL OR d.bgversion = '') "); + } + } + if (!query.isEmpty()) { + String block = "(" + String.join(" AND ", query) + ")"; + orBlock.add(block); + index++; + } + } + + if (needDatabaseJoin) + q.append("LEFT JOIN database d ON cl.database_id = d.id "); + + if (!orBlock.isEmpty()) + q.append("WHERE ").append(String.join(" OR ", orBlock)); + + var query = getEntityManager() + .createNativeQuery(q.toString(), DatabaseRegistry.class); + params.forEach(query::setParameter); + return query.getResultList(); + } } diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/service/DbBackupV2Service.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/service/DbBackupV2Service.java index da065cb5..4cb6d4df 100644 --- a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/service/DbBackupV2Service.java +++ b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/service/DbBackupV2Service.java @@ -427,38 +427,39 @@ public BackupStatusResponse getCurrentStatus(String backupName) { } protected Map> getAllDbByFilter(FilterCriteria filterCriteria) { - Filter filter = filterCriteria.getFilter().getFirst(); - - if (filter.getNamespace().isEmpty()) { - if (!filter.getMicroserviceName().isEmpty()) { - throw new FunctionalityNotImplemented("backup by microservice"); - } - if (!filter.getDatabaseKind().isEmpty()) { - throw new FunctionalityNotImplemented("backup by databaseKind"); - } - if (!filter.getDatabaseType().isEmpty()) { - throw new FunctionalityNotImplemented("backup by databaseType"); - } - throw new RequestValidationException(ErrorCodes.CORE_DBAAS_4043, "namespace", Source.builder().build()); + int uniqKinds = (int) filterCriteria.getExclude().stream() + .flatMap(exclude -> exclude.getDatabaseKind().stream()) + .distinct() + .count(); + if (uniqKinds == DatabaseKind.values().length) { + log.warn("No databases matching the filtering criteria were found during the backup"); + throw new DbNotFoundException("No databases matching the filtering criteria were found during the backup", Source.builder().build()); } - if (filter.getNamespace().size() > 1) { - throw new FunctionalityNotImplemented("backup by several namespace"); - } - - String namespace = filter.getNamespace().getFirst(); - - List databasesRegistriesForBackup = databaseRegistryDbaasRepository - .findAnyLogDbRegistryTypeByNamespace(namespace) + List filteredDatabases = databaseRegistryDbaasRepository + .findAllDatabasesByFilter(filterCriteria.getFilter()) .stream() - .filter(this::isValidRegistry) + .filter(registry -> { + if (!isValidRegistry(registry)) + return false; + return filterCriteria.getExclude().stream() + .filter(exclude -> !isEmpty(exclude)) + .noneMatch(exclude -> { + boolean configurational = registry.getBgVersion() != null && !registry.getBgVersion().isBlank(); + return isMatches(exclude, + (String) registry.getClassifier().get(NAMESPACE), + (String) registry.getClassifier().get(MICROSERVICE_NAME), + registry.getType(), + configurational); + }); + }) .toList(); - if (databasesRegistriesForBackup.isEmpty()) { - log.warn("During backup databases that match filterCriteria not found"); - throw new DbNotFoundException("Databases that match filterCriteria not found", Source.builder().build()); + if (filteredDatabases.isEmpty()) { + log.warn("No databases matching the filtering criteria were found during the backup"); + throw new DbNotFoundException("No databases matching the filtering criteria were found during the backup", Source.builder().build()); } - return databasesRegistriesForBackup.stream() + return filteredDatabases.stream() .collect(Collectors.groupingBy(DatabaseRegistry::getDatabase)); } @@ -468,6 +469,43 @@ private boolean isValidRegistry(DatabaseRegistry registry) { && CREATED.equals(registry.getDbState().getDatabaseState()); } + private boolean isMatches(Filter filter, String namespace, String microserviceName, String type, boolean configurational) { + if (!filter.getNamespace().isEmpty() && + !filter.getNamespace().contains(namespace)) { + return false; + } + + if (!filter.getMicroserviceName().isEmpty() && + !filter.getMicroserviceName().contains(microserviceName)) { + return false; + } + + if (!filter.getDatabaseType().isEmpty() && + filter.getDatabaseType().stream().noneMatch(dt -> dt.getType().equals(type))) { + return false; + } + + if (!filter.getDatabaseKind().isEmpty()) { + return isKindMatched(configurational, filter.getDatabaseKind().getFirst()); + } + return true; + } + + private boolean isEmpty(Filter f) { + return f.getNamespace().isEmpty() + && f.getMicroserviceName().isEmpty() + && f.getDatabaseType().isEmpty() + && f.getDatabaseKind().isEmpty(); + } + + private boolean isKindMatched(boolean configurational, DatabaseKind kind) { + if (kind == DatabaseKind.CONFIGURATION) + return configurational; + if (kind == DatabaseKind.TRANSACTIONAL) + return !configurational; + return true; + } + public BackupResponse getBackup(String backupName) { return mapper.toBackupResponse(getBackupOrThrowException(backupName)); } @@ -683,44 +721,30 @@ protected List getAllDbByFilter(List bac return backupDatabasesToFilter.stream().map(db -> new BackupDatabaseDelegate(db, db.getClassifiers())) .toList(); - Filter filter = filterCriteria.getFilter().getFirst(); - - if (filter.getNamespace().isEmpty()) { - if (!filter.getMicroserviceName().isEmpty()) { - throw new FunctionalityNotImplemented("restoration by microservice"); - } - if (!filter.getDatabaseKind().isEmpty()) { - throw new FunctionalityNotImplemented("restoration by databaseKind"); - } - if (!filter.getDatabaseType().isEmpty()) { - throw new FunctionalityNotImplemented("restoration by databaseType"); - } - throw new RequestValidationException(ErrorCodes.CORE_DBAAS_4043, "namespace", Source.builder().build()); - } - if (filter.getNamespace().size() > 1) { - throw new FunctionalityNotImplemented("restoration by several namespace"); - } - String namespace = filter.getNamespace().getFirst(); - // Filter BackupDatabase by namespace List databaseDelegateList = backupDatabasesToFilter.stream() - .map(backupDatabase -> { - List> filteredClassifiers = backupDatabase.getClassifiers().stream() - .filter(classifier -> namespace.equals(classifier.get(NAMESPACE))) - .map(classifier -> (SortedMap) new TreeMap<>(classifier)) - .toList(); + .map(db -> { + List> filteredClassifiers = db.getClassifiers().stream() + .filter(classifier -> { + String namespace = (String) classifier.get(NAMESPACE); + String microserviceName = (String) classifier.get(MICROSERVICE_NAME); + String type = db.getLogicalBackup().getType(); + boolean configurational = db.isConfigurational(); + return filterCriteria.getFilter().stream().anyMatch(filter -> isMatches(filter, namespace, microserviceName, type, configurational)) + && filterCriteria.getExclude().stream().filter(exclude -> !isEmpty(exclude)).noneMatch(ex -> isMatches(ex, namespace, microserviceName, type, configurational)); + }) + .map(c -> (SortedMap) new TreeMap<>(c)) + .toList(); - if (filteredClassifiers.isEmpty()) - return null; + if (filteredClassifiers.isEmpty()) { + return null; + } - return new BackupDatabaseDelegate( - backupDatabase, - filteredClassifiers - ); - } - ) + return new BackupDatabaseDelegate(db, filteredClassifiers); + }) .filter(Objects::nonNull) .toList(); + if (databaseDelegateList.isEmpty()) { log.warn("During restore databases that match filterCriteria not found"); throw new DbNotFoundException("Databases that match filterCriteria not found", Source.builder().build()); @@ -799,9 +823,9 @@ protected Restore initializeFullRestoreStructure( return restore; } - private List validateAndFilterExternalDb(List externalDatabases, - ExternalDatabaseStrategy strategy, - FilterCriteria filterCriteria) { + protected List validateAndFilterExternalDb(List externalDatabases, + ExternalDatabaseStrategy strategy, + FilterCriteria filterCriteria) { if (externalDatabases == null || externalDatabases.isEmpty()) return List.of(); @@ -827,29 +851,28 @@ private List validateAndFilterExternalDb(List { + List> filteredClassifiers = db.getClassifiers().stream() + .filter(classifier -> { + String namespace = (String) classifier.get(NAMESPACE); + String microserviceName = (String) classifier.get(MICROSERVICE_NAME); + String type = db.getType(); + return filterCriteria.getFilter().stream().anyMatch(filter -> isMatches(filter, namespace, microserviceName, type, false)) + && + filterCriteria.getExclude().stream().filter(exclude -> !isEmpty(exclude)).noneMatch(ex -> isMatches(ex, namespace, microserviceName, type, false)); + }) + .map(c -> (SortedMap) new TreeMap<>(c)) + .toList(); - if (filter.getNamespace().isEmpty()) { - if (!filter.getMicroserviceName().isEmpty()) { - throw new FunctionalityNotImplemented("restoration by microservice"); - } - if (!filter.getDatabaseKind().isEmpty()) { - throw new FunctionalityNotImplemented("restoration by databaseKind"); - } - if (!filter.getDatabaseType().isEmpty()) { - throw new FunctionalityNotImplemented("restoration by databaseType"); - } - throw new RequestValidationException(ErrorCodes.CORE_DBAAS_4043, "namespace", Source.builder().build()); - } - if (filter.getNamespace().size() > 1) { - throw new FunctionalityNotImplemented("restoration by several namespace"); - } - String namespace = filter.getNamespace().getFirst(); - yield mapper.toRestoreExternalDatabases(externalDatabases).stream() - .filter(db -> db.getClassifiers().stream() - .anyMatch(classifier -> - namespace.equals(classifier.get(NAMESPACE))) - ).toList(); + if (filteredClassifiers.isEmpty()) { + return null; + } + + return mapper.toRestoreExternalDatabase(db); + }) + .filter(Objects::nonNull) + .toList(); } }; } @@ -1068,15 +1091,8 @@ private LogicalRestoreAdapterResponse logicalRestore(LogicalRestore logicalResto Restore restore = logicalRestore.getRestore(); RetryPolicy retryPolicy = buildRetryPolicy(logicalRestore.getLogicalRestoreName(), RESTORE_OPERATION); - try { - return Failsafe.with(retryPolicy) - .get(() -> executeRestore(logicalRestore, logicalBackupName, restore, databases, dryRun)); - } catch (Exception e) { - log.error("Logical restore startup for adapterId={} failed, restore={}", logicalRestore.getAdapterId(), restore.getName()); - throw new BackupExecutionException( - String.format("Logical restore startup for adapterId=%s failed, restore=%s", - logicalRestore.getAdapterId(), restore.getName()), e); - } + return Failsafe.with(retryPolicy) + .get(() -> executeRestore(logicalRestore, logicalBackupName, restore, databases, dryRun)); } private List> buildRestoreDatabases(LogicalRestore logicalRestore) { @@ -1557,11 +1573,7 @@ private String extractErrorMessage(Throwable throwable) { } private boolean isFilterEmpty(FilterCriteria filterCriteria) { - if (filterCriteria == null || filterCriteria.getFilter() == null) - return true; - - return filterCriteria.getFilter().isEmpty() - || filterCriteria.getFilter().stream().allMatch(this::isSingleFilterEmpty); + return filterCriteria == null || filterCriteria.getFilter() == null || filterCriteria.getFilter().isEmpty(); } private boolean isSingleFilterEmpty(Filter f) { diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/BackupGroup.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/BackupGroup.java deleted file mode 100644 index 82a744a8..00000000 --- a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/BackupGroup.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.netcracker.cloud.dbaas.utils.validation; - -public interface BackupGroup { -} diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/NotEmptyFilter.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/NotEmptyFilter.java new file mode 100644 index 00000000..8cf82b91 --- /dev/null +++ b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/NotEmptyFilter.java @@ -0,0 +1,20 @@ +package com.netcracker.cloud.dbaas.utils.validation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = NotEmptyFilterValidation.class) +public @interface NotEmptyFilter { + String message() default "Filter must have at least one non-null field"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/NotEmptyFilterValidation.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/NotEmptyFilterValidation.java new file mode 100644 index 00000000..d41c3a34 --- /dev/null +++ b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/NotEmptyFilterValidation.java @@ -0,0 +1,21 @@ +package com.netcracker.cloud.dbaas.utils.validation; + +import com.netcracker.cloud.dbaas.dto.backupV2.Filter; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +import java.util.List; + +public class NotEmptyFilterValidation implements ConstraintValidator { + @Override + public boolean isValid(Filter value, ConstraintValidatorContext context) { + return isValid(value.getNamespace()) || + isValid(value.getMicroserviceName()) || + isValid(value.getDatabaseType()) || + isValid(value.getDatabaseKind()); + } + + private boolean isValid(List list) { + return list != null && !list.isEmpty(); + } +} diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/RestoreGroup.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/RestoreGroup.java deleted file mode 100644 index 201d455e..00000000 --- a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/RestoreGroup.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.netcracker.cloud.dbaas.utils.validation; - -public interface RestoreGroup { -} diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/group/BackupGroup.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/group/BackupGroup.java new file mode 100644 index 00000000..b2c92397 --- /dev/null +++ b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/group/BackupGroup.java @@ -0,0 +1,4 @@ +package com.netcracker.cloud.dbaas.utils.validation.group; + +public interface BackupGroup { +} diff --git a/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/group/RestoreGroup.java b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/group/RestoreGroup.java new file mode 100644 index 00000000..9c507d16 --- /dev/null +++ b/dbaas/dbaas-aggregator/src/main/java/com/netcracker/cloud/dbaas/utils/validation/group/RestoreGroup.java @@ -0,0 +1,4 @@ +package com.netcracker.cloud.dbaas.utils.validation.group; + +public interface RestoreGroup { +} diff --git a/dbaas/dbaas-aggregator/src/test/java/com/netcracker/cloud/dbaas/controller/v3/DatabaseBackupV2ControllerTest.java b/dbaas/dbaas-aggregator/src/test/java/com/netcracker/cloud/dbaas/controller/v3/DatabaseBackupV2ControllerTest.java index 3332a210..5fb38ef9 100644 --- a/dbaas/dbaas-aggregator/src/test/java/com/netcracker/cloud/dbaas/controller/v3/DatabaseBackupV2ControllerTest.java +++ b/dbaas/dbaas-aggregator/src/test/java/com/netcracker/cloud/dbaas/controller/v3/DatabaseBackupV2ControllerTest.java @@ -86,7 +86,7 @@ void initiateBackup_invalidDto() { .statusCode(BAD_REQUEST.getStatusCode()) .body("message", allOf( containsString("backupName: must not be blank"), - containsString("filter: must not be null") + containsString("filter: size must be between 1 and 2147483647") )); verify(dbBackupV2Service, times(0)).backup(any(), anyBoolean()); @@ -139,6 +139,49 @@ void initiateBackup_backupAlreadyExists() { verify(dbBackupV2Service, times(1)).backup(backupRequest, false); } + @Test + void initiateBackup_emptyFilterCase(){ + String namespace = "namespace"; + String backupName = "backupName"; + + BackupRequest backupRequest = createBackupRequest(namespace, backupName); + FilterCriteria emptyFilterCriteria = backupRequest.getFilterCriteria(); + emptyFilterCriteria.setFilter(List.of(new Filter())); + emptyFilterCriteria.setExclude(List.of(new Filter())); + + given().auth().preemptive().basic("backup_manager", "backup_manager") + .contentType(ContentType.JSON) + .body(backupRequest) + .when().post("/backup") + .then() + .statusCode(BAD_REQUEST.getStatusCode()) + .body("reason", equalTo("Request does not contain required fields")) + .body("message", equalTo("filter[0]: Filter must have at least one non-null field")); + verify(dbBackupV2Service, times(0)).backup(backupRequest, false); + } + + @Test + void restoreBackup_emptyFilterCase(){ + String namespace = "namespace"; + String restoreName = "restoreName"; + String backupName = "backupName"; + + RestoreRequest restoreRequest = createRestoreRequest(namespace, restoreName); + FilterCriteria emptyFilterCriteria = restoreRequest.getFilterCriteria(); + emptyFilterCriteria.setFilter(List.of(new Filter())); + emptyFilterCriteria.setExclude(List.of(new Filter())); + + given().auth().preemptive().basic("backup_manager", "backup_manager") + .contentType(ContentType.JSON) + .body(restoreRequest) + .pathParam("backupName", backupName) + .when().post("/backup/{backupName}/restore") + .then() + .statusCode(BAD_REQUEST.getStatusCode()) + .body("reason", equalTo("Request does not contain required fields")) + .body("message", equalTo("filter[0]: Filter must have at least one non-null field")); + verify(dbBackupV2Service, times(0)).restore(backupName, restoreRequest, false); + } @Test void getBackupStatus_validBackupNameCase() { String backupName = "test-backup-name"; @@ -330,6 +373,22 @@ public static BackupRequest createBackupRequest(String namespace, String backupN return dto; } + public static RestoreRequest createRestoreRequest(String namespace, String restoreName) { + Filter filter = new Filter(); + filter.setNamespace(List.of(namespace)); + + FilterCriteria filterCriteria = new FilterCriteria(); + filterCriteria.setFilter(List.of(filter)); + + RestoreRequest dto = new RestoreRequest(); + dto.setRestoreName(restoreName); + dto.setFilterCriteria(filterCriteria); + dto.setExternalDatabaseStrategy(ExternalDatabaseStrategy.FAIL); + dto.setBlobPath("path"); + dto.setStorageName("storageName"); + return dto; + } + private BackupResponse createBackupResponse(String backupName) { String storageName = "storageName"; SortedMap sortedMap = new TreeMap<>(); diff --git a/dbaas/dbaas-aggregator/src/test/java/com/netcracker/cloud/dbaas/service/DbBackupV2ServiceTest.java b/dbaas/dbaas-aggregator/src/test/java/com/netcracker/cloud/dbaas/service/DbBackupV2ServiceTest.java index 70a4f6a5..c4f5910f 100644 --- a/dbaas/dbaas-aggregator/src/test/java/com/netcracker/cloud/dbaas/service/DbBackupV2ServiceTest.java +++ b/dbaas/dbaas-aggregator/src/test/java/com/netcracker/cloud/dbaas/service/DbBackupV2ServiceTest.java @@ -2,6 +2,7 @@ import com.netcracker.cloud.dbaas.dto.EnsuredUser; import com.netcracker.cloud.dbaas.dto.backupV2.*; +import com.netcracker.cloud.dbaas.entity.dto.backupV2.BackupDatabaseDelegate; import com.netcracker.cloud.dbaas.entity.dto.backupV2.LogicalBackupAdapterResponse; import com.netcracker.cloud.dbaas.entity.dto.backupV2.LogicalRestoreAdapterResponse; import com.netcracker.cloud.dbaas.entity.pg.*; @@ -1580,6 +1581,327 @@ void restore_similarRegistryInAnotherNamespace_shouldCreateNewDb() { } + @Test + void getAllDbByFilter_1() { + String namespace1 = "namespace1"; + String namespace2 = "namespace2"; + String namespace3 = "namespace3"; + String namespace4 = "namespace4"; + + String microserviceName1 = "microserviceName1"; + String microserviceName3 = "microserviceName3"; + String microserviceName4 = "microserviceName4"; + String microserviceName5 = "microserviceName5"; + String microserviceName6 = "microserviceName6"; + + String postgresqlType = "postgresql"; + String arangoDbType = "arangoDb"; + String cassandraType = "cassandra"; + + String adapterId = "adapterId"; + + String dbName1 = "db1"; + String dbName2 = "db2"; + String dbName3 = "db3"; + String dbName4 = "db4"; + String dbName5 = "db5"; + + Database db1 = getDatabase(adapterId, dbName1, false, false, ""); + Database db2 = getDatabase(adapterId, dbName2, false, false, "cfg"); + Database db3 = getDatabase(adapterId, dbName3, false, false, "cfg"); + Database db4 = getDatabase(adapterId, dbName4, false, false, ""); + Database db5 = getDatabase(adapterId, dbName5, false, false, ""); + + DatabaseRegistry registry1 = getDatabaseRegistry(db1, namespace1, microserviceName1, "", postgresqlType); + DatabaseRegistry registry2 = getDatabaseRegistry(db1, namespace2, microserviceName1, "", postgresqlType); + DatabaseRegistry registry3 = getDatabaseRegistry(db2, namespace2, microserviceName3, "", cassandraType); + DatabaseRegistry registry4 = getDatabaseRegistry(db3, namespace2, microserviceName4, "", cassandraType); + DatabaseRegistry registry5 = getDatabaseRegistry(db4, namespace3, microserviceName5, "", arangoDbType); + DatabaseRegistry registry6 = getDatabaseRegistry(db5, namespace4, microserviceName6, "", arangoDbType); + + Stream.of(registry1, registry2, registry3, registry4, registry5, registry6) + .forEach(databaseRegistryDbaasRepository::saveAnyTypeLogDb); + + Filter filter = new Filter(); + filter.setNamespace(List.of(namespace1, namespace2)); + filter.setMicroserviceName(List.of(microserviceName1, microserviceName4)); + filter.setDatabaseType(List.of(DatabaseType.POSTGRESQL, DatabaseType.CASSANDRA)); + filter.setDatabaseKind(List.of(DatabaseKind.TRANSACTIONAL)); + + Filter exclude = new Filter(); + exclude.setNamespace(List.of(namespace2)); + exclude.setMicroserviceName(List.of(microserviceName1)); + + FilterCriteria filterCriteria = new FilterCriteria(); + filterCriteria.setFilter(List.of(filter)); + filterCriteria.setExclude(List.of(exclude)); + + Map> dbToBackup = dbBackupV2Service.getAllDbByFilter(filterCriteria); + assertNotNull(dbToBackup); + assertEquals(1, dbToBackup.size()); + + dbToBackup.forEach((db, registries) -> { + assertEquals(db1.getId(), db.getId()); + assertEquals(1, registries.size()); + assertEquals(registry1, db.getDatabaseRegistry().getFirst()); + assertEquals(registry1, registries.getFirst()); + }); + } + + @Test + void getAllDbByFilter_2() { + String namespace1 = "namespace1"; + String namespace2 = "namespace2"; + String namespace3 = "namespace3"; + String namespace4 = "namespace4"; + + String microserviceName1 = "microserviceName1"; + String microserviceName3 = "microserviceName3"; + String microserviceName4 = "microserviceName4"; + String microserviceName5 = "microserviceName5"; + String microserviceName6 = "microserviceName6"; + + String postgresqlType = "postgresql"; + String arangoDbType = "arangodb"; + String cassandraType = "cassandra"; + String adapterId = "adapterId"; + + String dbName1 = "db1"; + String dbName2 = "db2"; + String dbName3 = "db3"; + String dbName4 = "db4"; + String dbName5 = "db5"; + + Database db1 = getDatabase(adapterId, dbName1, false, false, ""); + Database db2 = getDatabase(adapterId, dbName2, false, false, "cfg"); + Database db3 = getDatabase(adapterId, dbName3, false, false, "cfg"); + Database db4 = getDatabase(adapterId, dbName4, false, false, ""); + Database db5 = getDatabase(adapterId, dbName5, false, false, ""); + + DatabaseRegistry registry1 = getDatabaseRegistry(db1, namespace1, microserviceName1, "", postgresqlType); + DatabaseRegistry registry2 = getDatabaseRegistry(db1, namespace2, microserviceName1, "", postgresqlType); + DatabaseRegistry registry3 = getDatabaseRegistry(db2, namespace2, microserviceName3, "", cassandraType); + DatabaseRegistry registry4 = getDatabaseRegistry(db3, namespace2, microserviceName4, "", cassandraType); + DatabaseRegistry registry5 = getDatabaseRegistry(db4, namespace3, microserviceName5, "", arangoDbType); + DatabaseRegistry registry6 = getDatabaseRegistry(db5, namespace4, microserviceName6, "", arangoDbType); + + Stream.of(registry1, registry2, registry3, registry4, registry5, registry6) + .forEach(databaseRegistryDbaasRepository::saveAnyTypeLogDb); + + Filter filter1 = new Filter(); + filter1.setNamespace(List.of(namespace1)); + filter1.setDatabaseType(List.of(DatabaseType.POSTGRESQL, DatabaseType.CASSANDRA)); + + Filter filter2 = new Filter(); + filter2.setNamespace(List.of(namespace2)); + filter2.setDatabaseType(List.of(DatabaseType.POSTGRESQL, DatabaseType.CASSANDRA)); + + Filter exclude = new Filter(); + exclude.setMicroserviceName(List.of(microserviceName1)); + + Filter exclude2 = new Filter(); + exclude2.setNamespace(List.of(namespace2)); + exclude2.setMicroserviceName(List.of(microserviceName4)); + + FilterCriteria filterCriteria = new FilterCriteria(); + filterCriteria.setFilter(List.of(filter1, filter2)); + filterCriteria.setExclude(List.of(exclude, exclude2)); + + Map> dbToBackup = dbBackupV2Service.getAllDbByFilter(filterCriteria); + assertNotNull(dbToBackup); + assertEquals(1, dbToBackup.size()); + + dbToBackup.forEach((db, registries) -> { + assertEquals(db2.getId(), db.getId()); + assertEquals(1, registries.size()); + assertEquals(registry3, registries.getFirst()); + }); + } + + @Test + void getAllDbByFilter_3() { + String namespace1 = "namespace1"; + String namespace2 = "namespace2"; + String namespace3 = "namespace3"; + String namespace4 = "namespace4"; + + String microserviceName1 = "microserviceName1"; + String microserviceName2 = "microserviceName2"; + String microserviceName3 = "microserviceName3"; + String microserviceName4 = "microserviceName4"; + String microserviceName5 = "microserviceName5"; + + String postgresqlType = "postgresql"; + String arangoDbType = "arangodb"; + String cassandraType = "cassandra"; + String adapterId = "adapterId"; + + String dbName1 = "db1"; + String dbName2 = "db2"; + String dbName3 = "db3"; + String dbName4 = "db4"; + String dbName5 = "db5"; + + Database db1 = getDatabase(adapterId, dbName1, false, false, ""); + Database db2 = getDatabase(adapterId, dbName2, false, false, "cfg"); + Database db3 = getDatabase(adapterId, dbName3, false, false, "cfg"); + Database db4 = getDatabase(adapterId, dbName4, false, false, ""); + Database db5 = getDatabase(adapterId, dbName5, false, false, ""); + + DatabaseRegistry registry1 = getDatabaseRegistry(db1, namespace1, microserviceName1, "", postgresqlType); + DatabaseRegistry registry2 = getDatabaseRegistry(db1, namespace2, microserviceName1, "", postgresqlType); + DatabaseRegistry registry3 = getDatabaseRegistry(db2, namespace2, microserviceName2, "", cassandraType); + DatabaseRegistry registry4 = getDatabaseRegistry(db3, namespace3, microserviceName3, "", cassandraType); + DatabaseRegistry registry5 = getDatabaseRegistry(db4, namespace4, microserviceName4, "", arangoDbType); + DatabaseRegistry registry6 = getDatabaseRegistry(db5, namespace4, microserviceName5, "", arangoDbType); + + Stream.of(registry1, registry2, registry3, registry4, registry5, registry6) + .forEach(databaseRegistryDbaasRepository::saveAnyTypeLogDb); + + Filter filter = new Filter(); + filter.setNamespace(List.of(namespace1, namespace2)); + filter.setMicroserviceName(List.of(microserviceName1)); + + FilterCriteria filterCriteria = new FilterCriteria(); + filterCriteria.setFilter(List.of(filter)); + + Map> filteredDbs = dbBackupV2Service.getAllDbByFilter(filterCriteria); + assertEquals(1, filteredDbs.size()); + + filteredDbs.forEach((db, registries) -> { + if (db.getId() == db1.getId()) { + assertEquals(db1.getId(), db.getId()); + assertEquals(2, registries.size()); + DatabaseRegistry actualRegistry1 = filteredDbs.get(db1).stream() + .filter(r -> namespace1.equals(r.getNamespace())) + .findAny().orElse(null); + + DatabaseRegistry actualRegistry2 = filteredDbs.get(db1).stream() + .filter(r -> namespace2.equals(r.getNamespace())) + .findAny().orElse(null); + assertEquals(registry1, actualRegistry1); + assertEquals(registry2, actualRegistry2); + } else { + assertEquals(db1.getId(), db.getId()); + } + }); + } + + @Test + void getAllDbByFilter_4() { + String namespace1 = "namespace1"; + String namespace2 = "namespace2"; + String namespace3 = "namespace3"; + String namespace4 = "namespace4"; + + String microserviceName1 = "microserviceName1"; + String microserviceName2 = "microserviceName2"; + String microserviceName3 = "microserviceName3"; + String microserviceName4 = "microserviceName4"; + String microserviceName5 = "microserviceName5"; + String microserviceName6 = "microserviceName6"; + + String postgresqlType = "postgresql"; + String arangoDbType = "arangodb"; + String cassandraType = "cassandra"; + String adapterId = "adapterId"; + + String dbName1 = "db1"; + String dbName2 = "db2"; + String dbName3 = "db3"; + String dbName4 = "db4"; + String dbName5 = "db5"; + + Database db1 = getDatabase(adapterId, dbName1, false, false, ""); + Database db2 = getDatabase(adapterId, dbName2, false, false, "cfg"); + Database db3 = getDatabase(adapterId, dbName3, false, false, "cfg"); + Database db4 = getDatabase(adapterId, dbName4, false, false, ""); + Database db5 = getDatabase(adapterId, dbName5, false, false, ""); + + DatabaseRegistry registry1 = getDatabaseRegistry(db1, namespace1, microserviceName1, "", postgresqlType); + DatabaseRegistry registry2 = getDatabaseRegistry(db1, namespace1, microserviceName2, "", postgresqlType); + DatabaseRegistry registry3 = getDatabaseRegistry(db2, namespace2, microserviceName3, "", cassandraType); + DatabaseRegistry registry4 = getDatabaseRegistry(db3, namespace2, microserviceName4, "", cassandraType); + DatabaseRegistry registry5 = getDatabaseRegistry(db4, namespace3, microserviceName5, "", arangoDbType); + DatabaseRegistry registry6 = getDatabaseRegistry(db5, namespace4, microserviceName6, "", arangoDbType); + + Stream.of(registry1, registry2, registry3, registry4, registry5, registry6) + .forEach(databaseRegistryDbaasRepository::saveAnyTypeLogDb); + + Filter filter = new Filter(); + filter.setNamespace(List.of(namespace1)); + + Filter filter1 = new Filter(); + filter1.setNamespace(List.of(namespace1)); + + FilterCriteria filterCriteria = new FilterCriteria(); + filterCriteria.setFilter(List.of(filter, filter1)); + + Map> allDbByFilter = dbBackupV2Service.getAllDbByFilter(filterCriteria); + + assertEquals(1, allDbByFilter.size()); + + allDbByFilter.forEach((db, registries) -> { + assertEquals(db1.getId(), db.getId()); + assertEquals(2, registries.size()); + }); + } + + @Test + void getAllDbByFilter_5() { + String namespace1 = "namespace1"; + String namespace2 = "namespace2"; + String namespace3 = "namespace3"; + String namespace4 = "namespace4"; + + String microserviceName1 = "microserviceName1"; + String microserviceName2 = "microserviceName2"; + String microserviceName3 = "microserviceName3"; + String microserviceName4 = "microserviceName4"; + String microserviceName5 = "microserviceName5"; + String microserviceName6 = "microserviceName6"; + + String postgresqlType = "postgresql"; + String arangoDbType = "arangodb"; + String cassandraType = "cassandra"; + String adapterId = "adapterId"; + + String dbName1 = "db1"; + String dbName2 = "db2"; + String dbName3 = "db3"; + String dbName4 = "db4"; + String dbName5 = "db5"; + + Database db1 = getDatabase(adapterId, dbName1, false, false, ""); + Database db2 = getDatabase(adapterId, dbName2, false, false, "cfg"); + Database db3 = getDatabase(adapterId, dbName3, false, false, "cfg"); + Database db4 = getDatabase(adapterId, dbName4, false, false, ""); + Database db5 = getDatabase(adapterId, dbName5, false, false, ""); + + DatabaseRegistry registry1 = getDatabaseRegistry(db1, namespace1, microserviceName1, "", postgresqlType); + DatabaseRegistry registry2 = getDatabaseRegistry(db1, namespace1, microserviceName2, "", postgresqlType); + DatabaseRegistry registry3 = getDatabaseRegistry(db2, namespace2, microserviceName3, "", cassandraType); + DatabaseRegistry registry4 = getDatabaseRegistry(db3, namespace2, microserviceName4, "", cassandraType); + DatabaseRegistry registry5 = getDatabaseRegistry(db4, namespace3, microserviceName5, "", arangoDbType); + DatabaseRegistry registry6 = getDatabaseRegistry(db5, namespace4, microserviceName6, "", arangoDbType); + + Stream.of(registry1, registry2, registry3, registry4, registry5, registry6) + .forEach(databaseRegistryDbaasRepository::saveAnyTypeLogDb); + + Filter filter = new Filter(); + filter.setDatabaseKind(List.of(DatabaseKind.TRANSACTIONAL, DatabaseKind.CONFIGURATION)); + + Filter exclude = new Filter(); + exclude.setDatabaseKind(List.of(DatabaseKind.CONFIGURATION, DatabaseKind.TRANSACTIONAL, DatabaseKind.TRANSACTIONAL)); //To check distinct method + + FilterCriteria filterCriteria = new FilterCriteria(); + filterCriteria.setFilter(List.of(filter)); + filterCriteria.setExclude(List.of(exclude)); + + assertThrows(DbNotFoundException.class, + () -> dbBackupV2Service.getAllDbByFilter(filterCriteria)); + } + @Test void getAllDbByFilter_whenDatabasesNotFound() { Filter filter = new Filter(); @@ -1591,6 +1913,310 @@ void getAllDbByFilter_whenDatabasesNotFound() { () -> dbBackupV2Service.getAllDbByFilter(filterCriteria)); } + @Test + void getAllDbByFilter_RestorePart_1() { + String dbName1 = "db1"; + String dbName2 = "db2"; + String dbName3 = "db3"; + String dbName4 = "db4"; + String dbName5 = "db5"; + String dbName6 = "db6"; + + String logicalBackupName1 = "lb1"; + String logicalBackupName2 = "db2"; + String logicalBackupName3 = "db3"; + + String adapterId1 = "adpater1"; + String adapterId2 = "adapter2"; + String adapterId3 = "adapter3"; + + String postgresqlType = "postgresql"; + String cassandraType = "cassandra"; + String arangoType = "arangodb"; + + String namespace1 = "namespace1"; + String namespace2 = "namespace2"; + String namespace3 = "namespace3"; + String namespace4 = "namespace4"; + + String microserviceName1 = "microserviceName1"; + String microserviceName2 = "microserviceName2"; + String microserviceName3 = "microserviceName3"; + String microserviceName4 = "microserviceName4"; + String microserviceName5 = "microserviceName5"; + String microserviceName6 = "microserviceName6"; + + BackupDatabase backupDatabase1 = getBackupDatabase(dbName1, List.of(getClassifier(namespace1, microserviceName1, null)), false, BackupTaskStatus.COMPLETED, null); + BackupDatabase backupDatabase2 = getBackupDatabase(dbName2, List.of(getClassifier(namespace1, microserviceName2, null)), false, BackupTaskStatus.COMPLETED, null); + BackupDatabase backupDatabase3 = getBackupDatabase(dbName3, List.of(getClassifier(namespace2, microserviceName3, null)), true, BackupTaskStatus.COMPLETED, null); + BackupDatabase backupDatabase4 = getBackupDatabase(dbName4, List.of(getClassifier(namespace2, microserviceName4, null)), false, BackupTaskStatus.COMPLETED, null); + BackupDatabase backupDatabase5 = getBackupDatabase(dbName5, List.of(getClassifier(namespace3, microserviceName5, null)), true, BackupTaskStatus.COMPLETED, null); + BackupDatabase backupDatabase6 = getBackupDatabase(dbName6, List.of(getClassifier(namespace4, microserviceName6, null)), false, BackupTaskStatus.COMPLETED, null); + + LogicalBackup logicalBackup1 = getLogicalBackup(logicalBackupName1, adapterId1, postgresqlType, List.of(backupDatabase1, backupDatabase2), BackupTaskStatus.COMPLETED, null); + LogicalBackup logicalBackup2 = getLogicalBackup(logicalBackupName2, adapterId2, cassandraType, List.of(backupDatabase3, backupDatabase4), BackupTaskStatus.COMPLETED, null); + LogicalBackup logicalBackup3 = getLogicalBackup(logicalBackupName3, adapterId3, arangoType, List.of(backupDatabase5, backupDatabase6), BackupTaskStatus.COMPLETED, null); + + Filter filter = new Filter(); + filter.setNamespace(List.of(namespace1, namespace2)); + filter.setMicroserviceName(List.of(microserviceName1, microserviceName4)); + filter.setDatabaseType(List.of(DatabaseType.POSTGRESQL, DatabaseType.CASSANDRA)); + filter.setDatabaseKind(List.of(DatabaseKind.TRANSACTIONAL)); + + Filter exclude = new Filter(); + exclude.setMicroserviceName(List.of(microserviceName4)); + + FilterCriteria filterCriteria = new FilterCriteria(); + filterCriteria.setFilter(List.of(filter)); + filterCriteria.setExclude(List.of(exclude)); + + List backupDatabases = List.of(backupDatabase1, backupDatabase2, backupDatabase3, backupDatabase4, backupDatabase5, backupDatabase6); + List filteredDatabases = dbBackupV2Service.getAllDbByFilter(backupDatabases, filterCriteria); + + assertEquals(1, filteredDatabases.size()); + + BackupDatabaseDelegate backupDatabaseDelegate = filteredDatabases.getFirst(); + + assertEquals(backupDatabaseDelegate.backupDatabase(), backupDatabase1); + assertEquals(backupDatabaseDelegate.classifiers().getFirst(), backupDatabase1.getClassifiers().getFirst()); + } + + @Test + void getAllDbByFilter_RestorePart_2() { + String dbName1 = "db1"; + String dbName2 = "db2"; + String dbName3 = "db3"; + String dbName4 = "db4"; + String dbName5 = "db5"; + String dbName6 = "db6"; + + String logicalBackupName1 = "lb1"; + String logicalBackupName2 = "db2"; + String logicalBackupName3 = "db3"; + + String adapterId1 = "adpater1"; + String adapterId2 = "adapter2"; + String adapterId3 = "adapter3"; + + String postgresqlType = "postgresql"; + String cassandraType = "cassandra"; + String arangoType = "arangodb"; + + String namespace1 = "namespace1"; + String namespace2 = "namespace2"; + String namespace3 = "namespace3"; + String namespace4 = "namespace4"; + + String microserviceName1 = "microserviceName1"; + String microserviceName2 = "microserviceName2"; + String microserviceName3 = "microserviceName3"; + String microserviceName4 = "microserviceName4"; + String microserviceName5 = "microserviceName5"; + String microserviceName6 = "microserviceName6"; + + BackupDatabase backupDatabase1 = getBackupDatabase(dbName1, List.of(getClassifier(namespace1, microserviceName1, null)), false, BackupTaskStatus.COMPLETED, null); + BackupDatabase backupDatabase2 = getBackupDatabase(dbName2, List.of(getClassifier(namespace1, microserviceName2, null)), false, BackupTaskStatus.COMPLETED, null); + BackupDatabase backupDatabase3 = getBackupDatabase(dbName3, List.of(getClassifier(namespace2, microserviceName3, null)), true, BackupTaskStatus.COMPLETED, null); + BackupDatabase backupDatabase4 = getBackupDatabase(dbName4, List.of(getClassifier(namespace2, microserviceName4, null)), false, BackupTaskStatus.COMPLETED, null); + BackupDatabase backupDatabase5 = getBackupDatabase(dbName5, List.of(getClassifier(namespace3, microserviceName5, null)), true, BackupTaskStatus.COMPLETED, null); + BackupDatabase backupDatabase6 = getBackupDatabase(dbName6, List.of(getClassifier(namespace4, microserviceName6, null)), false, BackupTaskStatus.COMPLETED, null); + + LogicalBackup logicalBackup1 = getLogicalBackup(logicalBackupName1, adapterId1, postgresqlType, List.of(backupDatabase1, backupDatabase2), BackupTaskStatus.COMPLETED, null); + LogicalBackup logicalBackup2 = getLogicalBackup(logicalBackupName2, adapterId2, cassandraType, List.of(backupDatabase3, backupDatabase4), BackupTaskStatus.COMPLETED, null); + LogicalBackup logicalBackup3 = getLogicalBackup(logicalBackupName3, adapterId3, arangoType, List.of(backupDatabase5, backupDatabase6), BackupTaskStatus.COMPLETED, null); + + Filter filter1 = new Filter(); + filter1.setNamespace(List.of(namespace1)); + filter1.setDatabaseType(List.of(DatabaseType.POSTGRESQL, DatabaseType.CASSANDRA)); + filter1.setDatabaseKind(List.of(DatabaseKind.TRANSACTIONAL)); + + Filter filter2 = new Filter(); + filter2.setNamespace(List.of(namespace2)); + filter2.setMicroserviceName(List.of(microserviceName3)); + filter2.setDatabaseType(List.of(DatabaseType.POSTGRESQL, DatabaseType.CASSANDRA)); + + Filter exclude = new Filter(); + exclude.setDatabaseType(List.of(DatabaseType.POSTGRESQL)); + + FilterCriteria filterCriteria = new FilterCriteria(); + filterCriteria.setFilter(List.of(filter1, filter2)); + filterCriteria.setExclude(List.of(exclude)); + + List backupDatabases = List.of(backupDatabase1, backupDatabase2, backupDatabase3, backupDatabase4, backupDatabase5, backupDatabase6); + List filteredDatabases = dbBackupV2Service.getAllDbByFilter(backupDatabases, filterCriteria); + + assertEquals(1, filteredDatabases.size()); + + BackupDatabaseDelegate backupDatabaseDelegate = filteredDatabases.getFirst(); + + assertEquals(backupDatabaseDelegate.backupDatabase(), backupDatabase3); + assertEquals(backupDatabaseDelegate.classifiers().getFirst(), backupDatabase3.getClassifiers().getFirst()); + } + + @Test + void getAllDbByFilter_RestorePart_3() { + String dbName1 = "db1"; + String dbName2 = "db2"; + String dbName3 = "db3"; + + String logicalBackupName1 = "lb1"; + String logicalBackupName2 = "db2"; + + String adapterId1 = "adpater1"; + String adapterId2 = "adapter2"; + + String postgresqlType = "postgresql"; + String cassandraType = "cassandra"; + + String namespace1 = "namespace1"; + String namespace2 = "namespace2"; + String namespace3 = "namespace3"; + + String microserviceName1 = "microserviceName1"; + String microserviceName2 = "microserviceName2"; + String microserviceName3 = "microserviceName3"; + + BackupDatabase backupDatabase1 = getBackupDatabase(dbName1, List.of(getClassifier(namespace1, microserviceName1, null), getClassifier(namespace2, microserviceName1, null)), false, BackupTaskStatus.COMPLETED, null); + BackupDatabase backupDatabase2 = getBackupDatabase(dbName2, List.of(getClassifier(namespace2, microserviceName2, null)), false, BackupTaskStatus.COMPLETED, null); + BackupDatabase backupDatabase3 = getBackupDatabase(dbName3, List.of(getClassifier(namespace3, microserviceName3, null)), true, BackupTaskStatus.COMPLETED, null); + + LogicalBackup logicalBackup1 = getLogicalBackup(logicalBackupName1, adapterId1, postgresqlType, List.of(backupDatabase1, backupDatabase2), BackupTaskStatus.COMPLETED, null); + LogicalBackup logicalBackup2 = getLogicalBackup(logicalBackupName2, adapterId2, cassandraType, List.of(backupDatabase3), BackupTaskStatus.COMPLETED, null); + + Filter filter1 = new Filter(); + filter1.setNamespace(List.of(namespace1)); + + Filter filter2 = new Filter(); + filter2.setNamespace(List.of(namespace2)); + + FilterCriteria filterCriteria = new FilterCriteria(); + filterCriteria.setFilter(List.of(filter1, filter2)); + + List backupDatabases = List.of(backupDatabase1, backupDatabase2, backupDatabase3); + List filteredDatabases = dbBackupV2Service.getAllDbByFilter(backupDatabases, filterCriteria); + + assertEquals(2, filteredDatabases.size()); + + BackupDatabaseDelegate backupDatabaseDelegate1 = filteredDatabases.stream() + .filter(db -> dbName1.equals(db.backupDatabase().getName())) + .findAny().orElse(null); + assertNotNull(backupDatabaseDelegate1); + assertEquals(backupDatabaseDelegate1.backupDatabase(), backupDatabase1); + assertEquals(2, backupDatabaseDelegate1.classifiers().size()); + + SortedMap classifier1 = backupDatabase1.getClassifiers().stream() + .filter(classifier -> namespace1.equals(classifier.get(NAMESPACE))) + .findAny().orElse(null); + SortedMap classifier2 = backupDatabase1.getClassifiers().stream() + .filter(classifier -> namespace2.equals(classifier.get(NAMESPACE))) + .findAny().orElse(null); + assertNotNull(classifier1); + assertNotNull(classifier2); + + BackupDatabaseDelegate backupDatabaseDelegate2 = filteredDatabases.stream() + .filter(db -> dbName2.equals(db.backupDatabase().getName())) + .findAny().orElse(null); + assertNotNull(backupDatabaseDelegate2); + assertEquals(backupDatabaseDelegate2.backupDatabase(), backupDatabase2); + assertEquals(1, backupDatabaseDelegate2.classifiers().size()); + assertEquals(backupDatabaseDelegate2.classifiers().getFirst(), backupDatabase2.getClassifiers().getFirst()); + } + + @Test + void getAllDbByFilter_RestorePart_4() { + String dbName1 = "db1"; + String dbName2 = "db2"; + String dbName3 = "db3"; + + String logicalBackupName1 = "lb1"; + String logicalBackupName2 = "db2"; + + String adapterId1 = "adpater1"; + String adapterId2 = "adapter2"; + + String postgresqlType = "postgresql"; + String cassandraType = "cassandra"; + + String namespace1 = "namespace1"; + String namespace2 = "namespace2"; + String namespace3 = "namespace3"; + + String microserviceName1 = "microserviceName1"; + String microserviceName2 = "microserviceName2"; + String microserviceName3 = "microserviceName3"; + + BackupDatabase backupDatabase1 = getBackupDatabase(dbName1, List.of(getClassifier(namespace1, microserviceName1, null), getClassifier(namespace2, microserviceName1, null)), false, BackupTaskStatus.COMPLETED, null); + BackupDatabase backupDatabase2 = getBackupDatabase(dbName2, List.of(getClassifier(namespace2, microserviceName2, null)), false, BackupTaskStatus.COMPLETED, null); + BackupDatabase backupDatabase3 = getBackupDatabase(dbName3, List.of(getClassifier(namespace3, microserviceName3, null)), true, BackupTaskStatus.COMPLETED, null); + + LogicalBackup logicalBackup1 = getLogicalBackup(logicalBackupName1, adapterId1, postgresqlType, List.of(backupDatabase1, backupDatabase2), BackupTaskStatus.COMPLETED, null); + LogicalBackup logicalBackup2 = getLogicalBackup(logicalBackupName2, adapterId2, cassandraType, List.of(backupDatabase3), BackupTaskStatus.COMPLETED, null); + + Filter filter1 = new Filter(); + filter1.setNamespace(List.of(namespace1)); + + FilterCriteria filterCriteria = new FilterCriteria(); + filterCriteria.setFilter(List.of(filter1)); + + List backupDatabases = List.of(backupDatabase1, backupDatabase2, backupDatabase3); + List filteredDatabases = dbBackupV2Service.getAllDbByFilter(backupDatabases, filterCriteria); + + assertEquals(1, filteredDatabases.size()); + + BackupDatabaseDelegate backupDatabaseDelegate1 = filteredDatabases.stream() + .filter(db -> dbName1.equals(db.backupDatabase().getName())) + .findAny().orElse(null); + assertNotNull(backupDatabaseDelegate1); + assertEquals(backupDatabaseDelegate1.backupDatabase(), backupDatabase1); + assertEquals(1, backupDatabaseDelegate1.classifiers().size()); + + SortedMap classifier1 = backupDatabase1.getClassifiers().stream() + .filter(classifier -> namespace1.equals(classifier.get(NAMESPACE))) + .findAny().orElse(null); + assertNotNull(classifier1); + } + + @Test + void validateAndFilterExternalDb_testFiltering() { + String namespace1 = "namespace1"; + String namespace2 = "namespace2"; + String namespace3 = "namespace3"; + + String microserviceName1 = "microserviceName1"; + String microserviceName2 = "microserviceName2"; + String microserviceName3 = "microserviceName3"; + + String dbName1 = "db1"; + String dbName2 = "db2"; + String dbName3 = "db3"; + + String postgresqlType = "postgresql"; + SortedMap classifier = getClassifier(namespace1, microserviceName1, null); + + BackupExternalDatabase externalDatabase1 = getBackupExternalDatabase(dbName1, postgresqlType, List.of(classifier)); + BackupExternalDatabase externalDatabase2 = getBackupExternalDatabase(dbName2, postgresqlType, List.of(getClassifier(namespace2, microserviceName2, null))); + BackupExternalDatabase externalDatabase3 = getBackupExternalDatabase(dbName3, postgresqlType, List.of(getClassifier(namespace3, microserviceName3, null))); + + Filter filter = new Filter(); + filter.setNamespace(List.of(namespace1, namespace2)); + + Filter exclude = new Filter(); + exclude.setMicroserviceName(List.of(microserviceName2)); + + FilterCriteria filterCriteria = new FilterCriteria(); + filterCriteria.setFilter(List.of(filter)); + filterCriteria.setExclude(List.of(exclude)); + + List restoreExternalDatabases = dbBackupV2Service.validateAndFilterExternalDb(List.of(externalDatabase1, externalDatabase2, externalDatabase3), ExternalDatabaseStrategy.INCLUDE, filterCriteria); + assertEquals(1, restoreExternalDatabases.size()); + + RestoreExternalDatabase externalDb = restoreExternalDatabases.getFirst(); + assertEquals(dbName1, externalDb.getName()); + assertEquals(postgresqlType, externalDb.getType()); + assertEquals(1, externalDb.getClassifiers().size()); + assertEquals(classifier, externalDb.getClassifiers().getFirst()); + } + @Test void validateAndFilterDatabasesForBackup_ExternalDatabaseStrategyInclude() { String namespace = "namespace"; @@ -2143,6 +2769,7 @@ void uploadBackupMetadata_restoreDeletedBackup_digestMismatch() { BackupResponse backupResponse = getBackupResponse(backupName, namespace); backupResponse.setDigest(anotherDigest); + IntegrityViolationException ex = assertThrows(IntegrityViolationException.class, () -> dbBackupV2Service.uploadBackupMetadata(backupResponse)); assertEquals( diff --git a/docs/OpenAPI.json b/docs/OpenAPI.json index f45181c5..3775c7bd 100644 --- a/docs/OpenAPI.json +++ b/docs/OpenAPI.json @@ -338,13 +338,6 @@ }, "description": "Apply the filter to the remaining databases" }, - "include": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Filter" - }, - "description": "Include databases that match any of the filters in the list" - }, "exclude": { "type": "array", "items": { @@ -452,13 +445,6 @@ }, "description": "Apply the filter to the remaining databases" }, - "include": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Filter" - }, - "description": "Include databases that match any of the filters in the list" - }, "exclude": { "type": "array", "items": { @@ -1802,13 +1788,6 @@ }, "description": "Apply the filter to the remaining databases" }, - "include": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Filter" - }, - "description": "Include databases that match any of the filters in the list" - }, "exclude": { "type": "array", "items": { @@ -3282,13 +3261,6 @@ }, "description": "Apply the filter to the remaining databases" }, - "include": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Filter" - }, - "description": "Include databases that match any of the filters in the list" - }, "exclude": { "type": "array", "items": { @@ -3412,13 +3384,6 @@ }, "description": "Apply the filter to the remaining databases" }, - "include": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Filter" - }, - "description": "Include databases that match any of the filters in the list" - }, "exclude": { "type": "array", "items": {