diff options
| author | Paul-Christian Volkmer | 2026-11-13 10:44:18 +0100 |
|---|---|---|
| committer | Paul-Christian Volkmer | 2026-11-13 10:44:18 +0100 |
| commit | 6f5d91fbab966d627e28f8269dae97f1f88ccfbe (patch) | |
| tree | 33f870d60e3e962117dc3588e2a40a1626b8ea75 | |
| parent | 86b4026bcfaed73eda1a435f76c23e0c23e95307 (diff) | |
feat: check required form revision rangedatabase-migration
5 files changed, 191 insertions, 2 deletions
diff --git a/src/main/java/dev/dnpm/oshelper/migration/DatabaseMigrationService.java b/src/main/java/dev/dnpm/oshelper/migration/DatabaseMigrationService.java index e052286..985ccf4 100644 --- a/src/main/java/dev/dnpm/oshelper/migration/DatabaseMigrationService.java +++ b/src/main/java/dev/dnpm/oshelper/migration/DatabaseMigrationService.java @@ -19,7 +19,10 @@ package dev.dnpm.oshelper.migration; +import java.io.IOException; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,6 +56,11 @@ public class DatabaseMigrationService { log.info("Starting database migration"); try { for (var migration : this.getNewMigrationIds()) { + if (!requiredFormRevisionAvailable(migration)) { + log.info("Skipping database migration due to pre-condition: '{}'", migration); + continue; + } + log.info("Apply database migration: '{}'", migration); try { jdbcTemplate.execute(MigrationUtils.getSqlQuery(resourcePatternResolver, migration)); @@ -74,6 +82,39 @@ public class DatabaseMigrationService { } } + private boolean requiredFormRevisionAvailable(String migration) throws IOException { + final var sqlQuery = MigrationUtils.getSqlQuery(resourcePatternResolver, migration); + + String formName = null; + Pattern formNamePattern = Pattern.compile("(?m)^-- form: (.+)$"); + Matcher formNameMatcher = formNamePattern.matcher(sqlQuery); + if (formNameMatcher.find()) { + formName = formNameMatcher.group(1); + } + + String minRev = "0"; + Pattern minRevPattern = Pattern.compile("(?m)^-- min-rev: (\\d+)$"); + Matcher minRevMatcher = minRevPattern.matcher(sqlQuery); + if (minRevMatcher.find()) { + minRev = minRevMatcher.group(1); + } + + String maxRev = "99999999999999"; + Pattern maxRevPattern = Pattern.compile("(?m)^-- max-rev: (\\d+)$"); + Matcher maxRevMatcher = maxRevPattern.matcher(sqlQuery); + if (maxRevMatcher.find()) { + maxRev = maxRevMatcher.group(1); + } + + if (formName == null) { + // No spezial form revision required + return true; + } + + return MigrationUtils.requiredFormRevisionAvailable( + this.jdbcTemplate, formName, minRev, maxRev); + } + List<String> getNewMigrationIds() throws Exception { var existingMigrations = MigrationUtils.getExecutedDatabaseMigrations(this.jdbcTemplate); return MigrationUtils.getDatabaseMigrationScripts(this.resourcePatternResolver).stream() diff --git a/src/main/java/dev/dnpm/oshelper/migration/MigrationUtils.java b/src/main/java/dev/dnpm/oshelper/migration/MigrationUtils.java index bde2612..fd17f22 100644 --- a/src/main/java/dev/dnpm/oshelper/migration/MigrationUtils.java +++ b/src/main/java/dev/dnpm/oshelper/migration/MigrationUtils.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.stream.Collectors; import org.springframework.core.io.Resource; import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; /** @@ -39,6 +40,19 @@ class MigrationUtils { (rs, rowNum) -> rs.getString("update_script")); } + static boolean requiredFormRevisionAvailable( + JdbcTemplate jdbcTemplate, String formName, String minRev, String maxRev) { + try { + return 1 + == jdbcTemplate.queryForObject( + "SELECT COUNT(*) FROM data_form WHERE name = ? AND revision >= ? AND revision <= ?", + new Object[] {formName, minRev, maxRev}, + Integer.class); + } catch (EmptyResultDataAccessException e) { + return false; + } + } + static List<String> getDatabaseMigrationScripts(ResourcePatternResolver resourcePatternResolver) throws IOException { var resources = diff --git a/src/main/resources/dev/dnpm/oshelper/migration/README.md b/src/main/resources/dev/dnpm/oshelper/migration/README.md new file mode 100644 index 0000000..14a530b --- /dev/null +++ b/src/main/resources/dev/dnpm/oshelper/migration/README.md @@ -0,0 +1,18 @@ +# Format der Migrationsdateien + +Die Migrationsdateien sollen fortlaufend aufsteigend nummeriert sein, mit `.sql` enden. + +Zudem sollen sie immer Angaben zum betreffenden Formular und Revision enthalten: + +```sql +-- form: DNPM Beispiel +-- min-rev: 1 +-- max-rev: 2 + +ALTER TABLE dk_dnpm_beispiel + MODIFY COLUMN langer_text text; +``` + +Ist keine Angabe zum Formularnamen angegeben, wird die Migration immer ausgeführt, +anderenfalls nur für eine entsprechende Revision des angegebenen Formulars. +Wenn nicht angegeben, ist die minimal erforderliche Revision ist standardmäßig 0, die maximale 99999999999999.
\ No newline at end of file diff --git a/src/main/resources/dev/dnpm/oshelper/migration/updatednpm_001.sql b/src/main/resources/dev/dnpm/oshelper/migration/updatednpm_001.sql index 44c6edc..6c6d80b 100644 --- a/src/main/resources/dev/dnpm/oshelper/migration/updatednpm_001.sql +++ b/src/main/resources/dev/dnpm/oshelper/migration/updatednpm_001.sql @@ -1 +1,5 @@ +-- form: DNPM UF Einzelempfehlung +-- min-rev: 273 +-- max-rev: 273 + ALTER TABLE dk_dnpm_uf_einzelempfehlung MODIFY COLUMN studie VARCHAR(255);
\ No newline at end of file diff --git a/src/test/java/dev/dnpm/oshelper/migration/DatabaseMigrationServiceTest.java b/src/test/java/dev/dnpm/oshelper/migration/DatabaseMigrationServiceTest.java index 9c22c8d..2085921 100644 --- a/src/test/java/dev/dnpm/oshelper/migration/DatabaseMigrationServiceTest.java +++ b/src/test/java/dev/dnpm/oshelper/migration/DatabaseMigrationServiceTest.java @@ -21,13 +21,14 @@ package dev.dnpm.oshelper.migration; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.*; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockedStatic; @@ -110,4 +111,115 @@ class DatabaseMigrationServiceTest { assertThat(argumentCaptor.getValue()).isEqualTo("UPDATE TABLE test;"); } } + + @ParameterizedTest + @ValueSource(booleans = {false, true}) + void shouldIgnoreMigrationFileDueToMissingFormRev(boolean formRevisionAvailable) { + try (MockedStatic<MigrationUtils> databaseUtils = Mockito.mockStatic(MigrationUtils.class)) { + databaseUtils + .when( + () -> MigrationUtils.getDatabaseMigrationScripts(any(ResourcePatternResolver.class))) + .thenReturn(List.of("updatednpm_001.sql", "updatednpm_002.sql")); + + databaseUtils + .when(() -> MigrationUtils.getExecutedDatabaseMigrations(any(JdbcTemplate.class))) + .thenReturn(List.of("updatednpm_001.sql")); + + databaseUtils + .when( + () -> + MigrationUtils.requiredFormRevisionAvailable( + any(JdbcTemplate.class), anyString(), anyString(), anyString())) + .thenReturn(formRevisionAvailable); + + databaseUtils + .when(() -> MigrationUtils.getSqlQuery(any(ResourcePatternResolver.class), anyString())) + .thenReturn("-- form: DNPM Test\n-- min-rev: 1\n-- max-rev: 2\nUPDATE TABLE test;"); + + databaseMigration.migrate(); + + if (formRevisionAvailable) { + verify(jdbcTemplate, times(1)).execute(anyString()); + } else { + verify(jdbcTemplate, times(0)).execute(anyString()); + } + } + } + + @Test + void shouldNotCheckRequiredRevsionRange() { + try (MockedStatic<MigrationUtils> databaseUtils = Mockito.mockStatic(MigrationUtils.class)) { + databaseUtils + .when( + () -> MigrationUtils.getDatabaseMigrationScripts(any(ResourcePatternResolver.class))) + .thenReturn(List.of("updatednpm_001.sql", "updatednpm_002.sql")); + + databaseUtils + .when(() -> MigrationUtils.getExecutedDatabaseMigrations(any(JdbcTemplate.class))) + .thenReturn(List.of("updatednpm_001.sql")); + + databaseUtils + .when( + () -> + MigrationUtils.requiredFormRevisionAvailable( + any(JdbcTemplate.class), anyString(), anyString(), anyString())) + .thenReturn(false); + + databaseUtils + .when(() -> MigrationUtils.getSqlQuery(any(ResourcePatternResolver.class), anyString())) + .thenReturn("UPDATE TABLE test;"); + + databaseMigration.migrate(); + + databaseUtils.verify( + () -> { + MigrationUtils.requiredFormRevisionAvailable( + any(JdbcTemplate.class), anyString(), anyString(), anyString()); + }, + never()); + } + } + + @Test + void shouldUseDefaultRequiredRevsionRange() { + try (MockedStatic<MigrationUtils> databaseUtils = Mockito.mockStatic(MigrationUtils.class)) { + databaseUtils + .when( + () -> MigrationUtils.getDatabaseMigrationScripts(any(ResourcePatternResolver.class))) + .thenReturn(List.of("updatednpm_001.sql", "updatednpm_002.sql")); + + databaseUtils + .when(() -> MigrationUtils.getExecutedDatabaseMigrations(any(JdbcTemplate.class))) + .thenReturn(List.of("updatednpm_001.sql")); + + databaseUtils + .when( + () -> + MigrationUtils.requiredFormRevisionAvailable( + any(JdbcTemplate.class), anyString(), anyString(), anyString())) + .thenReturn(false); + + databaseUtils + .when(() -> MigrationUtils.getSqlQuery(any(ResourcePatternResolver.class), anyString())) + .thenReturn("-- form: DNPM Test\nUPDATE TABLE test;"); + + databaseMigration.migrate(); + + var formNameCaptor = ArgumentCaptor.forClass(String.class); + var minRevCaptor = ArgumentCaptor.forClass(String.class); + var maxRevCaptor = ArgumentCaptor.forClass(String.class); + databaseUtils.verify( + () -> { + MigrationUtils.requiredFormRevisionAvailable( + any(JdbcTemplate.class), + formNameCaptor.capture(), + minRevCaptor.capture(), + maxRevCaptor.capture()); + }, + times(1)); + assertThat(formNameCaptor.getValue()).isEqualTo("DNPM Test"); + assertThat(minRevCaptor.getValue()).isEqualTo("0"); + assertThat(maxRevCaptor.getValue()).isEqualTo("99999999999999"); + } + } } |
