summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul-Christian Volkmer2026-11-13 10:44:18 +0100
committerPaul-Christian Volkmer2026-11-13 10:44:18 +0100
commit6f5d91fbab966d627e28f8269dae97f1f88ccfbe (patch)
tree33f870d60e3e962117dc3588e2a40a1626b8ea75
parent86b4026bcfaed73eda1a435f76c23e0c23e95307 (diff)
feat: check required form revision rangedatabase-migration
-rw-r--r--src/main/java/dev/dnpm/oshelper/migration/DatabaseMigrationService.java41
-rw-r--r--src/main/java/dev/dnpm/oshelper/migration/MigrationUtils.java14
-rw-r--r--src/main/resources/dev/dnpm/oshelper/migration/README.md18
-rw-r--r--src/main/resources/dev/dnpm/oshelper/migration/updatednpm_001.sql4
-rw-r--r--src/test/java/dev/dnpm/oshelper/migration/DatabaseMigrationServiceTest.java116
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");
+ }
+ }
}