diff --git a/backend/src/main/java/de/bund/digitalservice/ris/norms/adapter/output/s3/BucketService.java b/backend/src/main/java/de/bund/digitalservice/ris/norms/adapter/output/s3/BucketService.java index b03aaa7cc..251bd3ae6 100644 --- a/backend/src/main/java/de/bund/digitalservice/ris/norms/adapter/output/s3/BucketService.java +++ b/backend/src/main/java/de/bund/digitalservice/ris/norms/adapter/output/s3/BucketService.java @@ -8,6 +8,7 @@ import de.bund.digitalservice.ris.norms.utils.XmlMapper; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.time.Instant; import java.util.ArrayList; import java.util.List; import lombok.extern.slf4j.Slf4j; @@ -51,8 +52,8 @@ public class BucketService PublishPrivateNormPort, DeletePublicNormPort, DeletePrivateNormPort, - DeleteAllPublicNormsPort, - DeleteAllPrivateNormsPort, + DeleteAllPublicDokumentePort, + DeleteAllPrivateDokumentePort, PublishChangelogsPort { @Value("${otc.obs.private.bucket-name}") @@ -103,13 +104,23 @@ public void deletePublicNorm(DeletePublicNormPort.Command command) throws Bucket } @Override - public void deleteAllPublicNorms() { - deleteAllExceptChangelog(publicS3Client, publicBucketName); + public void deleteAllPublicDokumente(DeleteAllPublicDokumentePort.Command command) { + deleteAllDokumenteLastModifiedBefore( + publicS3Client, + publicBucketName, + publicChangelog, + command.lastChangeBefore() + ); } @Override - public void deleteAllPrivateNorms() { - deleteAllExceptChangelog(privateS3Client, privateBucketName); + public void deleteAllPrivateDokumente(DeleteAllPrivateDokumentePort.Command command) { + deleteAllDokumenteLastModifiedBefore( + privateS3Client, + privateBucketName, + privateChangelog, + command.lastChangeBefore() + ); } @Override @@ -235,7 +246,8 @@ private void deleteFromBucket( } /** - * Deletes all objects in the specified S3 bucket, except for the changelog files, which are contained within the "changelogs" folder + * Deletes all Dokumente in the specified S3 bucket, (not the changelog files) which have not been changed since the + * given date. * The deletion process handles pagination automatically if there are more than 1,000 objects in the bucket. *

* AWS S3 allows a maximum of 1,000 keys to be processed per delete request. This method retrieves and deletes objects @@ -244,10 +256,20 @@ private void deleteFromBucket( * * @param s3Client the S3 client used to interact with the S3 service * @param bucketName the name of the S3 bucket where the objects are located + * @param lastChangeBefore Dokumente that have been changed since this date are ignored */ - private void deleteAllExceptChangelog(final S3Client s3Client, final String bucketName) { + private void deleteAllDokumenteLastModifiedBefore( + final S3Client s3Client, + final String bucketName, + Changelog changelog, + Instant lastChangeBefore + ) { try { - ListObjectsV2Request listRequest = ListObjectsV2Request.builder().bucket(bucketName).build(); + ListObjectsV2Request listRequest = ListObjectsV2Request + .builder() + .bucket(bucketName) + .prefix("eli") + .build(); ListObjectsV2Response listResponse; int objectsDeleted = 0; do { @@ -256,7 +278,10 @@ private void deleteAllExceptChangelog(final S3Client s3Client, final String buck for (S3Object s3Object : listResponse.contents()) { final String key = s3Object.key(); - if (!key.startsWith(Changelog.FOLDER + "/")) { + if ( + !key.startsWith(Changelog.FOLDER + "/") && + s3Object.lastModified().isBefore(lastChangeBefore) + ) { objectsToDelete.add(ObjectIdentifier.builder().key(key).build()); } } @@ -268,6 +293,9 @@ private void deleteAllExceptChangelog(final S3Client s3Client, final String buck .build(); s3Client.deleteObjects(deleteRequest); objectsDeleted += objectsToDelete.size(); + objectsToDelete.forEach(objectIdentifier -> + changelog.addContent(Changelog.DELETED, objectIdentifier.key()) + ); } listRequest = @@ -278,7 +306,7 @@ private void deleteAllExceptChangelog(final S3Client s3Client, final String buck throw new BucketException( BucketException.Operation.DELETE, bucketName, - "All norms could not be deleted", + "All dokumente could not be deleted", e ); } diff --git a/backend/src/main/java/de/bund/digitalservice/ris/norms/adapter/output/s3/Changelog.java b/backend/src/main/java/de/bund/digitalservice/ris/norms/adapter/output/s3/Changelog.java index 594d17b28..fa8a6bc86 100644 --- a/backend/src/main/java/de/bund/digitalservice/ris/norms/adapter/output/s3/Changelog.java +++ b/backend/src/main/java/de/bund/digitalservice/ris/norms/adapter/output/s3/Changelog.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.nio.file.Paths; -import java.time.LocalDate; +import java.time.Instant; import java.util.*; import lombok.Getter; @@ -15,7 +15,7 @@ public class Changelog { public static final String FOLDER = "changelogs"; - public static final String FILE_NAME_FORMAT = "changelog-%s.json"; + public static final String FILE_NAME_FORMAT = "%s-norms.json"; public static final String CHANGED = "changed"; public static final String DELETED = "deleted"; @@ -27,7 +27,8 @@ public class Changelog { private final String fileName; public Changelog() { - this.fileName = Paths.get(FOLDER, FILE_NAME_FORMAT.formatted(LocalDate.now())).toString(); + this.fileName = + Paths.get(FOLDER, FILE_NAME_FORMAT.formatted(Instant.now().toString())).toString(); } /** diff --git a/backend/src/main/java/de/bund/digitalservice/ris/norms/adapter/output/s3/S3MockClient.java b/backend/src/main/java/de/bund/digitalservice/ris/norms/adapter/output/s3/S3MockClient.java index 9af7ceb26..959cb485e 100644 --- a/backend/src/main/java/de/bund/digitalservice/ris/norms/adapter/output/s3/S3MockClient.java +++ b/backend/src/main/java/de/bund/digitalservice/ris/norms/adapter/output/s3/S3MockClient.java @@ -3,6 +3,7 @@ import jakarta.annotation.PostConstruct; import java.io.*; import java.nio.file.*; +import java.time.Instant; import java.util.List; import java.util.Objects; import java.util.stream.Stream; @@ -73,7 +74,11 @@ public ListObjectsV2Response listObjectsV2(ListObjectsV2Request listObjectsV2Req // If the file is not under 'eli', return only the file name key = path.getFileName().toString(); } - return S3Object.builder().key(key).build(); + return S3Object + .builder() + .key(key) + .lastModified(Instant.ofEpochMilli(path.toFile().lastModified())) + .build(); }) .toList(); diff --git a/backend/src/main/java/de/bund/digitalservice/ris/norms/application/port/output/DeleteAllPrivateDokumentePort.java b/backend/src/main/java/de/bund/digitalservice/ris/norms/application/port/output/DeleteAllPrivateDokumentePort.java new file mode 100644 index 000000000..52d55a50a --- /dev/null +++ b/backend/src/main/java/de/bund/digitalservice/ris/norms/application/port/output/DeleteAllPrivateDokumentePort.java @@ -0,0 +1,25 @@ +package de.bund.digitalservice.ris.norms.application.port.output; + +import de.bund.digitalservice.ris.norms.domain.entity.Dokument; +import java.time.Instant; + +/** + * Interface representing the output port for deleting multiple {@link Dokument} entities from a storage location designated + * for private data. + */ +public interface DeleteAllPrivateDokumentePort { + /** + * Deletes all {@link Dokument} entities that have not been edited since the given date from a designated private + * storage location. + * + * @param command command for deleting Dokumente + */ + void deleteAllPrivateDokumente(DeleteAllPrivateDokumentePort.Command command); + + /** + * Command for deleting dokumente + * + * @param lastChangeBefore Dokumente last edited after the given date are not deleted. + */ + record Command(Instant lastChangeBefore) {} +} diff --git a/backend/src/main/java/de/bund/digitalservice/ris/norms/application/port/output/DeleteAllPrivateNormsPort.java b/backend/src/main/java/de/bund/digitalservice/ris/norms/application/port/output/DeleteAllPrivateNormsPort.java deleted file mode 100644 index 8c154d416..000000000 --- a/backend/src/main/java/de/bund/digitalservice/ris/norms/application/port/output/DeleteAllPrivateNormsPort.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.bund.digitalservice.ris.norms.application.port.output; - -import de.bund.digitalservice.ris.norms.domain.entity.Norm; - -/** - * Interface representing the output port for deleting multiple {@link Norm} entities from a storage location designated - * for private data. - */ -public interface DeleteAllPrivateNormsPort { - /** - * Deletes the specified {@link Norm} entities from a designated private storage location. - * - */ - void deleteAllPrivateNorms(); -} diff --git a/backend/src/main/java/de/bund/digitalservice/ris/norms/application/port/output/DeleteAllPublicDokumentePort.java b/backend/src/main/java/de/bund/digitalservice/ris/norms/application/port/output/DeleteAllPublicDokumentePort.java new file mode 100644 index 000000000..6b347f724 --- /dev/null +++ b/backend/src/main/java/de/bund/digitalservice/ris/norms/application/port/output/DeleteAllPublicDokumentePort.java @@ -0,0 +1,25 @@ +package de.bund.digitalservice.ris.norms.application.port.output; + +import de.bund.digitalservice.ris.norms.domain.entity.Dokument; +import java.time.Instant; + +/** + * Interface representing the output port for deleting all {@link Dokument} entities from a storage location designated + * for public data. + */ +public interface DeleteAllPublicDokumentePort { + /** + * Deletes all {@link Dokument} entities that have not been edited since the given date from a designated public + * storage location. + * + * @param command command for deleting Dokumente + */ + void deleteAllPublicDokumente(DeleteAllPublicDokumentePort.Command command); + + /** + * Command for deleting dokumente + * + * @param lastChangeBefore Dokumente last edited after the given date are not deleted. + */ + record Command(Instant lastChangeBefore) {} +} diff --git a/backend/src/main/java/de/bund/digitalservice/ris/norms/application/port/output/DeleteAllPublicNormsPort.java b/backend/src/main/java/de/bund/digitalservice/ris/norms/application/port/output/DeleteAllPublicNormsPort.java deleted file mode 100644 index dd150e450..000000000 --- a/backend/src/main/java/de/bund/digitalservice/ris/norms/application/port/output/DeleteAllPublicNormsPort.java +++ /dev/null @@ -1,14 +0,0 @@ -package de.bund.digitalservice.ris.norms.application.port.output; - -import de.bund.digitalservice.ris.norms.domain.entity.Norm; - -/** - * Interface representing the output port for deleting all {@link Norm} entities from a storage location designated - * for public data. - */ -public interface DeleteAllPublicNormsPort { - /** - * Deletes all {@link Norm} entities from a designated public storage location. - */ - void deleteAllPublicNorms(); -} diff --git a/backend/src/main/java/de/bund/digitalservice/ris/norms/application/service/PublishService.java b/backend/src/main/java/de/bund/digitalservice/ris/norms/application/service/PublishService.java index c3a43eaca..79d5d181b 100644 --- a/backend/src/main/java/de/bund/digitalservice/ris/norms/application/service/PublishService.java +++ b/backend/src/main/java/de/bund/digitalservice/ris/norms/application/service/PublishService.java @@ -7,6 +7,7 @@ import de.bund.digitalservice.ris.norms.domain.entity.eli.NormManifestationEli; import de.bund.digitalservice.ris.norms.utils.NodeParser; import de.bund.digitalservice.ris.norms.utils.exceptions.StorageException; +import java.time.Instant; import java.time.LocalDate; import java.time.ZoneId; import java.time.ZoneOffset; @@ -33,8 +34,8 @@ public class PublishService implements PublishNormUseCase { private final DeletePublicNormPort deletePublicNormPort; private final DeletePrivateNormPort deletePrivateNormPort; private final LoadLastMigrationLogPort loadLastMigrationLogPort; - private final DeleteAllPublicNormsPort deleteAllPublicNormsPort; - private final DeleteAllPrivateNormsPort deleteAllPrivateNormsPort; + private final DeleteAllPublicDokumentePort deleteAllPublicDokumentePort; + private final DeleteAllPrivateDokumentePort deleteAllPrivateDokumentePort; private final PublishChangelogsPort publishChangelogsPort; public PublishService( @@ -46,8 +47,8 @@ public PublishService( DeletePublicNormPort deletePublicNormPort, DeletePrivateNormPort deletePrivateNormPort, LoadLastMigrationLogPort loadLastMigrationLogPort, - DeleteAllPublicNormsPort deleteAllPublicNormsPort, - DeleteAllPrivateNormsPort deleteAllPrivateNormsPort, + DeleteAllPublicDokumentePort deleteAllPublicDokumentePort, + DeleteAllPrivateDokumentePort deleteAllPrivateDokumentePort, PublishChangelogsPort publishChangelogsPort ) { this.loadNormManifestationElisByPublishStatePort = loadNormManifestationElisByPublishStatePort; @@ -58,13 +59,32 @@ public PublishService( this.deletePublicNormPort = deletePublicNormPort; this.deletePrivateNormPort = deletePrivateNormPort; this.loadLastMigrationLogPort = loadLastMigrationLogPort; - this.deleteAllPublicNormsPort = deleteAllPublicNormsPort; - this.deleteAllPrivateNormsPort = deleteAllPrivateNormsPort; + this.deleteAllPublicDokumentePort = deleteAllPublicDokumentePort; + this.deleteAllPrivateDokumentePort = deleteAllPrivateDokumentePort; this.publishChangelogsPort = publishChangelogsPort; } @Override public void processQueuedFilesForPublish() { + final Instant startOfProcessing = Instant.now(); + final LocalDate today = startOfProcessing.atZone(ZoneId.systemDefault()).toLocalDate(); + + List manifestationElis = + loadNormManifestationElisByPublishStatePort.loadNormManifestationElisByPublishState( + new LoadNormManifestationElisByPublishStatePort.Command(NormPublishState.QUEUED_FOR_PUBLISH) + ); + + log.info("Found {} norms that are queued for publishing", manifestationElis.size()); + + manifestationElis.forEach(manifestationEli -> { + log.info("Processing norm with manifestation eli {}", manifestationEli); + Optional norm = loadNormPort.loadNorm(new LoadNormPort.Command(manifestationEli)); + norm.ifPresent(this::processNorm); + if (norm.isEmpty()) { + log.error("Norm with manifestation eli {} not found", manifestationEli); + } + }); + loadLastMigrationLogPort .loadLastMigrationLog() .ifPresent(migrationLog -> { @@ -72,41 +92,31 @@ public void processQueuedFilesForPublish() { .getCreatedAt() .atZone(ZoneId.systemDefault()) .toLocalDate(); - final LocalDate today = LocalDate.now(); final LocalDate yesterday = today.minusDays(1); if (createdAtDate.equals(today) || createdAtDate.equals(yesterday)) { log.info( - "Migration log found with timestamp {} (UTC). Deleting all norms in both buckets", + "Migration log found with timestamp {} (UTC) and {} dokumente.", migrationLog .getCreatedAt() .atOffset(ZoneOffset.UTC) - .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), + migrationLog.getSize() ); if (migrationLog.getSize() <= 0) { throw new MigrationJobException(); } - deleteAllPublicNormsPort.deleteAllPublicNorms(); - deleteAllPrivateNormsPort.deleteAllPrivateNorms(); - log.info("Deleted all norms in both buckets"); + log.info("Deleting all old dokumente in both buckets"); + deleteAllPublicDokumentePort.deleteAllPublicDokumente( + new DeleteAllPublicDokumentePort.Command(startOfProcessing) + ); + deleteAllPrivateDokumentePort.deleteAllPrivateDokumente( + new DeleteAllPrivateDokumentePort.Command(startOfProcessing) + ); + log.info("Deleted all dokumente in both buckets"); } }); - List manifestationElis = - loadNormManifestationElisByPublishStatePort.loadNormManifestationElisByPublishState( - new LoadNormManifestationElisByPublishStatePort.Command(NormPublishState.QUEUED_FOR_PUBLISH) - ); - - log.info("Found {} norms that are queued for publishing", manifestationElis.size()); - - manifestationElis.forEach(manifestationEli -> { - log.info("Processing norm with manifestation eli {}", manifestationEli); - Optional norm = loadNormPort.loadNorm(new LoadNormPort.Command(manifestationEli)); - norm.ifPresent(this::processNorm); - if (norm.isEmpty()) { - log.error("Norm with manifestation eli {} not found", manifestationEli); - } - }); - publishChangelogsPort.publishChangelogs(new PublishChangelogsPort.Command(true)); + publishChangelogsPort.publishChangelogs(new PublishChangelogsPort.Command(false)); log.info("Publish job successfully completed."); } @@ -167,7 +177,7 @@ private void rollbackPrivatePublish(Norm norm) { try { deletePrivateNormPort.deletePrivateNorm(new DeletePrivateNormPort.Command(norm)); log.info( - "Deleted privated norm on rollback strategy: {}", + "Deleted private norm on rollback strategy: {}", norm.getManifestationEli().toString() ); } catch (StorageException e) { diff --git a/backend/src/test/java/de/bund/digitalservice/ris/norms/adapter/output/s3/ChangelogTest.java b/backend/src/test/java/de/bund/digitalservice/ris/norms/adapter/output/s3/ChangelogTest.java index be246657a..4b34cee82 100644 --- a/backend/src/test/java/de/bund/digitalservice/ris/norms/adapter/output/s3/ChangelogTest.java +++ b/backend/src/test/java/de/bund/digitalservice/ris/norms/adapter/output/s3/ChangelogTest.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import java.io.IOException; -import java.time.LocalDate; import org.junit.jupiter.api.Test; class ChangelogTest { @@ -14,7 +13,7 @@ void createsChangelogWithGivenDate() { final Changelog changelog = new Changelog(); assertThat(changelog.getFileName()).isNotEmpty(); assertThat(changelog.getFileName()) - .isEqualTo("changelogs/changelog-%s.json".formatted(LocalDate.now().toString())); + .matches("changelogs/\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.\\d+Z-norms\\.json"); } @Test diff --git a/backend/src/test/java/de/bund/digitalservice/ris/norms/application/service/PublishServiceTest.java b/backend/src/test/java/de/bund/digitalservice/ris/norms/application/service/PublishServiceTest.java index 13c6ff216..b315bb000 100644 --- a/backend/src/test/java/de/bund/digitalservice/ris/norms/application/service/PublishServiceTest.java +++ b/backend/src/test/java/de/bund/digitalservice/ris/norms/application/service/PublishServiceTest.java @@ -32,9 +32,13 @@ class PublishServiceTest { final DeletePrivateNormPort deletePrivateNormPort = mock(DeletePrivateNormPort.class); - final DeleteAllPublicNormsPort deleteAllPublicNormsPort = mock(DeleteAllPublicNormsPort.class); + final DeleteAllPublicDokumentePort deleteAllPublicDokumentePort = mock( + DeleteAllPublicDokumentePort.class + ); - final DeleteAllPrivateNormsPort deleteAllPrivateNormsPort = mock(DeleteAllPrivateNormsPort.class); + final DeleteAllPrivateDokumentePort deleteAllPrivateDokumentePort = mock( + DeleteAllPrivateDokumentePort.class + ); final LoadNormPort loadNormPort = mock(LoadNormPort.class); final LoadLastMigrationLogPort loadLastMigrationLogPort = mock(LoadLastMigrationLogPort.class); @@ -50,8 +54,8 @@ class PublishServiceTest { deletePublicNormPort, deletePrivateNormPort, loadLastMigrationLogPort, - deleteAllPublicNormsPort, - deleteAllPrivateNormsPort, + deleteAllPublicDokumentePort, + deleteAllPrivateDokumentePort, publishChangelogsPort ); @@ -185,8 +189,8 @@ void deleteAllNormsIfMigrationLogExists() { ); // Check that deletion was called - verify(deleteAllPublicNormsPort, times(1)).deleteAllPublicNorms(); - verify(deleteAllPrivateNormsPort, times(1)).deleteAllPrivateNorms(); + verify(deleteAllPublicDokumentePort, times(1)).deleteAllPublicDokumente(any()); + verify(deleteAllPrivateDokumentePort, times(1)).deleteAllPrivateDokumente(any()); // Verify norm publishing actions verify(publishPublicNormPort, times(1)) @@ -216,27 +220,12 @@ void doNotdeleteAllNormsIfMigrationLogExistsButSizeIsZero() { when(loadLastMigrationLogPort.loadLastMigrationLog()).thenReturn(Optional.of(migrationLog)); // Migration log found // Then When - assertThatThrownBy(publishService::processQueuedFilesForPublish) .isInstanceOf(MigrationJobException.class); - // Then - verify(loadNormManifestationElisByPublishStatePort, times(0)) - .loadNormManifestationElisByPublishState( - argThat(command -> command.publishState() == NormPublishState.QUEUED_FOR_PUBLISH) - ); - - // Check that deletion was called - verify(deleteAllPublicNormsPort, times(0)).deleteAllPublicNorms(); - verify(deleteAllPrivateNormsPort, times(0)).deleteAllPrivateNorms(); - - // Verify norm publishing actions - verify(publishPublicNormPort, times(0)) - .publishPublicNorm(new PublishPublicNormPort.Command(norm)); - verify(publishPrivateNormPort, times(0)) - .publishPrivateNorm(new PublishPrivateNormPort.Command(norm)); - verify(updateOrSaveNormPort, times(0)).updateOrSave(new UpdateOrSaveNormPort.Command(norm)); - verify(publishChangelogsPort, times(0)).publishChangelogs(any()); + // Check that deletion was not called + verify(deleteAllPublicDokumentePort, times(0)).deleteAllPublicDokumente(any()); + verify(deleteAllPrivateDokumentePort, times(0)).deleteAllPrivateDokumente(any()); } @Test @@ -262,8 +251,8 @@ void doNotDeleteNormsIfNoMigrationLogExists() { ); // Verify that deletion was NOT called - verify(deleteAllPublicNormsPort, never()).deleteAllPublicNorms(); - verify(deleteAllPrivateNormsPort, never()).deleteAllPrivateNorms(); + verify(deleteAllPublicDokumentePort, never()).deleteAllPublicDokumente(any()); + verify(deleteAllPrivateDokumentePort, never()).deleteAllPrivateDokumente(any()); // Verify norm publishing actions verify(publishPublicNormPort, times(1)) diff --git a/backend/src/test/java/de/bund/digitalservice/ris/norms/integration/adapter/output/s3/BucketServiceIntegrationTest.java b/backend/src/test/java/de/bund/digitalservice/ris/norms/integration/adapter/output/s3/BucketServiceIntegrationTest.java index ec00bc223..2db244a89 100644 --- a/backend/src/test/java/de/bund/digitalservice/ris/norms/integration/adapter/output/s3/BucketServiceIntegrationTest.java +++ b/backend/src/test/java/de/bund/digitalservice/ris/norms/integration/adapter/output/s3/BucketServiceIntegrationTest.java @@ -17,10 +17,12 @@ import de.bund.digitalservice.ris.norms.integration.BaseS3MockIntegrationTest; import java.nio.file.Files; import java.nio.file.Path; -import java.time.LocalDate; +import java.time.Instant; +import java.util.Comparator; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -190,7 +192,7 @@ void itDeletesAllNormsFromPublicBucket() { bucketService.publishPublicNorm(commandPublish2); // When - bucketService.deleteAllPublicNorms(); + bucketService.deleteAllPublicDokumente(new DeleteAllPublicDokumentePort.Command(Instant.now())); final PublishChangelogsPort.Command commandPublishChangelogs = new PublishChangelogsPort.Command(false); bucketService.publishChangelogs(commandPublishChangelogs); @@ -209,50 +211,30 @@ void itDeletesAllNormsFromPrivateBucket() { // Given final Norm norm1 = Fixtures.loadNormFromDisk("SimpleNorm.xml"); final Norm norm2 = Fixtures.loadNormFromDisk("NormToBeReleased.xml"); - final PublishPrivateNormPort.Command commandPublish1 = new PublishPrivateNormPort.Command( - norm1 - ); - final PublishPrivateNormPort.Command commandPublish2 = new PublishPrivateNormPort.Command( - norm2 - ); - bucketService.publishPrivateNorm(commandPublish1); - bucketService.publishPrivateNorm(commandPublish2); + final Norm norm3 = Fixtures.loadNormFromDisk("NormWithMods.xml"); + + bucketService.publishPrivateNorm(new PublishPrivateNormPort.Command(norm1)); + bucketService.publishPrivateNorm(new PublishPrivateNormPort.Command(norm2)); + Instant afterTwoPublishes = Instant.now(); + bucketService.publishChangelogs(new PublishChangelogsPort.Command(false)); + bucketService.publishPrivateNorm(new PublishPrivateNormPort.Command(norm3)); // When - bucketService.deleteAllPrivateNorms(); - final PublishChangelogsPort.Command commandPublishChangelogs = - new PublishChangelogsPort.Command(false); - bucketService.publishChangelogs(commandPublishChangelogs); + bucketService.deleteAllPrivateDokumente( + new DeleteAllPrivateDokumentePort.Command(afterTwoPublishes) + ); + bucketService.publishChangelogs(new PublishChangelogsPort.Command(false)); // Then final Path filePath1 = getPrivatePath(norm1.getRegelungstext1()); final Path filePath2 = getPrivatePath(norm2.getRegelungstext1()); + final Path filePath3 = getPrivatePath(norm3.getRegelungstext1()); assertThat(Files.exists(filePath1)).isFalse(); assertThat(Files.exists(filePath2)).isFalse(); + assertThat(Files.exists(filePath3)).isTrue(); assertChangelogContains(false, PRIVATE_BUCKET, DELETED, norm1); assertChangelogContains(false, PRIVATE_BUCKET, DELETED, norm2); - } - - @Test - void itAddsToExistingChangelog() { - // Given - final Norm norm = Fixtures.loadNormFromDisk("SimpleNorm.xml"); - final PublishPublicNormPort.Command command = new PublishPublicNormPort.Command(norm); - final Norm anotherNorm = Fixtures.loadNormFromDisk("NormToBeReleased.xml"); - final PublishPublicNormPort.Command commandAnotherNorm = new PublishPublicNormPort.Command( - anotherNorm - ); - final PublishChangelogsPort.Command commandPublishChangelogs = - new PublishChangelogsPort.Command(false); - // When - bucketService.publishPublicNorm(command); - bucketService.publishChangelogs(commandPublishChangelogs); - bucketService.publishPublicNorm(commandAnotherNorm); - bucketService.publishChangelogs(commandPublishChangelogs); - - // Then - assertChangelogContains(true, PUBLIC_BUCKET, CHANGED, norm); - assertChangelogContains(true, PUBLIC_BUCKET, CHANGED, anotherNorm); + assertChangelogContains(true, PRIVATE_BUCKET, CHANGED, norm3); } private void assertChangelogContains( @@ -261,21 +243,20 @@ private void assertChangelogContains( final String operation, final Norm norm ) { - Path changeLogPath; - if (Objects.equals(location, PUBLIC_BUCKET)) { - changeLogPath = - getPublicPath() - .resolve(Changelog.FOLDER) - .resolve("changelog-%s.json".formatted(LocalDate.now().toString())); - } else { - changeLogPath = - getPrivatePath() - .resolve(Changelog.FOLDER) - .resolve("changelog-%s.json".formatted(LocalDate.now().toString())); - } - final Map> changelogEntries; + try { + Path changeLogPath; + if (Objects.equals(location, PUBLIC_BUCKET)) { + try (Stream files = Files.list(getPublicPath().resolve(Changelog.FOLDER))) { + changeLogPath = files.max(Comparator.naturalOrder()).orElseThrow(); + } + } else { + try (Stream files = Files.list(getPrivatePath().resolve(Changelog.FOLDER))) { + changeLogPath = files.max(Comparator.naturalOrder()).orElseThrow(); + } + } + final String json = Files.readString(changeLogPath); changelogEntries = OBJECT_MAPPER.readValue(json, new TypeReference<>() {}); } catch (Exception e) {