diff --git a/pom.xml b/pom.xml index e8dbfcfe93f84bc818e739441f93e92cc5ee60d8..0dc310ccb39de6142440d20422576851de0ad52b 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,7 @@ <spring-cloud-config-server.version>4.1.0</spring-cloud-config-server.version> <testcontainers-keycloak.version>3.2.0</testcontainers-keycloak.version> <keycloak-admin-client.version>23.0.6</keycloak-admin-client.version> + <mongock.version>5.4.0</mongock.version> </properties> <dependencies> @@ -67,6 +68,17 @@ <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> + <!-- mongock --> + <dependency> + <groupId>io.mongock</groupId> + <artifactId>mongock-springboot-v3</artifactId> + <version>${mongock.version}</version> + </dependency> + <dependency> + <groupId>io.mongock</groupId> + <artifactId>mongodb-springdata-v4-driver</artifactId> + <version>${mongock.version}</version> + </dependency> <!-- Dev --> <dependency> diff --git a/src/main/java/de/ozgcloud/admin/AdministrationApplication.java b/src/main/java/de/ozgcloud/admin/AdministrationApplication.java index c9535837fdbb37dcb5a4344bff54816679d33223..563eea5b36d486c99187d35a08c61e6fb2e34bf1 100644 --- a/src/main/java/de/ozgcloud/admin/AdministrationApplication.java +++ b/src/main/java/de/ozgcloud/admin/AdministrationApplication.java @@ -21,6 +21,7 @@ */ package de.ozgcloud.admin; +import io.mongock.runner.springboot.EnableMongock; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @@ -28,6 +29,7 @@ import org.springframework.data.mongodb.repository.config.EnableMongoRepositorie @SpringBootApplication @EnableConfigServer +@EnableMongock @EnableMongoRepositories public class AdministrationApplication { diff --git a/src/main/java/de/ozgcloud/admin/migration/M001_CreateEmptyPostfachIfMissing.java b/src/main/java/de/ozgcloud/admin/migration/M001_CreateEmptyPostfachIfMissing.java new file mode 100644 index 0000000000000000000000000000000000000000..b871728d899c5b7eb209501167555d9325d17e49 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/migration/M001_CreateEmptyPostfachIfMissing.java @@ -0,0 +1,38 @@ +package de.ozgcloud.admin.migration; + +import io.mongock.api.annotations.ChangeUnit; +import io.mongock.api.annotations.Execution; +import io.mongock.api.annotations.RollbackExecution; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; + +@ChangeUnit(id = "2024-02-20 17:00:00 OZG-4948-OZG-5058", order = "M001", author = "lmonnerjahn", runAlways = true) +public class M001_CreateEmptyPostfachIfMissing { // NOSONAR + static final String SETTINGS_COLLECTION = "settings"; + + static final String TYPE_NAME_KEY = "name"; + static final String TYPE_NAME_POSTFACH_VALUE = "Postfach"; + + @Execution + public void doMigration(MongoTemplate template) { + template.upsert(buildQuery(), buildUpdate(), SETTINGS_COLLECTION); + } + + private Query buildQuery() { + var criteria = Criteria.where(TYPE_NAME_KEY).is(TYPE_NAME_POSTFACH_VALUE); + return Query.query(criteria); + } + + private Update buildUpdate() { + var update = new Update(); + update.setOnInsert(TYPE_NAME_KEY, TYPE_NAME_POSTFACH_VALUE); + return update; + } + + @RollbackExecution + public void rollback() { + // kein rollback implementiert + } +} diff --git a/src/main/java/de/ozgcloud/admin/migration/MongockFailedEventListener.java b/src/main/java/de/ozgcloud/admin/migration/MongockFailedEventListener.java new file mode 100644 index 0000000000000000000000000000000000000000..84e860cb2cbdee0312df576f903dd285e89e1bd9 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/migration/MongockFailedEventListener.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.migration; + +import io.mongock.runner.spring.base.events.SpringMigrationFailureEvent; +import lombok.extern.log4j.Log4j2; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +@Log4j2 +@Component +public class MongockFailedEventListener implements ApplicationListener<SpringMigrationFailureEvent> { + + @Override + public void onApplicationEvent(SpringMigrationFailureEvent event) { + log.error("Mongock migration failed", event.getMigrationResult()); + } +} \ No newline at end of file diff --git a/src/main/java/de/ozgcloud/admin/migration/MongockStartEventListener.java b/src/main/java/de/ozgcloud/admin/migration/MongockStartEventListener.java new file mode 100644 index 0000000000000000000000000000000000000000..ad1558351ae353658dbe653bfe2630e627373489 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/migration/MongockStartEventListener.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.migration; + +import io.mongock.runner.spring.base.events.SpringMigrationStartedEvent; +import lombok.extern.log4j.Log4j2; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +@Log4j2 +@Component +public class MongockStartEventListener implements ApplicationListener<SpringMigrationStartedEvent> { + + @Override + public void onApplicationEvent(SpringMigrationStartedEvent event) { + log.info("Mongock start migration..."); + } +} \ No newline at end of file diff --git a/src/main/java/de/ozgcloud/admin/migration/MongockSuccessEventListener.java b/src/main/java/de/ozgcloud/admin/migration/MongockSuccessEventListener.java new file mode 100644 index 0000000000000000000000000000000000000000..95a7f76ee988dc8f1e72b8db6341da77514e5b54 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/migration/MongockSuccessEventListener.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.migration; + +import io.mongock.runner.spring.base.events.SpringMigrationSuccessEvent; +import lombok.extern.log4j.Log4j2; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +@Log4j2 +@Component +public class MongockSuccessEventListener implements ApplicationListener<SpringMigrationSuccessEvent> { + + @Override + public void onApplicationEvent(SpringMigrationSuccessEvent event) { + log.info("Mongock migration successfull", event.getMigrationResult()); + } +} \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 29082f7d8446f187cd3f16f077b94f28d41bf807..315e37b9756df692953d1c5761747bfc1e97f4ac 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -49,6 +49,12 @@ management: exposure: include: health,prometheus +mongock: + runner-type: initializingbean + migration-scan-package: + - de.ozgcloud.admin.migration + enabled: true + spring: application: name: OzgCloud_Administration diff --git a/src/test/java/de/ozgcloud/admin/AdministrationApplicationTest.java b/src/test/java/de/ozgcloud/admin/AdministrationApplicationTest.java index 7d88d4237191cb0b07aaad9235443c689c1efe59..7b7fb5056c8fd67bc11be9a1cc016610e1181146 100644 --- a/src/test/java/de/ozgcloud/admin/AdministrationApplicationTest.java +++ b/src/test/java/de/ozgcloud/admin/AdministrationApplicationTest.java @@ -21,10 +21,10 @@ */ package de.ozgcloud.admin; +import de.ozgcloud.common.test.ITCase; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -@SpringBootTest +@ITCase class AdministrationApplicationTest { @Test diff --git a/src/test/java/de/ozgcloud/admin/migration/M001_CreateEmptyPostfachIfMissingITCase.java b/src/test/java/de/ozgcloud/admin/migration/M001_CreateEmptyPostfachIfMissingITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..8eb5819980340878289af90178eea9b1b67b6fae --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/migration/M001_CreateEmptyPostfachIfMissingITCase.java @@ -0,0 +1,103 @@ +package de.ozgcloud.admin.migration; + +import static de.ozgcloud.admin.migration.M001_CreateEmptyPostfachIfMissing.*; +import static org.assertj.core.api.Assertions.*; + +import java.util.List; + +import org.bson.Document; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; + +import de.ozgcloud.common.test.DataITCase; + +@DataITCase +class M001_CreateEmptyPostfachIfMissingITCase { + private final M001_CreateEmptyPostfachIfMissing changeUnit = new M001_CreateEmptyPostfachIfMissing(); + + private static final Document POSTFACH = MigrationTestFactory.createDummyPostfach(); + + @Autowired + private MongoTemplate template; + + @DisplayName("Do migration") + @Nested + class TestDoMigration { + @BeforeEach + public void init() { + template.dropCollection(SETTINGS_COLLECTION); + } + + @Test + void shouldAddPostfachIfEmpty() { + changeUnit.doMigration(template); + + List<Document> settings = findAllSettings(); + + assertThat(settings).usingRecursiveFieldByFieldElementComparatorIgnoringFields("_id") + .containsExactly(MigrationTestFactory.createEmptyPostfach()); + } + + @Test + void shouldAddPostfachIfMissingInCollection() { + var settingItem = MigrationTestFactory.createSettingsItem("SomeType", new Document()); + template.save(settingItem, SETTINGS_COLLECTION); + + changeUnit.doMigration(template); + + List<Document> settings = findAllSettings(); + assertThat(settings) + .extracting(TYPE_NAME_KEY) + .containsExactly("SomeType", MigrationTestFactory.ITEM_TYPE_VALUE_POSTFACH); + } + + @Test + void shouldKeepExistingPostfach() { + template.save(POSTFACH, SETTINGS_COLLECTION); + + changeUnit.doMigration(template); + + List<Document> settings = findAllSettings(); + assertThat(settings).containsExactly(POSTFACH); + } + + private List<Document> findAllSettings() { + return template.findAll(Document.class, SETTINGS_COLLECTION); + } + + } + + static class MigrationTestFactory { + + public static final String ITEM_TYPE_KEY = "name"; + public static final String ITEM_SETTINGS_KEY = "settings"; + public static final String ITEM_ID = "_id"; + public static final String ITEM_TYPE_VALUE_POSTFACH = "Postfach"; + public static final String POSTFACH_ABSENDER_KEY = "absender"; + + static Document createDummyPostfach() { + var postfach = new Document(); + postfach.put(POSTFACH_ABSENDER_KEY, "Some Value"); + return createSettingsItem(ITEM_TYPE_VALUE_POSTFACH, postfach); + } + + static Document createEmptyPostfach() { + var postfach = new Document(); + postfach.put(ITEM_TYPE_KEY, ITEM_TYPE_VALUE_POSTFACH); + postfach.put(ITEM_ID, null); + return postfach; + } + + static Document createSettingsItem(String itemType, Document itemValue) { + var settingsItem = new Document(); + settingsItem.put(ITEM_TYPE_KEY, itemType); + settingsItem.put(ITEM_SETTINGS_KEY, itemValue); + return settingsItem; + } + + } +} diff --git a/src/test/resources/application-itcase.yaml b/src/test/resources/application-itcase.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fc717c37b9cfb97360022930cb07c950b16d1983 --- /dev/null +++ b/src/test/resources/application-itcase.yaml @@ -0,0 +1,2 @@ +mongock: + enabled: false \ No newline at end of file