From 2c55b575e59783ed4c0c87bd02d7aaf9ccdea24b Mon Sep 17 00:00:00 2001 From: "m.zharinova" Date: Fri, 25 Oct 2024 15:03:16 +0500 Subject: [PATCH 01/14] add base classes to generate migations via POST --- .../build.gradle.kts | 1 + .../controller/DbMigrationController.java | 23 +++++++++++++ .../demo/dto/ForeignKeyMigrationRequest.java | 11 +++++++ .../demo/dto/ForeignKeyMigrationResponse.java | 13 ++++++++ .../service/DbMigrationGeneratorService.java | 32 +++++++++++++++++++ 5 files changed, 80 insertions(+) create mode 100644 pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java create mode 100644 pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationRequest.java create mode 100644 pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java create mode 100644 pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java diff --git a/pg-index-health-spring-boot-demo/build.gradle.kts b/pg-index-health-spring-boot-demo/build.gradle.kts index 47d25b4..b5623d6 100644 --- a/pg-index-health-spring-boot-demo/build.gradle.kts +++ b/pg-index-health-spring-boot-demo/build.gradle.kts @@ -10,6 +10,7 @@ plugins { dependencies { implementation(project(":db-migrations")) + implementation("io.github.mfvanek:pg-index-health-generator") implementation(platform("org.springframework.boot:spring-boot-dependencies:3.3.4")) implementation(platform("org.apache.httpcomponents.client5:httpclient5-parent:5.4")) implementation(platform("org.springdoc:springdoc-openapi:2.6.0")) diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java new file mode 100644 index 0000000..9f5a7c4 --- /dev/null +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java @@ -0,0 +1,23 @@ +package io.github.mfvanek.pg.index.health.demo.controller; + +import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; +import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; +import io.github.mfvanek.pg.index.health.demo.service.DbMigrationGeneratorService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequiredArgsConstructor +@RestController +@RequestMapping("/db/migration") +public class DbMigrationController { + + private final DbMigrationGeneratorService dbMigrationGeneratorService; + + @PostMapping("/generate") + public ForeignKeyMigrationResponse generateFKMigration(@RequestBody ForeignKeyMigrationRequest foreignKeyMigrationRequest) { + return dbMigrationGeneratorService.addIndexesWithFKChecks(foreignKeyMigrationRequest); + } +} diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationRequest.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationRequest.java new file mode 100644 index 0000000..6bb95d7 --- /dev/null +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationRequest.java @@ -0,0 +1,11 @@ +package io.github.mfvanek.pg.index.health.demo.dto; + +import io.github.mfvanek.pg.connection.ConnectionCredentials; +import io.github.mfvanek.pg.connection.HighAvailabilityPgConnectionFactory; + +public record ForeignKeyMigrationRequest( + HighAvailabilityPgConnectionFactory connectionFactory, + ConnectionCredentials credentials +) { + +} diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java new file mode 100644 index 0000000..ee85630 --- /dev/null +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java @@ -0,0 +1,13 @@ +package io.github.mfvanek.pg.index.health.demo.dto; + +import io.github.mfvanek.pg.model.constraint.ForeignKey; + +import java.util.List; + +public record ForeignKeyMigrationResponse( + List foreignKeysBefore, + List foreignKeysAfter, + List generatedMigrations +) { + +} diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java new file mode 100644 index 0000000..c053fe7 --- /dev/null +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java @@ -0,0 +1,32 @@ +package io.github.mfvanek.pg.index.health.demo.service; + +import io.github.mfvanek.pg.connection.ConnectionCredentials; +import io.github.mfvanek.pg.connection.HighAvailabilityPgConnectionFactory; +import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; +import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; +import io.github.mfvanek.pg.model.constraint.ForeignKey; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@RequiredArgsConstructor +@Service +public class DbMigrationGeneratorService { + public ForeignKeyMigrationResponse addIndexesWithFKChecks(ForeignKeyMigrationRequest fKMigrationRequest){ + var keysBefore = getFKsFromDb(fKMigrationRequest.connectionFactory(), fKMigrationRequest.credentials()); + var migrations = generategMigrations(keysBefore); + var keysAfter = getFKsFromDb(fKMigrationRequest.connectionFactory(), fKMigrationRequest.credentials()); + if (!keysAfter.isEmpty()) throw new RuntimeException(); + return new ForeignKeyMigrationResponse(keysBefore, keysAfter, migrations); + } + + private List getFKsFromDb (HighAvailabilityPgConnectionFactory connectionFactory, ConnectionCredentials credentials) { + return List.of(); + } + private List generategMigrations(List foreignKeys) { + return List.of(); + } +} From fdb63c42e441cb934d2b3db877b4f499d0253082 Mon Sep 17 00:00:00 2001 From: "m.zharinova" Date: Fri, 25 Oct 2024 15:23:34 +0500 Subject: [PATCH 02/14] add exception handling --- .../pg/index/health/demo/dto/MigrationError.java | 8 ++++++++ .../health/demo/exception/ExceptionHandler.java | 14 ++++++++++++++ .../health/demo/exception/MigrationException.java | 8 ++++++++ .../demo/service/DbMigrationGeneratorService.java | 3 ++- 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/MigrationError.java create mode 100644 pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/ExceptionHandler.java create mode 100644 pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/MigrationError.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/MigrationError.java new file mode 100644 index 0000000..b4567c6 --- /dev/null +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/MigrationError.java @@ -0,0 +1,8 @@ +package io.github.mfvanek.pg.index.health.demo.dto; + +public record MigrationError( + int statusCode, + String message +) { + +} diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/ExceptionHandler.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/ExceptionHandler.java new file mode 100644 index 0000000..3fd9d49 --- /dev/null +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/ExceptionHandler.java @@ -0,0 +1,14 @@ +package io.github.mfvanek.pg.index.health.demo.exception; + +import io.github.mfvanek.pg.index.health.demo.dto.MigrationError; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; + +@ControllerAdvice +public class ExceptionHandler { + @org.springframework.web.bind.annotation.ExceptionHandler + public ResponseEntity catchMigrationException(MigrationException e) { + return new ResponseEntity<>(new MigrationError(HttpStatus.EXPECTATION_FAILED.value(), e.getMessage()), HttpStatus.EXPECTATION_FAILED); + } +} diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java new file mode 100644 index 0000000..cb4f097 --- /dev/null +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java @@ -0,0 +1,8 @@ +package io.github.mfvanek.pg.index.health.demo.exception; + +public class MigrationException extends IllegalStateException { + + public MigrationException(String message) { + super(message); + } +} diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java index c053fe7..594580f 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java @@ -4,6 +4,7 @@ import io.github.mfvanek.pg.connection.HighAvailabilityPgConnectionFactory; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; +import io.github.mfvanek.pg.index.health.demo.exception.MigrationException; import io.github.mfvanek.pg.model.constraint.ForeignKey; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -19,7 +20,7 @@ public ForeignKeyMigrationResponse addIndexesWithFKChecks(ForeignKeyMigrationReq var keysBefore = getFKsFromDb(fKMigrationRequest.connectionFactory(), fKMigrationRequest.credentials()); var migrations = generategMigrations(keysBefore); var keysAfter = getFKsFromDb(fKMigrationRequest.connectionFactory(), fKMigrationRequest.credentials()); - if (!keysAfter.isEmpty()) throw new RuntimeException(); + if (!keysAfter.isEmpty()) throw new MigrationException("There should be no foreign keys not covered by the index"); return new ForeignKeyMigrationResponse(keysBefore, keysAfter, migrations); } From 05f240e923e29ae42300c8ca262172dce581893f Mon Sep 17 00:00:00 2001 From: "m.zharinova" Date: Tue, 29 Oct 2024 12:46:57 +0500 Subject: [PATCH 03/14] add test --- .../controller/DbMigrationController.java | 13 +++- .../demo/dto/ForeignKeyMigrationRequest.java | 9 ++- .../demo/dto/ForeignKeyMigrationResponse.java | 7 +++ .../index/health/demo/dto/MigrationError.java | 7 +++ .../demo/exception/ExceptionHandler.java | 12 +++- .../demo/exception/MigrationException.java | 14 ++++- .../service/DbMigrationGeneratorService.java | 59 +++++++++++++++---- .../controller/DbMigrationControllerTest.java | 53 +++++++++++++++++ 8 files changed, 156 insertions(+), 18 deletions(-) create mode 100644 pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java index 9f5a7c4..f1660f2 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java @@ -1,7 +1,14 @@ +/* + * Copyright (c) 2019-2024. Ivan Vakhrushev and others. + * https://github.com/mfvanek/pg-index-health-demo + * + * Licensed under the Apache License 2.0 + */ + package io.github.mfvanek.pg.index.health.demo.controller; -import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; +import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; import io.github.mfvanek.pg.index.health.demo.service.DbMigrationGeneratorService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; @@ -17,7 +24,7 @@ public class DbMigrationController { private final DbMigrationGeneratorService dbMigrationGeneratorService; @PostMapping("/generate") - public ForeignKeyMigrationResponse generateFKMigration(@RequestBody ForeignKeyMigrationRequest foreignKeyMigrationRequest) { - return dbMigrationGeneratorService.addIndexesWithFKChecks(foreignKeyMigrationRequest); + public ForeignKeyMigrationResponse generateFkMigration(@RequestBody final ForeignKeyMigrationRequest foreignKeyMigrationRequest) { + return dbMigrationGeneratorService.addIndexesWithFkChecks(foreignKeyMigrationRequest); } } diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationRequest.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationRequest.java index 6bb95d7..55edb5e 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationRequest.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationRequest.java @@ -1,10 +1,15 @@ +/* + * Copyright (c) 2019-2024. Ivan Vakhrushev and others. + * https://github.com/mfvanek/pg-index-health-demo + * + * Licensed under the Apache License 2.0 + */ + package io.github.mfvanek.pg.index.health.demo.dto; import io.github.mfvanek.pg.connection.ConnectionCredentials; -import io.github.mfvanek.pg.connection.HighAvailabilityPgConnectionFactory; public record ForeignKeyMigrationRequest( - HighAvailabilityPgConnectionFactory connectionFactory, ConnectionCredentials credentials ) { diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java index ee85630..72df566 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java @@ -1,3 +1,10 @@ +/* + * Copyright (c) 2019-2024. Ivan Vakhrushev and others. + * https://github.com/mfvanek/pg-index-health-demo + * + * Licensed under the Apache License 2.0 + */ + package io.github.mfvanek.pg.index.health.demo.dto; import io.github.mfvanek.pg.model.constraint.ForeignKey; diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/MigrationError.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/MigrationError.java index b4567c6..4bee4ca 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/MigrationError.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/MigrationError.java @@ -1,3 +1,10 @@ +/* + * Copyright (c) 2019-2024. Ivan Vakhrushev and others. + * https://github.com/mfvanek/pg-index-health-demo + * + * Licensed under the Apache License 2.0 + */ + package io.github.mfvanek.pg.index.health.demo.dto; public record MigrationError( diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/ExceptionHandler.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/ExceptionHandler.java index 3fd9d49..a09d80f 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/ExceptionHandler.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/ExceptionHandler.java @@ -1,3 +1,10 @@ +/* + * Copyright (c) 2019-2024. Ivan Vakhrushev and others. + * https://github.com/mfvanek/pg-index-health-demo + * + * Licensed under the Apache License 2.0 + */ + package io.github.mfvanek.pg.index.health.demo.exception; import io.github.mfvanek.pg.index.health.demo.dto.MigrationError; @@ -7,8 +14,9 @@ @ControllerAdvice public class ExceptionHandler { + @org.springframework.web.bind.annotation.ExceptionHandler - public ResponseEntity catchMigrationException(MigrationException e) { - return new ResponseEntity<>(new MigrationError(HttpStatus.EXPECTATION_FAILED.value(), e.getMessage()), HttpStatus.EXPECTATION_FAILED); + public ResponseEntity catchMigrationException(final MigrationException migrationException) { + return new ResponseEntity<>(new MigrationError(HttpStatus.EXPECTATION_FAILED.value(), migrationException.getMessage()), HttpStatus.EXPECTATION_FAILED); } } diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java index cb4f097..568da04 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java @@ -1,8 +1,20 @@ +/* + * Copyright (c) 2019-2024. Ivan Vakhrushev and others. + * https://github.com/mfvanek/pg-index-health-demo + * + * Licensed under the Apache License 2.0 + */ + package io.github.mfvanek.pg.index.health.demo.exception; +import java.io.Serial; + public class MigrationException extends IllegalStateException { - public MigrationException(String message) { + @Serial + private static final long serialVersionUID = 42L; + + public MigrationException(final String message) { super(message); } } diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java index 594580f..11f9f7d 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java @@ -1,33 +1,72 @@ +/* + * Copyright (c) 2019-2024. Ivan Vakhrushev and others. + * https://github.com/mfvanek/pg-index-health-demo + * + * Licensed under the Apache License 2.0 + */ + package io.github.mfvanek.pg.index.health.demo.service; +import io.github.mfvanek.pg.checks.cluster.ForeignKeysNotCoveredWithIndexCheckOnCluster; +import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnCluster; import io.github.mfvanek.pg.connection.ConnectionCredentials; +import io.github.mfvanek.pg.connection.HighAvailabilityPgConnection; import io.github.mfvanek.pg.connection.HighAvailabilityPgConnectionFactory; -import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; +import io.github.mfvanek.pg.generator.DbMigrationGenerator; +import io.github.mfvanek.pg.generator.ForeignKeyMigrationGenerator; +import io.github.mfvanek.pg.generator.GeneratingOptions; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; +import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; import io.github.mfvanek.pg.index.health.demo.exception.MigrationException; +import io.github.mfvanek.pg.model.PgContext; import io.github.mfvanek.pg.model.constraint.ForeignKey; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; import java.util.List; +import javax.sql.DataSource; @Slf4j @RequiredArgsConstructor @Service public class DbMigrationGeneratorService { - public ForeignKeyMigrationResponse addIndexesWithFKChecks(ForeignKeyMigrationRequest fKMigrationRequest){ - var keysBefore = getFKsFromDb(fKMigrationRequest.connectionFactory(), fKMigrationRequest.credentials()); - var migrations = generategMigrations(keysBefore); - var keysAfter = getFKsFromDb(fKMigrationRequest.connectionFactory(), fKMigrationRequest.credentials()); - if (!keysAfter.isEmpty()) throw new MigrationException("There should be no foreign keys not covered by the index"); + + private final HighAvailabilityPgConnectionFactory highAvailabilityPgConnectionFactory; + private final DataSource dataSource; + + public ForeignKeyMigrationResponse addIndexesWithFkChecks(final ForeignKeyMigrationRequest fkMigrationRequest) { + final List keysBefore = getFksFromDb(fkMigrationRequest.credentials()); + final List migrations = generatedMigrations(keysBefore); + final List keysAfter = getFksFromDb(fkMigrationRequest.credentials()); + if (!keysAfter.isEmpty()) { + throw new MigrationException("There should be no foreign keys not covered by the index"); + } return new ForeignKeyMigrationResponse(keysBefore, keysAfter, migrations); } - private List getFKsFromDb (HighAvailabilityPgConnectionFactory connectionFactory, ConnectionCredentials credentials) { - return List.of(); + private List getFksFromDb(final ConnectionCredentials credentials) { + final HighAvailabilityPgConnection haPgConnection = highAvailabilityPgConnectionFactory.of(credentials); + final DatabaseCheckOnCluster foreignKeysNotCoveredWithIndex = new ForeignKeysNotCoveredWithIndexCheckOnCluster(haPgConnection); + return foreignKeysNotCoveredWithIndex.check(PgContext.of("demo")); } - private List generategMigrations(List foreignKeys) { - return List.of(); + + private List generatedMigrations(final List foreignKeys) { + final DbMigrationGenerator generator = new ForeignKeyMigrationGenerator(GeneratingOptions.builder().build()); + final List generatedMigrations = generator.generate(foreignKeys); + log.info("Generated migrations: {}", generatedMigrations); + try (Connection connection = dataSource.getConnection()) { + for (final String migration : generatedMigrations) { + try (Statement statement = connection.createStatement()) { + statement.execute(migration); + } + } + } catch (SQLException e) { + log.error("ERROR", e); + } + return generatedMigrations; } } diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java new file mode 100644 index 0000000..252c906 --- /dev/null +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019-2024. Ivan Vakhrushev and others. + * https://github.com/mfvanek/pg-index-health-demo + * + * Licensed under the Apache License 2.0 + */ + +package io.github.mfvanek.pg.index.health.demo.controller; + +import io.github.mfvanek.pg.connection.ConnectionCredentials; +import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; +import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; +import io.github.mfvanek.pg.index.health.demo.utils.BasePgIndexHealthDemoSpringBootTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.BodyInserters; + +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; + +class DbMigrationControllerTest extends BasePgIndexHealthDemoSpringBootTest { + + @Autowired + private ApplicationContext context; + + @Test + void runsMigrations() { + final ConnectionCredentials creds = ConnectionCredentials.ofUrl( + Objects.requireNonNull(context.getEnvironment().getProperty("spring.datasource.url")), + Objects.requireNonNull(context.getEnvironment().getProperty("spring.datasource.userName")), + Objects.requireNonNull(context.getEnvironment().getProperty("spring.datasource.password"))); + final ForeignKeyMigrationRequest foreignKeyMigrationRequest = new ForeignKeyMigrationRequest(creds); + final ForeignKeyMigrationResponse result = webTestClient + .post() + .uri(uriBuilder -> uriBuilder + .pathSegment("db", "migration", "generate") + .build()) + .body(BodyInserters.fromValue(foreignKeyMigrationRequest)) + .accept(MediaType.APPLICATION_JSON) + .headers(this::setUpBasicAuth) + .exchange() + .expectStatus().isOk() + .expectBody(ForeignKeyMigrationResponse.class) + .returnResult() + .getResponseBody(); + + assertThat(result).isNotNull(); + } + +} From 5c9f6489ae9ca6f4c117bf91a35784c7dc0bc3eb Mon Sep 17 00:00:00 2001 From: "m.zharinova" Date: Tue, 29 Oct 2024 13:35:23 +0500 Subject: [PATCH 04/14] suppress warnings, test passes --- .../demo/controller/DbMigrationController.java | 2 ++ .../demo/dto/ForeignKeyMigrationResponse.java | 2 ++ .../demo/exception/MigrationException.java | 6 +----- .../service/DbMigrationGeneratorService.java | 5 +++++ .../controller/DbMigrationControllerTest.java | 16 +++++++--------- 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java index f1660f2..d7b6faa 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java @@ -7,6 +7,7 @@ package io.github.mfvanek.pg.index.health.demo.controller; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; import io.github.mfvanek.pg.index.health.demo.service.DbMigrationGeneratorService; @@ -16,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +@SuppressFBWarnings("EI_EXPOSE_REP2") @RequiredArgsConstructor @RestController @RequestMapping("/db/migration") diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java index 72df566..fb55ea7 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java @@ -7,10 +7,12 @@ package io.github.mfvanek.pg.index.health.demo.dto; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.github.mfvanek.pg.model.constraint.ForeignKey; import java.util.List; +@SuppressFBWarnings("EI_EXPOSE_REP2") public record ForeignKeyMigrationResponse( List foreignKeysBefore, List foreignKeysAfter, diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java index 568da04..c998ae8 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java @@ -7,13 +7,9 @@ package io.github.mfvanek.pg.index.health.demo.exception; -import java.io.Serial; - +@SuppressWarnings("serial") public class MigrationException extends IllegalStateException { - @Serial - private static final long serialVersionUID = 42L; - public MigrationException(final String message) { super(message); } diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java index 11f9f7d..797f2e6 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java @@ -7,6 +7,7 @@ package io.github.mfvanek.pg.index.health.demo.service; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.github.mfvanek.pg.checks.cluster.ForeignKeysNotCoveredWithIndexCheckOnCluster; import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnCluster; import io.github.mfvanek.pg.connection.ConnectionCredentials; @@ -23,6 +24,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.sql.Connection; import java.sql.SQLException; @@ -30,9 +32,11 @@ import java.util.List; import javax.sql.DataSource; +@SuppressFBWarnings("EI_EXPOSE_REP2") @Slf4j @RequiredArgsConstructor @Service +@Transactional public class DbMigrationGeneratorService { private final HighAvailabilityPgConnectionFactory highAvailabilityPgConnectionFactory; @@ -54,6 +58,7 @@ private List getFksFromDb(final ConnectionCredentials credentials) { return foreignKeysNotCoveredWithIndex.check(PgContext.of("demo")); } + @SuppressFBWarnings("SIL_SQL_IN_LOOP") private List generatedMigrations(final List foreignKeys) { final DbMigrationGenerator generator = new ForeignKeyMigrationGenerator(GeneratingOptions.builder().build()); final List generatedMigrations = generator.generate(foreignKeys); diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java index 252c906..69b0718 100644 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java @@ -13,26 +13,24 @@ import io.github.mfvanek.pg.index.health.demo.utils.BasePgIndexHealthDemoSpringBootTest; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; import org.springframework.http.MediaType; import org.springframework.web.reactive.function.BodyInserters; - -import java.util.Objects; +import org.testcontainers.containers.JdbcDatabaseContainer; import static org.assertj.core.api.Assertions.assertThat; class DbMigrationControllerTest extends BasePgIndexHealthDemoSpringBootTest { @Autowired - private ApplicationContext context; + private JdbcDatabaseContainer jdbcDatabaseContainer; @Test void runsMigrations() { - final ConnectionCredentials creds = ConnectionCredentials.ofUrl( - Objects.requireNonNull(context.getEnvironment().getProperty("spring.datasource.url")), - Objects.requireNonNull(context.getEnvironment().getProperty("spring.datasource.userName")), - Objects.requireNonNull(context.getEnvironment().getProperty("spring.datasource.password"))); - final ForeignKeyMigrationRequest foreignKeyMigrationRequest = new ForeignKeyMigrationRequest(creds); + final ConnectionCredentials credentials = ConnectionCredentials.ofUrl( + jdbcDatabaseContainer.getJdbcUrl(), + jdbcDatabaseContainer.getUsername(), + jdbcDatabaseContainer.getPassword()); + final ForeignKeyMigrationRequest foreignKeyMigrationRequest = new ForeignKeyMigrationRequest(credentials); final ForeignKeyMigrationResponse result = webTestClient .post() .uri(uriBuilder -> uriBuilder From 43c13c5d2ac4e6a66005b99b7dc5914a2580087c Mon Sep 17 00:00:00 2001 From: "m.zharinova" Date: Wed, 30 Oct 2024 09:57:33 +0500 Subject: [PATCH 05/14] add another test class --- .../demo/service/DbMigrationGeneratorService.java | 2 +- .../demo/controller/DbMigrationControllerTest.java | 8 ++++++++ .../health/demo/service/DbMigrationServiceTest.java | 12 ++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationServiceTest.java diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java index 797f2e6..28d995e 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java @@ -70,7 +70,7 @@ private List generatedMigrations(final List foreignKeys) { } } } catch (SQLException e) { - log.error("ERROR", e); + log.error("Error running migration", e); } return generatedMigrations; } diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java index 69b0718..4001fd6 100644 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java @@ -11,6 +11,7 @@ import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; import io.github.mfvanek.pg.index.health.demo.utils.BasePgIndexHealthDemoSpringBootTest; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; @@ -46,6 +47,13 @@ void runsMigrations() { .getResponseBody(); assertThat(result).isNotNull(); + assertThat(result.foreignKeysBefore()).isNotEmpty(); + assertThat(result.foreignKeysAfter()).isEmpty(); + assertThat(result.generatedMigrations()).allMatch(s -> s.contains("create index concurrently if not exists")); } + @AfterEach + void truncateTables() { + jdbcTemplate.execute("truncate table demo.buyer, demo.courier, demo.payment, demo.order_item, demo.orders"); + } } diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationServiceTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationServiceTest.java new file mode 100644 index 0000000..7a6275d --- /dev/null +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationServiceTest.java @@ -0,0 +1,12 @@ +package io.github.mfvanek.pg.index.health.demo.service; + +import io.github.mfvanek.pg.index.health.demo.utils.BasePgIndexHealthDemoSpringBootTest; +import org.junit.jupiter.api.Test; + + +class DbMigrationServiceTest extends BasePgIndexHealthDemoSpringBootTest { + + @Test + void addsIndexesWithFkChecks(){} + +} From 5948fda2484ab0e659d4a4d000e2760021dd5374 Mon Sep 17 00:00:00 2001 From: "m.zharinova" Date: Sat, 2 Nov 2024 09:43:00 +0500 Subject: [PATCH 06/14] correct tests --- .../controller/DbMigrationController.java | 9 ++++++ .../demo/exception/ExceptionHandler.java | 22 ------------- .../demo/exception/MigrationException.java | 16 ---------- .../service/DbMigrationGeneratorService.java | 3 +- .../controller/DbMigrationControllerTest.java | 32 ++++++++++++++++--- .../demo/service/DbMigrationServiceTest.java | 17 ++++++++-- 6 files changed, 53 insertions(+), 46 deletions(-) delete mode 100644 pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/ExceptionHandler.java delete mode 100644 pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java index d7b6faa..85dc18e 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java @@ -10,11 +10,14 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; +import io.github.mfvanek.pg.index.health.demo.dto.MigrationError; import io.github.mfvanek.pg.index.health.demo.service.DbMigrationGeneratorService; import lombok.RequiredArgsConstructor; +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.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; @SuppressFBWarnings("EI_EXPOSE_REP2") @@ -29,4 +32,10 @@ public class DbMigrationController { public ForeignKeyMigrationResponse generateFkMigration(@RequestBody final ForeignKeyMigrationRequest foreignKeyMigrationRequest) { return dbMigrationGeneratorService.addIndexesWithFkChecks(foreignKeyMigrationRequest); } + + @ResponseStatus(HttpStatus.EXPECTATION_FAILED) + @org.springframework.web.bind.annotation.ExceptionHandler(RuntimeException.class) + public MigrationError handleMigrationException(final RuntimeException runtimeException) { + return new MigrationError(HttpStatus.EXPECTATION_FAILED.value(), "Migrations failed - " + runtimeException.getMessage()); + } } diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/ExceptionHandler.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/ExceptionHandler.java deleted file mode 100644 index a09d80f..0000000 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/ExceptionHandler.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2019-2024. Ivan Vakhrushev and others. - * https://github.com/mfvanek/pg-index-health-demo - * - * Licensed under the Apache License 2.0 - */ - -package io.github.mfvanek.pg.index.health.demo.exception; - -import io.github.mfvanek.pg.index.health.demo.dto.MigrationError; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ControllerAdvice; - -@ControllerAdvice -public class ExceptionHandler { - - @org.springframework.web.bind.annotation.ExceptionHandler - public ResponseEntity catchMigrationException(final MigrationException migrationException) { - return new ResponseEntity<>(new MigrationError(HttpStatus.EXPECTATION_FAILED.value(), migrationException.getMessage()), HttpStatus.EXPECTATION_FAILED); - } -} diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java deleted file mode 100644 index c998ae8..0000000 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/exception/MigrationException.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2019-2024. Ivan Vakhrushev and others. - * https://github.com/mfvanek/pg-index-health-demo - * - * Licensed under the Apache License 2.0 - */ - -package io.github.mfvanek.pg.index.health.demo.exception; - -@SuppressWarnings("serial") -public class MigrationException extends IllegalStateException { - - public MigrationException(final String message) { - super(message); - } -} diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java index 28d995e..106cf24 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java @@ -18,7 +18,6 @@ import io.github.mfvanek.pg.generator.GeneratingOptions; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; -import io.github.mfvanek.pg.index.health.demo.exception.MigrationException; import io.github.mfvanek.pg.model.PgContext; import io.github.mfvanek.pg.model.constraint.ForeignKey; import lombok.RequiredArgsConstructor; @@ -47,7 +46,7 @@ public ForeignKeyMigrationResponse addIndexesWithFkChecks(final ForeignKeyMigrat final List migrations = generatedMigrations(keysBefore); final List keysAfter = getFksFromDb(fkMigrationRequest.credentials()); if (!keysAfter.isEmpty()) { - throw new MigrationException("There should be no foreign keys not covered by the index"); + throw new IllegalStateException("There should be no foreign keys not covered by the index"); } return new ForeignKeyMigrationResponse(keysBefore, keysAfter, migrations); } diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java index 4001fd6..65daa98 100644 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java @@ -7,19 +7,22 @@ package io.github.mfvanek.pg.index.health.demo.controller; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.github.mfvanek.pg.connection.ConnectionCredentials; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; +import io.github.mfvanek.pg.index.health.demo.dto.MigrationError; import io.github.mfvanek.pg.index.health.demo.utils.BasePgIndexHealthDemoSpringBootTest; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.web.reactive.function.BodyInserters; import org.testcontainers.containers.JdbcDatabaseContainer; import static org.assertj.core.api.Assertions.assertThat; +@SuppressFBWarnings("SIC_INNER_SHOULD_BE_STATIC_ANON") class DbMigrationControllerTest extends BasePgIndexHealthDemoSpringBootTest { @Autowired @@ -52,8 +55,29 @@ void runsMigrations() { assertThat(result.generatedMigrations()).allMatch(s -> s.contains("create index concurrently if not exists")); } - @AfterEach - void truncateTables() { - jdbcTemplate.execute("truncate table demo.buyer, demo.courier, demo.payment, demo.order_item, demo.orders"); + @Test + void returnsMigrationErrorWithWrongDataInBody() { + final ConnectionCredentials credentials = ConnectionCredentials.ofUrl( + jdbcDatabaseContainer.getJdbcUrl(), + jdbcDatabaseContainer.getUsername(), + "123"); + final ForeignKeyMigrationRequest foreignKeyMigrationRequest = new ForeignKeyMigrationRequest(credentials); + final MigrationError result = webTestClient + .post() + .uri(uriBuilder -> uriBuilder + .pathSegment("db", "migration", "generate") + .build()) + .body(BodyInserters.fromValue(foreignKeyMigrationRequest)) + .accept(MediaType.APPLICATION_JSON) + .headers(this::setUpBasicAuth) + .exchange() + .expectStatus().isEqualTo(HttpStatus.EXPECTATION_FAILED) + .expectBody(MigrationError.class) + .returnResult() + .getResponseBody(); + + assertThat(result).isNotNull(); + assertThat(result).isInstanceOf(MigrationError.class); + assertThat(result.message()).contains("Migrations failed - "); } } diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationServiceTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationServiceTest.java index 7a6275d..5315b07 100644 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationServiceTest.java +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationServiceTest.java @@ -1,12 +1,25 @@ +/* + * Copyright (c) 2019-2024. Ivan Vakhrushev and others. + * https://github.com/mfvanek/pg-index-health-demo + * + * Licensed under the Apache License 2.0 + */ + package io.github.mfvanek.pg.index.health.demo.service; import io.github.mfvanek.pg.index.health.demo.utils.BasePgIndexHealthDemoSpringBootTest; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import static org.assertj.core.api.Assertions.assertThat; class DbMigrationServiceTest extends BasePgIndexHealthDemoSpringBootTest { - @Test - void addsIndexesWithFkChecks(){} + @Autowired + DbMigrationGeneratorService dbMigrationGeneratorService; + @Test + void addsIndexesWithFkChecks() { + assertThat(dbMigrationGeneratorService).isNotNull(); + } } From 3dba0afb24eaa2b2ecade55fa8fbbd50e8201f60 Mon Sep 17 00:00:00 2001 From: "m.zharinova" Date: Sat, 2 Nov 2024 10:18:54 +0500 Subject: [PATCH 07/14] delete dublicated tests --- .../demo/service/DbMigrationServiceTest.java | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationServiceTest.java diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationServiceTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationServiceTest.java deleted file mode 100644 index 5315b07..0000000 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationServiceTest.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2019-2024. Ivan Vakhrushev and others. - * https://github.com/mfvanek/pg-index-health-demo - * - * Licensed under the Apache License 2.0 - */ - -package io.github.mfvanek.pg.index.health.demo.service; - -import io.github.mfvanek.pg.index.health.demo.utils.BasePgIndexHealthDemoSpringBootTest; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import static org.assertj.core.api.Assertions.assertThat; - -class DbMigrationServiceTest extends BasePgIndexHealthDemoSpringBootTest { - - @Autowired - DbMigrationGeneratorService dbMigrationGeneratorService; - - @Test - void addsIndexesWithFkChecks() { - assertThat(dbMigrationGeneratorService).isNotNull(); - } -} From 38c50b697015a77d8095619bcde15e24d4eca4e6 Mon Sep 17 00:00:00 2001 From: "m.zharinova" Date: Tue, 5 Nov 2024 10:34:48 +0500 Subject: [PATCH 08/14] add service tests, change service structure --- .../demo/config/MigrationGeneratorConfig.java | 24 ++++++ .../service/DbMigrationGeneratorService.java | 8 +- .../controller/DbMigrationControllerTest.java | 2 - .../DbMigrationGeneratorServiceTest.java | 73 +++++++++++++++++++ 4 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/config/MigrationGeneratorConfig.java create mode 100644 pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorServiceTest.java diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/config/MigrationGeneratorConfig.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/config/MigrationGeneratorConfig.java new file mode 100644 index 0000000..6386132 --- /dev/null +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/config/MigrationGeneratorConfig.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2019-2024. Ivan Vakhrushev and others. + * https://github.com/mfvanek/pg-index-health-demo + * + * Licensed under the Apache License 2.0 + */ + +package io.github.mfvanek.pg.index.health.demo.config; + +import io.github.mfvanek.pg.generator.DbMigrationGenerator; +import io.github.mfvanek.pg.generator.ForeignKeyMigrationGenerator; +import io.github.mfvanek.pg.generator.GeneratingOptions; +import io.github.mfvanek.pg.model.constraint.ForeignKey; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MigrationGeneratorConfig { + + @Bean + public DbMigrationGenerator dbMigrationGenerator() { + return new ForeignKeyMigrationGenerator(GeneratingOptions.builder().build()); + } +} diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java index 106cf24..df519d8 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java @@ -14,8 +14,6 @@ import io.github.mfvanek.pg.connection.HighAvailabilityPgConnection; import io.github.mfvanek.pg.connection.HighAvailabilityPgConnectionFactory; import io.github.mfvanek.pg.generator.DbMigrationGenerator; -import io.github.mfvanek.pg.generator.ForeignKeyMigrationGenerator; -import io.github.mfvanek.pg.generator.GeneratingOptions; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; import io.github.mfvanek.pg.model.PgContext; @@ -40,6 +38,7 @@ public class DbMigrationGeneratorService { private final HighAvailabilityPgConnectionFactory highAvailabilityPgConnectionFactory; private final DataSource dataSource; + private final DbMigrationGenerator dbMigrationGenerator; public ForeignKeyMigrationResponse addIndexesWithFkChecks(final ForeignKeyMigrationRequest fkMigrationRequest) { final List keysBefore = getFksFromDb(fkMigrationRequest.credentials()); @@ -51,7 +50,7 @@ public ForeignKeyMigrationResponse addIndexesWithFkChecks(final ForeignKeyMigrat return new ForeignKeyMigrationResponse(keysBefore, keysAfter, migrations); } - private List getFksFromDb(final ConnectionCredentials credentials) { + public List getFksFromDb(final ConnectionCredentials credentials) { final HighAvailabilityPgConnection haPgConnection = highAvailabilityPgConnectionFactory.of(credentials); final DatabaseCheckOnCluster foreignKeysNotCoveredWithIndex = new ForeignKeysNotCoveredWithIndexCheckOnCluster(haPgConnection); return foreignKeysNotCoveredWithIndex.check(PgContext.of("demo")); @@ -59,8 +58,7 @@ private List getFksFromDb(final ConnectionCredentials credentials) { @SuppressFBWarnings("SIL_SQL_IN_LOOP") private List generatedMigrations(final List foreignKeys) { - final DbMigrationGenerator generator = new ForeignKeyMigrationGenerator(GeneratingOptions.builder().build()); - final List generatedMigrations = generator.generate(foreignKeys); + final List generatedMigrations = dbMigrationGenerator.generate(foreignKeys); log.info("Generated migrations: {}", generatedMigrations); try (Connection connection = dataSource.getConnection()) { for (final String migration : generatedMigrations) { diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java index 65daa98..65ab92b 100644 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java @@ -7,7 +7,6 @@ package io.github.mfvanek.pg.index.health.demo.controller; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.github.mfvanek.pg.connection.ConnectionCredentials; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; @@ -22,7 +21,6 @@ import static org.assertj.core.api.Assertions.assertThat; -@SuppressFBWarnings("SIC_INNER_SHOULD_BE_STATIC_ANON") class DbMigrationControllerTest extends BasePgIndexHealthDemoSpringBootTest { @Autowired diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorServiceTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorServiceTest.java new file mode 100644 index 0000000..310e1f9 --- /dev/null +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorServiceTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019-2024. Ivan Vakhrushev and others. + * https://github.com/mfvanek/pg-index-health-demo + * + * Licensed under the Apache License 2.0 + */ + +package io.github.mfvanek.pg.index.health.demo.service; + +import io.github.mfvanek.pg.connection.ConnectionCredentials; +import io.github.mfvanek.pg.generator.DbMigrationGenerator; +import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; +import io.github.mfvanek.pg.index.health.demo.utils.BasePgIndexHealthDemoSpringBootTest; +import io.github.mfvanek.pg.model.constraint.ForeignKey; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.testcontainers.containers.JdbcDatabaseContainer; + +import java.util.List; +import javax.annotation.Nonnull; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@ExtendWith(OutputCaptureExtension.class) +class DbMigrationGeneratorServiceTest extends BasePgIndexHealthDemoSpringBootTest { + + @Autowired + DbMigrationGeneratorService dbMigrationGeneratorService; + + @MockBean + DbMigrationGenerator dbMigrationGenerator; + + @Autowired + private JdbcDatabaseContainer jdbcDatabaseContainer; + + @Test + void throwsIllegalStateExceptionWhenEmptyMigrationString(@Nonnull final CapturedOutput output) { + final ConnectionCredentials credentials = ConnectionCredentials.ofUrl( + jdbcDatabaseContainer.getJdbcUrl(), + jdbcDatabaseContainer.getUsername(), + jdbcDatabaseContainer.getPassword()); + final List foreignKeys = dbMigrationGeneratorService.getFksFromDb(credentials); + Mockito.when(dbMigrationGenerator.generate(foreignKeys)).thenReturn(List.of()); + final ForeignKeyMigrationRequest foreignKeyMigrationRequest = new ForeignKeyMigrationRequest(credentials); + + assertThatThrownBy(() -> dbMigrationGeneratorService.addIndexesWithFkChecks(foreignKeyMigrationRequest)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("There should be no foreign keys not covered by the index"); + assertThat(output).contains("Generated migrations: []"); + } + + @Test + void logsAboutSqlExceptionWhenBadMigrationStringAndThrowsExceptionAfter(@Nonnull final CapturedOutput output) { + final ConnectionCredentials credentials = ConnectionCredentials.ofUrl( + jdbcDatabaseContainer.getJdbcUrl(), + jdbcDatabaseContainer.getUsername(), + jdbcDatabaseContainer.getPassword()); + final List foreignKeys = dbMigrationGeneratorService.getFksFromDb(credentials); + Mockito.when(dbMigrationGenerator.generate(foreignKeys)).thenReturn(List.of("select * from payments")); + final ForeignKeyMigrationRequest foreignKeyMigrationRequest = new ForeignKeyMigrationRequest(credentials); + + assertThatThrownBy(() -> dbMigrationGeneratorService.addIndexesWithFkChecks(foreignKeyMigrationRequest)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("There should be no foreign keys not covered by the index"); + assertThat(output).contains("Error running migration"); + } +} From b57a5b5195b1f5b037d51ec0b426085d5da88002 Mon Sep 17 00:00:00 2001 From: "m.zharinova" Date: Thu, 7 Nov 2024 15:00:10 +0500 Subject: [PATCH 09/14] remove sensitive class, change tests, add bean autowiring, change naming, change request --- .../build.gradle.kts | 3 +- .../controller/DbMigrationController.java | 12 +- .../demo/dto/ForeignKeyMigrationRequest.java | 16 -- .../service/DbMigrationGeneratorService.java | 145 +++++++++--------- .../DbMigrationControllerMockTest.java | 49 ++++++ .../controller/DbMigrationControllerTest.java | 29 +--- .../DbMigrationGeneratorServiceTest.java | 24 +-- 7 files changed, 133 insertions(+), 145 deletions(-) delete mode 100644 pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationRequest.java create mode 100644 pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerMockTest.java diff --git a/pg-index-health-spring-boot-demo/build.gradle.kts b/pg-index-health-spring-boot-demo/build.gradle.kts index 58d2d2e..ebaae8b 100644 --- a/pg-index-health-spring-boot-demo/build.gradle.kts +++ b/pg-index-health-spring-boot-demo/build.gradle.kts @@ -10,8 +10,6 @@ plugins { dependencies { implementation(project(":db-migrations")) - implementation("io.github.mfvanek:pg-index-health-generator") - implementation(platform("org.springframework.boot:spring-boot-dependencies:3.3.4")) implementation(platform("org.apache.httpcomponents.client5:httpclient5-parent:5.4")) implementation(platform("org.springframework.boot:spring-boot-dependencies:3.3.5")) implementation(platform("org.apache.httpcomponents.client5:httpclient5-parent:5.4.1")) @@ -30,6 +28,7 @@ dependencies { implementation("org.testcontainers:postgresql") implementation("io.github.mfvanek:pg-index-health") implementation("io.github.mfvanek:pg-index-health-logger") + implementation("io.github.mfvanek:pg-index-health-generator") implementation("com.github.blagerweij:liquibase-sessionlock:1.6.9") annotationProcessor(platform("org.springframework.boot:spring-boot-dependencies:3.3.5")) diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java index 85dc18e..bebfe8c 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java @@ -8,14 +8,12 @@ package io.github.mfvanek.pg.index.health.demo.controller; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; import io.github.mfvanek.pg.index.health.demo.dto.MigrationError; import io.github.mfvanek.pg.index.health.demo.service.DbMigrationGeneratorService; import lombok.RequiredArgsConstructor; 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.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; @@ -29,13 +27,13 @@ public class DbMigrationController { private final DbMigrationGeneratorService dbMigrationGeneratorService; @PostMapping("/generate") - public ForeignKeyMigrationResponse generateFkMigration(@RequestBody final ForeignKeyMigrationRequest foreignKeyMigrationRequest) { - return dbMigrationGeneratorService.addIndexesWithFkChecks(foreignKeyMigrationRequest); + public ForeignKeyMigrationResponse generateMigrationsWithForeignKeysChecked() { + return dbMigrationGeneratorService.generateMigrationsWithForeignKeysChecked(); } @ResponseStatus(HttpStatus.EXPECTATION_FAILED) - @org.springframework.web.bind.annotation.ExceptionHandler(RuntimeException.class) - public MigrationError handleMigrationException(final RuntimeException runtimeException) { - return new MigrationError(HttpStatus.EXPECTATION_FAILED.value(), "Migrations failed - " + runtimeException.getMessage()); + @org.springframework.web.bind.annotation.ExceptionHandler(IllegalStateException.class) + public MigrationError handleMigrationException(final IllegalStateException illegalStateException) { + return new MigrationError(HttpStatus.EXPECTATION_FAILED.value(), "Migrations failed - " + illegalStateException.getMessage()); } } diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationRequest.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationRequest.java deleted file mode 100644 index 55edb5e..0000000 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationRequest.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2019-2024. Ivan Vakhrushev and others. - * https://github.com/mfvanek/pg-index-health-demo - * - * Licensed under the Apache License 2.0 - */ - -package io.github.mfvanek.pg.index.health.demo.dto; - -import io.github.mfvanek.pg.connection.ConnectionCredentials; - -public record ForeignKeyMigrationRequest( - ConnectionCredentials credentials -) { - -} diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java index df519d8..e6b2967 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java @@ -1,74 +1,71 @@ -/* - * Copyright (c) 2019-2024. Ivan Vakhrushev and others. - * https://github.com/mfvanek/pg-index-health-demo - * - * Licensed under the Apache License 2.0 - */ - -package io.github.mfvanek.pg.index.health.demo.service; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import io.github.mfvanek.pg.checks.cluster.ForeignKeysNotCoveredWithIndexCheckOnCluster; -import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnCluster; -import io.github.mfvanek.pg.connection.ConnectionCredentials; -import io.github.mfvanek.pg.connection.HighAvailabilityPgConnection; -import io.github.mfvanek.pg.connection.HighAvailabilityPgConnectionFactory; -import io.github.mfvanek.pg.generator.DbMigrationGenerator; -import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; -import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; -import io.github.mfvanek.pg.model.PgContext; -import io.github.mfvanek.pg.model.constraint.ForeignKey; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.List; -import javax.sql.DataSource; - -@SuppressFBWarnings("EI_EXPOSE_REP2") -@Slf4j -@RequiredArgsConstructor -@Service -@Transactional -public class DbMigrationGeneratorService { - - private final HighAvailabilityPgConnectionFactory highAvailabilityPgConnectionFactory; - private final DataSource dataSource; - private final DbMigrationGenerator dbMigrationGenerator; - - public ForeignKeyMigrationResponse addIndexesWithFkChecks(final ForeignKeyMigrationRequest fkMigrationRequest) { - final List keysBefore = getFksFromDb(fkMigrationRequest.credentials()); - final List migrations = generatedMigrations(keysBefore); - final List keysAfter = getFksFromDb(fkMigrationRequest.credentials()); - if (!keysAfter.isEmpty()) { - throw new IllegalStateException("There should be no foreign keys not covered by the index"); - } - return new ForeignKeyMigrationResponse(keysBefore, keysAfter, migrations); - } - - public List getFksFromDb(final ConnectionCredentials credentials) { - final HighAvailabilityPgConnection haPgConnection = highAvailabilityPgConnectionFactory.of(credentials); - final DatabaseCheckOnCluster foreignKeysNotCoveredWithIndex = new ForeignKeysNotCoveredWithIndexCheckOnCluster(haPgConnection); - return foreignKeysNotCoveredWithIndex.check(PgContext.of("demo")); - } - - @SuppressFBWarnings("SIL_SQL_IN_LOOP") - private List generatedMigrations(final List foreignKeys) { - final List generatedMigrations = dbMigrationGenerator.generate(foreignKeys); - log.info("Generated migrations: {}", generatedMigrations); - try (Connection connection = dataSource.getConnection()) { - for (final String migration : generatedMigrations) { - try (Statement statement = connection.createStatement()) { - statement.execute(migration); - } - } - } catch (SQLException e) { - log.error("Error running migration", e); - } - return generatedMigrations; - } -} +/* + * Copyright (c) 2019-2024. Ivan Vakhrushev and others. + * https://github.com/mfvanek/pg-index-health-demo + * + * Licensed under the Apache License 2.0 + */ + +package io.github.mfvanek.pg.index.health.demo.service; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import io.github.mfvanek.pg.checks.cluster.ForeignKeysNotCoveredWithIndexCheckOnCluster; +import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnCluster; +import io.github.mfvanek.pg.connection.HighAvailabilityPgConnection; +import io.github.mfvanek.pg.generator.DbMigrationGenerator; +import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; +import io.github.mfvanek.pg.model.PgContext; +import io.github.mfvanek.pg.model.constraint.ForeignKey; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; +import javax.sql.DataSource; + +@SuppressFBWarnings("EI_EXPOSE_REP2") +@Slf4j +@RequiredArgsConstructor +@Service +@Transactional +public class DbMigrationGeneratorService { + + private final DataSource dataSource; + private final DbMigrationGenerator dbMigrationGenerator; + private final HighAvailabilityPgConnection haPgConnection; + + @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS") + public ForeignKeyMigrationResponse generateMigrationsWithForeignKeysChecked() { + final List keysBefore = getForeignKeysFromDb(); + final List migrations = generatedMigrations(keysBefore); + final List keysAfter = getForeignKeysFromDb(); + if (!keysAfter.isEmpty()) { + throw new IllegalStateException("There should be no foreign keys not covered by the index"); + } + return new ForeignKeyMigrationResponse(keysBefore, keysAfter, migrations); + } + + public List getForeignKeysFromDb() { + final DatabaseCheckOnCluster foreignKeysNotCoveredWithIndex = new ForeignKeysNotCoveredWithIndexCheckOnCluster(haPgConnection); + return foreignKeysNotCoveredWithIndex.check(PgContext.of("demo")); + } + + @SuppressFBWarnings("SIL_SQL_IN_LOOP") + private List generatedMigrations(final List foreignKeys) { + final List generatedMigrations = dbMigrationGenerator.generate(foreignKeys); + log.info("Generated migrations: {}", generatedMigrations); + try (Connection connection = dataSource.getConnection()) { + for (final String migration : generatedMigrations) { + try (Statement statement = connection.createStatement()) { + statement.execute(migration); + } + } + } catch (SQLException e) { + log.error("Error running migration", e); + } + return generatedMigrations; + } +} diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerMockTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerMockTest.java new file mode 100644 index 0000000..7adfc72 --- /dev/null +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerMockTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019-2024. Ivan Vakhrushev and others. + * https://github.com/mfvanek/pg-index-health-demo + * + * Licensed under the Apache License 2.0 + */ + +package io.github.mfvanek.pg.index.health.demo.controller; + +import io.github.mfvanek.pg.index.health.demo.dto.MigrationError; +import io.github.mfvanek.pg.index.health.demo.service.DbMigrationGeneratorService; +import io.github.mfvanek.pg.index.health.demo.utils.BasePgIndexHealthDemoSpringBootTest; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +import static org.assertj.core.api.Assertions.assertThat; + +class DbMigrationControllerMockTest extends BasePgIndexHealthDemoSpringBootTest { + + @MockBean + DbMigrationGeneratorService dbMigrationGeneratorService; + + @Test + void returnsMigrationErrorWhenKeysAfterAreNotEmpty() { + final IllegalStateException illegalStateException = new IllegalStateException("There should be no foreign keys not covered by the index"); + Mockito.when(dbMigrationGeneratorService.generateMigrationsWithForeignKeysChecked()) + .thenThrow(illegalStateException); + + final MigrationError result = webTestClient + .post() + .uri(uriBuilder -> uriBuilder + .pathSegment("db", "migration", "generate") + .build()) + .accept(MediaType.APPLICATION_JSON) + .headers(this::setUpBasicAuth) + .exchange() + .expectStatus().isEqualTo(HttpStatus.EXPECTATION_FAILED) + .expectBody(MigrationError.class) + .returnResult() + .getResponseBody(); + + assertThat(result).isNotNull(); + assertThat(result).isInstanceOf(MigrationError.class); + assertThat(result.message()).contains("Migrations failed - "); + } +} diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java index 65ab92b..420700d 100644 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java @@ -7,38 +7,24 @@ package io.github.mfvanek.pg.index.health.demo.controller; -import io.github.mfvanek.pg.connection.ConnectionCredentials; -import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; import io.github.mfvanek.pg.index.health.demo.dto.MigrationError; import io.github.mfvanek.pg.index.health.demo.utils.BasePgIndexHealthDemoSpringBootTest; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.web.reactive.function.BodyInserters; -import org.testcontainers.containers.JdbcDatabaseContainer; import static org.assertj.core.api.Assertions.assertThat; class DbMigrationControllerTest extends BasePgIndexHealthDemoSpringBootTest { - @Autowired - private JdbcDatabaseContainer jdbcDatabaseContainer; - @Test void runsMigrations() { - final ConnectionCredentials credentials = ConnectionCredentials.ofUrl( - jdbcDatabaseContainer.getJdbcUrl(), - jdbcDatabaseContainer.getUsername(), - jdbcDatabaseContainer.getPassword()); - final ForeignKeyMigrationRequest foreignKeyMigrationRequest = new ForeignKeyMigrationRequest(credentials); final ForeignKeyMigrationResponse result = webTestClient .post() .uri(uriBuilder -> uriBuilder .pathSegment("db", "migration", "generate") .build()) - .body(BodyInserters.fromValue(foreignKeyMigrationRequest)) .accept(MediaType.APPLICATION_JSON) .headers(this::setUpBasicAuth) .exchange() @@ -54,28 +40,19 @@ void runsMigrations() { } @Test - void returnsMigrationErrorWithWrongDataInBody() { - final ConnectionCredentials credentials = ConnectionCredentials.ofUrl( - jdbcDatabaseContainer.getJdbcUrl(), - jdbcDatabaseContainer.getUsername(), - "123"); - final ForeignKeyMigrationRequest foreignKeyMigrationRequest = new ForeignKeyMigrationRequest(credentials); + void returnsMigrationErrorWithWrongAuthorization() { final MigrationError result = webTestClient .post() .uri(uriBuilder -> uriBuilder .pathSegment("db", "migration", "generate") .build()) - .body(BodyInserters.fromValue(foreignKeyMigrationRequest)) .accept(MediaType.APPLICATION_JSON) - .headers(this::setUpBasicAuth) .exchange() - .expectStatus().isEqualTo(HttpStatus.EXPECTATION_FAILED) + .expectStatus().isEqualTo(HttpStatus.UNAUTHORIZED) .expectBody(MigrationError.class) .returnResult() .getResponseBody(); - assertThat(result).isNotNull(); - assertThat(result).isInstanceOf(MigrationError.class); - assertThat(result.message()).contains("Migrations failed - "); + assertThat(result).isNull(); } } diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorServiceTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorServiceTest.java index 310e1f9..33cd0a9 100644 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorServiceTest.java +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorServiceTest.java @@ -7,9 +7,7 @@ package io.github.mfvanek.pg.index.health.demo.service; -import io.github.mfvanek.pg.connection.ConnectionCredentials; import io.github.mfvanek.pg.generator.DbMigrationGenerator; -import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationRequest; import io.github.mfvanek.pg.index.health.demo.utils.BasePgIndexHealthDemoSpringBootTest; import io.github.mfvanek.pg.model.constraint.ForeignKey; import org.junit.jupiter.api.Test; @@ -19,7 +17,6 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; -import org.testcontainers.containers.JdbcDatabaseContainer; import java.util.List; import javax.annotation.Nonnull; @@ -36,20 +33,12 @@ class DbMigrationGeneratorServiceTest extends BasePgIndexHealthDemoSpringBootTes @MockBean DbMigrationGenerator dbMigrationGenerator; - @Autowired - private JdbcDatabaseContainer jdbcDatabaseContainer; - @Test void throwsIllegalStateExceptionWhenEmptyMigrationString(@Nonnull final CapturedOutput output) { - final ConnectionCredentials credentials = ConnectionCredentials.ofUrl( - jdbcDatabaseContainer.getJdbcUrl(), - jdbcDatabaseContainer.getUsername(), - jdbcDatabaseContainer.getPassword()); - final List foreignKeys = dbMigrationGeneratorService.getFksFromDb(credentials); + final List foreignKeys = dbMigrationGeneratorService.getForeignKeysFromDb(); Mockito.when(dbMigrationGenerator.generate(foreignKeys)).thenReturn(List.of()); - final ForeignKeyMigrationRequest foreignKeyMigrationRequest = new ForeignKeyMigrationRequest(credentials); - assertThatThrownBy(() -> dbMigrationGeneratorService.addIndexesWithFkChecks(foreignKeyMigrationRequest)) + assertThatThrownBy(dbMigrationGeneratorService::generateMigrationsWithForeignKeysChecked) .isInstanceOf(IllegalStateException.class) .hasMessage("There should be no foreign keys not covered by the index"); assertThat(output).contains("Generated migrations: []"); @@ -57,15 +46,10 @@ void throwsIllegalStateExceptionWhenEmptyMigrationString(@Nonnull final Captured @Test void logsAboutSqlExceptionWhenBadMigrationStringAndThrowsExceptionAfter(@Nonnull final CapturedOutput output) { - final ConnectionCredentials credentials = ConnectionCredentials.ofUrl( - jdbcDatabaseContainer.getJdbcUrl(), - jdbcDatabaseContainer.getUsername(), - jdbcDatabaseContainer.getPassword()); - final List foreignKeys = dbMigrationGeneratorService.getFksFromDb(credentials); + final List foreignKeys = dbMigrationGeneratorService.getForeignKeysFromDb(); Mockito.when(dbMigrationGenerator.generate(foreignKeys)).thenReturn(List.of("select * from payments")); - final ForeignKeyMigrationRequest foreignKeyMigrationRequest = new ForeignKeyMigrationRequest(credentials); - assertThatThrownBy(() -> dbMigrationGeneratorService.addIndexesWithFkChecks(foreignKeyMigrationRequest)) + assertThatThrownBy(dbMigrationGeneratorService::generateMigrationsWithForeignKeysChecked) .isInstanceOf(IllegalStateException.class) .hasMessage("There should be no foreign keys not covered by the index"); assertThat(output).contains("Error running migration"); From 6511f76d0f12c31dd1e53eb7fd0f680c5af040fa Mon Sep 17 00:00:00 2001 From: "m.zharinova" Date: Fri, 15 Nov 2024 11:30:23 +0500 Subject: [PATCH 10/14] exclude excessive spot bugs warnings, correct build file, tests --- config/spotbugs/exclude.xml | 6 +++++- pg-index-health-spring-boot-demo/build.gradle.kts | 1 - .../health/demo/controller/DbMigrationController.java | 4 +--- .../index/health/demo/dto/ForeignKeyMigrationResponse.java | 2 -- .../health/demo/service/DbMigrationGeneratorService.java | 4 ---- .../demo/controller/DbMigrationControllerMockTest.java | 7 ++++--- .../health/demo/controller/DbMigrationControllerTest.java | 2 +- 7 files changed, 11 insertions(+), 15 deletions(-) diff --git a/config/spotbugs/exclude.xml b/config/spotbugs/exclude.xml index e6106b0..d6344c4 100644 --- a/config/spotbugs/exclude.xml +++ b/config/spotbugs/exclude.xml @@ -1,7 +1,7 @@ - + @@ -15,6 +15,10 @@ + + + + diff --git a/pg-index-health-spring-boot-demo/build.gradle.kts b/pg-index-health-spring-boot-demo/build.gradle.kts index ebaae8b..7303d6e 100644 --- a/pg-index-health-spring-boot-demo/build.gradle.kts +++ b/pg-index-health-spring-boot-demo/build.gradle.kts @@ -10,7 +10,6 @@ plugins { dependencies { implementation(project(":db-migrations")) - implementation(platform("org.apache.httpcomponents.client5:httpclient5-parent:5.4")) implementation(platform("org.springframework.boot:spring-boot-dependencies:3.3.5")) implementation(platform("org.apache.httpcomponents.client5:httpclient5-parent:5.4.1")) implementation(platform("org.springdoc:springdoc-openapi:2.6.0")) diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java index bebfe8c..4f0f9b7 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationController.java @@ -7,7 +7,6 @@ package io.github.mfvanek.pg.index.health.demo.controller; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; import io.github.mfvanek.pg.index.health.demo.dto.MigrationError; import io.github.mfvanek.pg.index.health.demo.service.DbMigrationGeneratorService; @@ -18,7 +17,6 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -@SuppressFBWarnings("EI_EXPOSE_REP2") @RequiredArgsConstructor @RestController @RequestMapping("/db/migration") @@ -34,6 +32,6 @@ public ForeignKeyMigrationResponse generateMigrationsWithForeignKeysChecked() { @ResponseStatus(HttpStatus.EXPECTATION_FAILED) @org.springframework.web.bind.annotation.ExceptionHandler(IllegalStateException.class) public MigrationError handleMigrationException(final IllegalStateException illegalStateException) { - return new MigrationError(HttpStatus.EXPECTATION_FAILED.value(), "Migrations failed - " + illegalStateException.getMessage()); + return new MigrationError(HttpStatus.EXPECTATION_FAILED.value(), "Migrations failed: " + illegalStateException.getMessage()); } } diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java index fb55ea7..72df566 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/dto/ForeignKeyMigrationResponse.java @@ -7,12 +7,10 @@ package io.github.mfvanek.pg.index.health.demo.dto; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.github.mfvanek.pg.model.constraint.ForeignKey; import java.util.List; -@SuppressFBWarnings("EI_EXPOSE_REP2") public record ForeignKeyMigrationResponse( List foreignKeysBefore, List foreignKeysAfter, diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java index e6b2967..878790b 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java @@ -7,7 +7,6 @@ package io.github.mfvanek.pg.index.health.demo.service; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.github.mfvanek.pg.checks.cluster.ForeignKeysNotCoveredWithIndexCheckOnCluster; import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnCluster; import io.github.mfvanek.pg.connection.HighAvailabilityPgConnection; @@ -26,7 +25,6 @@ import java.util.List; import javax.sql.DataSource; -@SuppressFBWarnings("EI_EXPOSE_REP2") @Slf4j @RequiredArgsConstructor @Service @@ -37,7 +35,6 @@ public class DbMigrationGeneratorService { private final DbMigrationGenerator dbMigrationGenerator; private final HighAvailabilityPgConnection haPgConnection; - @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS") public ForeignKeyMigrationResponse generateMigrationsWithForeignKeysChecked() { final List keysBefore = getForeignKeysFromDb(); final List migrations = generatedMigrations(keysBefore); @@ -53,7 +50,6 @@ public List getForeignKeysFromDb() { return foreignKeysNotCoveredWithIndex.check(PgContext.of("demo")); } - @SuppressFBWarnings("SIL_SQL_IN_LOOP") private List generatedMigrations(final List foreignKeys) { final List generatedMigrations = dbMigrationGenerator.generate(foreignKeys); log.info("Generated migrations: {}", generatedMigrations); diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerMockTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerMockTest.java index 7adfc72..8138e6c 100644 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerMockTest.java +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerMockTest.java @@ -42,8 +42,9 @@ void returnsMigrationErrorWhenKeysAfterAreNotEmpty() { .returnResult() .getResponseBody(); - assertThat(result).isNotNull(); - assertThat(result).isInstanceOf(MigrationError.class); - assertThat(result.message()).contains("Migrations failed - "); + assertThat(result) + .isNotNull() + .isInstanceOf(MigrationError.class); + assertThat(result.message()).contains("Migrations failed: There should be no foreign keys not covered by the index"); } } diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java index 420700d..ed5ab51 100644 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java @@ -40,7 +40,7 @@ void runsMigrations() { } @Test - void returnsMigrationErrorWithWrongAuthorization() { + void returnsNothingWithWrongAuthorization() { final MigrationError result = webTestClient .post() .uri(uriBuilder -> uriBuilder From 4ea486747ecd475a611bebb63bd6321b3d6e263a Mon Sep 17 00:00:00 2001 From: "m.zharinova" Date: Wed, 20 Nov 2024 10:53:14 +0500 Subject: [PATCH 11/14] apply remarks except for mock test --- .../demo/config/MigrationGeneratorConfig.java | 10 ++++++++- .../service/DbMigrationGeneratorService.java | 20 ++++++++++-------- .../controller/DbMigrationControllerTest.java | 21 +++++++++++++++++++ 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/config/MigrationGeneratorConfig.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/config/MigrationGeneratorConfig.java index 6386132..c90373e 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/config/MigrationGeneratorConfig.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/config/MigrationGeneratorConfig.java @@ -7,6 +7,9 @@ package io.github.mfvanek.pg.index.health.demo.config; +import io.github.mfvanek.pg.checks.cluster.ForeignKeysNotCoveredWithIndexCheckOnCluster; +import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnCluster; +import io.github.mfvanek.pg.connection.HighAvailabilityPgConnection; import io.github.mfvanek.pg.generator.DbMigrationGenerator; import io.github.mfvanek.pg.generator.ForeignKeyMigrationGenerator; import io.github.mfvanek.pg.generator.GeneratingOptions; @@ -14,11 +17,16 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -@Configuration +@Configuration(proxyBeanMethods = false) public class MigrationGeneratorConfig { @Bean public DbMigrationGenerator dbMigrationGenerator() { return new ForeignKeyMigrationGenerator(GeneratingOptions.builder().build()); } + + @Bean + public DatabaseCheckOnCluster foreignKeysNotCoveredWithIndex(final HighAvailabilityPgConnection haPgConnection) { + return new ForeignKeysNotCoveredWithIndexCheckOnCluster(haPgConnection); + } } diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java index 878790b..09ee873 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java @@ -7,9 +7,7 @@ package io.github.mfvanek.pg.index.health.demo.service; -import io.github.mfvanek.pg.checks.cluster.ForeignKeysNotCoveredWithIndexCheckOnCluster; import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnCluster; -import io.github.mfvanek.pg.connection.HighAvailabilityPgConnection; import io.github.mfvanek.pg.generator.DbMigrationGenerator; import io.github.mfvanek.pg.index.health.demo.dto.ForeignKeyMigrationResponse; import io.github.mfvanek.pg.model.PgContext; @@ -28,16 +26,17 @@ @Slf4j @RequiredArgsConstructor @Service -@Transactional +@Transactional(readOnly = true) public class DbMigrationGeneratorService { private final DataSource dataSource; private final DbMigrationGenerator dbMigrationGenerator; - private final HighAvailabilityPgConnection haPgConnection; + private final DatabaseCheckOnCluster foreignKeysNotCoveredWithIndex; public ForeignKeyMigrationResponse generateMigrationsWithForeignKeysChecked() { final List keysBefore = getForeignKeysFromDb(); - final List migrations = generatedMigrations(keysBefore); + final List migrations = generateMigrations(keysBefore); + runGeneratedMigrations(migrations); final List keysAfter = getForeignKeysFromDb(); if (!keysAfter.isEmpty()) { throw new IllegalStateException("There should be no foreign keys not covered by the index"); @@ -45,14 +44,17 @@ public ForeignKeyMigrationResponse generateMigrationsWithForeignKeysChecked() { return new ForeignKeyMigrationResponse(keysBefore, keysAfter, migrations); } - public List getForeignKeysFromDb() { - final DatabaseCheckOnCluster foreignKeysNotCoveredWithIndex = new ForeignKeysNotCoveredWithIndexCheckOnCluster(haPgConnection); + List getForeignKeysFromDb() { return foreignKeysNotCoveredWithIndex.check(PgContext.of("demo")); } - private List generatedMigrations(final List foreignKeys) { + private List generateMigrations(final List foreignKeys) { final List generatedMigrations = dbMigrationGenerator.generate(foreignKeys); log.info("Generated migrations: {}", generatedMigrations); + return generatedMigrations; + } + + private void runGeneratedMigrations(final List generatedMigrations) { try (Connection connection = dataSource.getConnection()) { for (final String migration : generatedMigrations) { try (Statement statement = connection.createStatement()) { @@ -62,6 +64,6 @@ private List generatedMigrations(final List foreignKeys) { } catch (SQLException e) { log.error("Error running migration", e); } - return generatedMigrations; + } } diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java index ed5ab51..cfdfa3a 100644 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java @@ -55,4 +55,25 @@ void returnsNothingWithWrongAuthorization() { assertThat(result).isNull(); } + + @Test + void returnsMigrationErrorWhenKeysAfterAreNotEmpty() { + final MigrationError result = webTestClient + .post() + .uri(uriBuilder -> uriBuilder + .pathSegment("db", "migration", "generate") + .build()) + .accept(MediaType.APPLICATION_JSON) + .headers(this::setUpBasicAuth) + .exchange() + .expectStatus().isEqualTo(HttpStatus.EXPECTATION_FAILED) + .expectBody(MigrationError.class) + .returnResult() + .getResponseBody(); + + assertThat(result) + .isNotNull() + .isInstanceOf(MigrationError.class); + assertThat(result.message()).contains("Migrations failed: There should be no foreign keys not covered by the index"); + } } From 61c47e6dad4456f3d164a3eef4331609099163fc Mon Sep 17 00:00:00 2001 From: "m.zharinova" Date: Wed, 20 Nov 2024 15:40:28 +0500 Subject: [PATCH 12/14] delete method in test --- .../controller/DbMigrationControllerTest.java | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java index cfdfa3a..ed5ab51 100644 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java @@ -55,25 +55,4 @@ void returnsNothingWithWrongAuthorization() { assertThat(result).isNull(); } - - @Test - void returnsMigrationErrorWhenKeysAfterAreNotEmpty() { - final MigrationError result = webTestClient - .post() - .uri(uriBuilder -> uriBuilder - .pathSegment("db", "migration", "generate") - .build()) - .accept(MediaType.APPLICATION_JSON) - .headers(this::setUpBasicAuth) - .exchange() - .expectStatus().isEqualTo(HttpStatus.EXPECTATION_FAILED) - .expectBody(MigrationError.class) - .returnResult() - .getResponseBody(); - - assertThat(result) - .isNotNull() - .isInstanceOf(MigrationError.class); - assertThat(result.message()).contains("Migrations failed: There should be no foreign keys not covered by the index"); - } } From 65d13856ad9abfb188239aa3dda9db5349c794d7 Mon Sep 17 00:00:00 2001 From: "m.zharinova" Date: Wed, 20 Nov 2024 16:05:37 +0500 Subject: [PATCH 13/14] change because of new dependencies --- .../health/demo/controller/DbMigrationControllerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java index ed5ab51..d84ee3f 100644 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java @@ -34,8 +34,8 @@ void runsMigrations() { .getResponseBody(); assertThat(result).isNotNull(); - assertThat(result.foreignKeysBefore()).isNotEmpty(); - assertThat(result.foreignKeysAfter()).isEmpty(); + assertThat(result.foreignKeysBefore().isEmpty()).isFalse(); + assertThat(result.foreignKeysAfter().isEmpty()).isTrue(); assertThat(result.generatedMigrations()).allMatch(s -> s.contains("create index concurrently if not exists")); } From b8dff99d4f9ee85f413ed88e173d7c8ab75de4c4 Mon Sep 17 00:00:00 2001 From: Ivan Vakhrushev Date: Wed, 20 Nov 2024 22:13:38 +0400 Subject: [PATCH 14/14] A bit of refactoring --- .../health/demo/config/DatabaseStructureHealthConfig.java | 6 ++++++ .../pg/index/health/demo/controller/DbHealthController.java | 3 ++- .../health/demo/service/DbMigrationGeneratorService.java | 4 ++-- .../pg/index/health/demo/IndexesMaintenanceTest.java | 5 +++-- .../health/demo/controller/DbMigrationControllerTest.java | 4 ++-- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/config/DatabaseStructureHealthConfig.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/config/DatabaseStructureHealthConfig.java index cd4cd22..32a318a 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/config/DatabaseStructureHealthConfig.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/config/DatabaseStructureHealthConfig.java @@ -18,6 +18,7 @@ import io.github.mfvanek.pg.connection.HighAvailabilityPgConnectionFactoryImpl; import io.github.mfvanek.pg.connection.PgConnectionFactoryImpl; import io.github.mfvanek.pg.connection.PrimaryHostDeterminerImpl; +import io.github.mfvanek.pg.model.PgContext; import io.github.mfvanek.pg.settings.maintenance.ConfigurationMaintenanceOnHostImpl; import io.github.mfvanek.pg.statistics.maintenance.StatisticsMaintenanceOnHostImpl; import org.springframework.context.annotation.Bean; @@ -59,4 +60,9 @@ public DatabaseManagement databaseManagement(@Nonnull final HighAvailabilityPgCo StatisticsMaintenanceOnHostImpl::new, ConfigurationMaintenanceOnHostImpl::new); } + + @Bean + public PgContext pgContext() { + return PgContext.of("demo"); + } } diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbHealthController.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbHealthController.java index 776f2de..49b7bfa 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbHealthController.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/controller/DbHealthController.java @@ -25,6 +25,7 @@ public class DbHealthController { private final HealthLogger healthLogger; + private final PgContext pgContext; @GetMapping public ResponseEntity> collectHealthData() { @@ -32,6 +33,6 @@ public ResponseEntity> collectHealthData() { .withIndexSizeThreshold(1, MemoryUnit.MB) .withTableSizeThreshold(1, MemoryUnit.MB) .build(); - return ResponseEntity.ok(healthLogger.logAll(exclusions, PgContext.of("demo"))); + return ResponseEntity.ok(healthLogger.logAll(exclusions, pgContext)); } } diff --git a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java index 09ee873..4918077 100644 --- a/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java +++ b/pg-index-health-spring-boot-demo/src/main/java/io/github/mfvanek/pg/index/health/demo/service/DbMigrationGeneratorService.java @@ -32,6 +32,7 @@ public class DbMigrationGeneratorService { private final DataSource dataSource; private final DbMigrationGenerator dbMigrationGenerator; private final DatabaseCheckOnCluster foreignKeysNotCoveredWithIndex; + private final PgContext pgContext; public ForeignKeyMigrationResponse generateMigrationsWithForeignKeysChecked() { final List keysBefore = getForeignKeysFromDb(); @@ -45,7 +46,7 @@ public ForeignKeyMigrationResponse generateMigrationsWithForeignKeysChecked() { } List getForeignKeysFromDb() { - return foreignKeysNotCoveredWithIndex.check(PgContext.of("demo")); + return foreignKeysNotCoveredWithIndex.check(pgContext); } private List generateMigrations(final List foreignKeys) { @@ -64,6 +65,5 @@ private void runGeneratedMigrations(final List generatedMigrations) { } catch (SQLException e) { log.error("Error running migration", e); } - } } diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/IndexesMaintenanceTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/IndexesMaintenanceTest.java index 8e0f20d..63a7ab8 100644 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/IndexesMaintenanceTest.java +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/IndexesMaintenanceTest.java @@ -45,7 +45,8 @@ class IndexesMaintenanceTest extends BasePgIndexHealthDemoSpringBootTest { private static final String ORDERS_TABLE = "demo.orders"; private static final String ORDER_ID_COLUMN = "order_id"; - private final PgContext demoSchema = PgContext.of("demo"); + @Autowired + private PgContext pgContext; @Autowired private List> checks; @@ -79,7 +80,7 @@ void databaseStructureCheckForDemoSchema() { // Skip all runtime checks except SEQUENCE_OVERFLOW .filter(check -> check.getDiagnostic() == Diagnostic.SEQUENCE_OVERFLOW || check.isStatic()) .forEach(check -> { - final ListAssert checksAssert = assertThat(check.check(demoSchema)) + final ListAssert checksAssert = assertThat(check.check(pgContext)) .as(check.getDiagnostic().name()); switch (check.getDiagnostic()) { diff --git a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java index d84ee3f..ed5ab51 100644 --- a/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java +++ b/pg-index-health-spring-boot-demo/src/test/java/io/github/mfvanek/pg/index/health/demo/controller/DbMigrationControllerTest.java @@ -34,8 +34,8 @@ void runsMigrations() { .getResponseBody(); assertThat(result).isNotNull(); - assertThat(result.foreignKeysBefore().isEmpty()).isFalse(); - assertThat(result.foreignKeysAfter().isEmpty()).isTrue(); + assertThat(result.foreignKeysBefore()).isNotEmpty(); + assertThat(result.foreignKeysAfter()).isEmpty(); assertThat(result.generatedMigrations()).allMatch(s -> s.contains("create index concurrently if not exists")); }