diff --git a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/AttachmentsExport.groovy b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/AttachmentsExport.groovy index 0521e89..0fe1253 100644 --- a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/AttachmentsExport.groovy +++ b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/AttachmentsExport.groovy @@ -27,7 +27,7 @@ static void run(Migration migration, Path attachmentsDstPath) { attachmentsDstPath.toFile().createParentDirectories() attachmentsDstPath.toFile().withWriter { writer -> - def headers = ["id", "name", "sourcePath", "attachmentType", "targetFolder", "status", "skip", "skipPlaceholder", "skipReason", Mapping.displayHeader("originalName", true), Mapping.displayHeader("originLocations", true)] + def headers = ["id", "name", "sourcePath", "attachmentType", "targetFolder", "targetImageId", "status", "skip", "skipPlaceholder", "skipReason", Mapping.displayHeader("originalName", true), Mapping.displayHeader("originLocations", true)] writer.writeLine(headers.join(",")) attachments.each { obj -> def status = migration.statusTrackingRepository.findLastEventRelevantToOutput(obj.id, @@ -40,6 +40,7 @@ static void run(Migration migration, Path attachmentsDstPath) { builder.append("," + Csv.serialize(obj.sourcePath)) builder.append("," + Csv.serialize(obj.attachmentType)) builder.append("," + Csv.serialize(obj.targetFolder)) + builder.append("," + Csv.serialize(obj.targetImageId)) builder.append("," + Csv.serialize(status.class.simpleName)) builder.append("," + Csv.serialize(obj.skip.skipped)) builder.append("," + Csv.serialize(obj.skip.placeholder)) diff --git a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/AttachmentsImport.groovy b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/AttachmentsImport.groovy index a1954ac..ad37526 100644 --- a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/AttachmentsImport.groovy +++ b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/AttachmentsImport.groovy @@ -55,6 +55,9 @@ static void run(Migration migration, Path attachmentsFilePath) { def newTargetFolder = Csv.deserialize(values.get("targetFolder"), String.class) Mapping.mapProp(existingMapping, existingAttachment, "targetFolder", newTargetFolder) + def newTargetImageId = Csv.deserialize(values.get("targetImageId"), String.class) + Mapping.mapProp(existingMapping, existingAttachment, "targetImageId", newTargetImageId) + def csvStatus = values.get("status") if (status != null && csvStatus == "Active" && status.class.simpleName != "Active") { migration.statusTrackingRepository.active(existingAttachment.id, ResourceType.Attachment, [reason: "Manual"]) diff --git a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesExport.groovy b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesExport.groovy index 0d6d84a..8772969 100644 --- a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesExport.groovy +++ b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesExport.groovy @@ -27,7 +27,7 @@ static void run(Migration migration, Path imagesDstPath) { imagesDstPath.toFile().createParentDirectories() imagesDstPath.toFile().withWriter { writer -> - def headers = ["id", "name", "sourcePath", "imageType", "targetFolder", "alternateText", "status", "skip", "skipPlaceholder", "skipReason", Mapping.displayHeader("originalName", true), Mapping.displayHeader("originLocations", true)] + def headers = ["id", "name", "sourcePath", "imageType", "targetFolder", "alternateText", "targetAttachmentId", "status", "skip", "skipPlaceholder", "skipReason", Mapping.displayHeader("originalName", true), Mapping.displayHeader("originLocations", true)] writer.writeLine(headers.join(",")) images.each { obj -> def status = migration.statusTrackingRepository.findLastEventRelevantToOutput(obj.id, @@ -41,6 +41,7 @@ static void run(Migration migration, Path imagesDstPath) { builder.append("," + Csv.serialize(obj.imageType)) builder.append("," + Csv.serialize(obj.targetFolder)) builder.append("," + Csv.serialize(obj.alternateText)) + builder.append("," + Csv.serialize(obj.targetAttachmentId)) builder.append("," + Csv.serialize(status.class.simpleName)) builder.append("," + Csv.serialize(obj.skip.skipped)) builder.append("," + Csv.serialize(obj.skip.placeholder)) diff --git a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesImport.groovy b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesImport.groovy index 0afde16..28b0dd6 100644 --- a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesImport.groovy +++ b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesImport.groovy @@ -58,6 +58,9 @@ static void run(Migration migration, Path imagesFilePath) { def newAlternateText = Csv.deserialize(values.get("alternateText"), String.class) Mapping.mapProp(existingMapping, existingImage, "alternateText", newAlternateText) + def newTargetAttachmentId = Csv.deserialize(values.get("targetAttachmentId"), String.class) + Mapping.mapProp(existingMapping, existingImage, "targetAttachmentId", newTargetAttachmentId) + def csvStatus = values.get("status") if (status != null && csvStatus == "Active" && status.class.simpleName != "Active") { migration.statusTrackingRepository.active(existingImage.id, ResourceType.Image, [reason: "Manual"]) diff --git a/migration-examples/src/test/groovy/AttachmentsMappingExportTest.groovy b/migration-examples/src/test/groovy/AttachmentsMappingExportTest.groovy index 879628c..f84382a 100644 --- a/migration-examples/src/test/groovy/AttachmentsMappingExportTest.groovy +++ b/migration-examples/src/test/groovy/AttachmentsMappingExportTest.groovy @@ -24,10 +24,11 @@ class AttachmentsMappingExportTest { def migration = Utils.mockMigration() when(migration.attachmentRepository.listAll()).thenReturn([ - new Attachment("empty", null, [], new CustomFieldMap([:]), null, null, AttachmentType.Document, emptySkipOptions()), - new Attachment("full", "full", ["foo", "bar"], new CustomFieldMap([:]), "sourcePath", "targetDir", AttachmentType.Document, new SkipOptions(true, "placeholder", "reason")), - new Attachment("overridden empty", null, [], new CustomFieldMap([:]), null, null, AttachmentType.Document, emptySkipOptions()), - new Attachment("overridden full", "full", ["foo", "bar"], new CustomFieldMap(["originalName": "originalFull"]), "sourcePath", "targetDir", AttachmentType.Attachment, emptySkipOptions()), + new Attachment("empty", null, [], new CustomFieldMap([:]), null, null, AttachmentType.Document, emptySkipOptions(), null), + new Attachment("full", "full", ["foo", "bar"], new CustomFieldMap([:]), "sourcePath", "targetDir", AttachmentType.Document, new SkipOptions(true, "placeholder", "reason"), null), + new Attachment("with-target-image", "with-target", [], new CustomFieldMap([:]), null, null, AttachmentType.Attachment, emptySkipOptions(), "img456"), + new Attachment("overridden empty", null, [], new CustomFieldMap([:]), null, null, AttachmentType.Document, emptySkipOptions(), null), + new Attachment("overridden full", "full", ["foo", "bar"], new CustomFieldMap(["originalName": "originalFull"]), "sourcePath", "targetDir", AttachmentType.Attachment, emptySkipOptions(), null), ]) when(migration.statusTrackingRepository.findLastEventRelevantToOutput(any(), any(), any())).thenReturn(new Active()) @@ -35,11 +36,12 @@ class AttachmentsMappingExportTest { AttachmentsExport.run(migration, mappingFile) def expected = """\ - id,name,sourcePath,attachmentType,targetFolder,status,skip,skipPlaceholder,skipReason,originalName (read-only),originLocations (read-only) - empty,,,Document,,Active,false,,,,[] - full,full,sourcePath,Document,targetDir,Active,true,placeholder,reason,,[foo; bar] - overridden empty,,,Document,,Active,false,,,,[] - overridden full,full,sourcePath,Attachment,targetDir,Active,false,,,originalFull,[foo; bar] + id,name,sourcePath,attachmentType,targetFolder,targetImageId,status,skip,skipPlaceholder,skipReason,originalName (read-only),originLocations (read-only) + empty,,,Document,,,Active,false,,,,[] + full,full,sourcePath,Document,targetDir,,Active,true,placeholder,reason,,[foo; bar] + with-target-image,with-target,,Attachment,,img456,Active,false,,,,[] + overridden empty,,,Document,,,Active,false,,,,[] + overridden full,full,sourcePath,Attachment,targetDir,,Active,false,,,originalFull,[foo; bar] """.stripIndent() Assertions.assertEquals(expected, mappingFile.toFile().text.replaceAll("\\r\\n|\\r", "\n")) } diff --git a/migration-examples/src/test/groovy/AttachmentsMappingImportTest.groovy b/migration-examples/src/test/groovy/AttachmentsMappingImportTest.groovy index 02045cc..1d92dc9 100644 --- a/migration-examples/src/test/groovy/AttachmentsMappingImportTest.groovy +++ b/migration-examples/src/test/groovy/AttachmentsMappingImportTest.groovy @@ -20,9 +20,9 @@ class AttachmentsMappingImportTest { def migration = Utils.mockMigration() Path mappingFile = Paths.get(dir.path, "testProject.csv") def input = """\ - id,name,sourcePath,attachmentType,targetFolder,status,originLocations,skip,skipPlaceholder,skipReason - attachment1,newName,newPath,Attachment,newFolder,Active,[],false,, - attachment2,,,Document,,Active,[],true,placeholder,reason + id,name,sourcePath,attachmentType,targetFolder,targetImageId,status,originLocations,skip,skipPlaceholder,skipReason + attachment1,newName,newPath,Attachment,newFolder,img123,Active,[],false,, + attachment2,,,Document,,,Active,[],true,placeholder,reason """.stripIndent() mappingFile.toFile().write(input) @@ -33,14 +33,14 @@ class AttachmentsMappingImportTest { AttachmentsImport.run(migration, mappingFile) - verify(migration.mappingRepository, times(1)).upsert("attachment1", new MappingItem.Attachment("newName", "newFolder", "newPath", AttachmentType.Attachment, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("attachment1", new MappingItem.Attachment("newName", "newFolder", "newPath", AttachmentType.Attachment, new SkipOptions(false, null, null), "img123")) verify(migration.mappingRepository, times(1)).applyAttachmentMapping("attachment1") - verify(migration.mappingRepository, times(1)).upsert("attachment2", new MappingItem.Attachment(null, null, null, AttachmentType.Document, new SkipOptions(true, "placeholder", "reason"))) + verify(migration.mappingRepository, times(1)).upsert("attachment2", new MappingItem.Attachment(null, null, null, AttachmentType.Document, new SkipOptions(true, "placeholder", "reason"), null)) verify(migration.mappingRepository, times(1)).applyAttachmentMapping("attachment2") } static void givenExistingAttachment(Migration mig, String id, String name, String targetFolder, String sourcePath, AttachmentType attachmentType) { - when(mig.attachmentRepository.find(id)).thenReturn(new Attachment(id, name, [], new CustomFieldMap([:]), sourcePath, targetFolder, attachmentType, new SkipOptions(false, null, null))) + when(mig.attachmentRepository.find(id)).thenReturn(new Attachment(id, name, [], new CustomFieldMap([:]), sourcePath, targetFolder, attachmentType, new SkipOptions(false, null, null), null)) } static void givenExistingAttachmentMapping(Migration mig, @@ -50,6 +50,6 @@ class AttachmentsMappingImportTest { String sourcePath, AttachmentType attachmentType) { when(mig.mappingRepository.getAttachmentMapping(id)) - .thenReturn(new MappingItem.Attachment(name, targetFolder, sourcePath, attachmentType, null)) + .thenReturn(new MappingItem.Attachment(name, targetFolder, sourcePath, attachmentType, null, null)) } } diff --git a/migration-examples/src/test/groovy/ImagesMappingExportTest.groovy b/migration-examples/src/test/groovy/ImagesMappingExportTest.groovy index 481a55a..10c804d 100644 --- a/migration-examples/src/test/groovy/ImagesMappingExportTest.groovy +++ b/migration-examples/src/test/groovy/ImagesMappingExportTest.groovy @@ -25,10 +25,11 @@ class ImagesMappingExportTest { def migration = Utils.mockMigration() when(migration.imageRepository.listAll()).thenReturn([ - new Image("empty", null, [], new CustomFieldMap([:]), null, null, null, null, [:], emptySkipOptions(), null), - new Image("full", "full", ["foo", "bar"], new CustomFieldMap([:]), "sourcePath", null, ImageType.Jpeg, "targetDir", [:], new SkipOptions(true, "placeholder", "reason"), "Alt text for full"), - new Image("overridden empty", null, [], new CustomFieldMap([:]), null, null, null, null, [:], emptySkipOptions(), null), - new Image("overridden full", "full", ["foo", "bar"], new CustomFieldMap(["originalName": "originalFull"]), "sourcePath", null, ImageType.Gif, "targetDir", [:], emptySkipOptions(), "Alt text for overridden"), + new Image("empty", null, [], new CustomFieldMap([:]), null, null, null, null, [:], emptySkipOptions(), null, null), + new Image("full", "full", ["foo", "bar"], new CustomFieldMap([:]), "sourcePath", null, ImageType.Jpeg, "targetDir", [:], new SkipOptions(true, "placeholder", "reason"), "Alt text for full", null), + new Image("with-target-attachment", "with-target", [], new CustomFieldMap([:]), null, null, ImageType.Png, null, [:], emptySkipOptions(), null, "att123"), + new Image("overridden empty", null, [], new CustomFieldMap([:]), null, null, null, null, [:], emptySkipOptions(), null, null), + new Image("overridden full", "full", ["foo", "bar"], new CustomFieldMap(["originalName": "originalFull"]), "sourcePath", null, ImageType.Gif, "targetDir", [:], emptySkipOptions(), "Alt text for overridden", null), ]) when(migration.statusTrackingRepository.findLastEventRelevantToOutput(any(), any(), any())).thenReturn(new Active()) @@ -36,11 +37,12 @@ class ImagesMappingExportTest { ImagesExport.run(migration, mappingFile) def expected = """\ - id,name,sourcePath,imageType,targetFolder,alternateText,status,skip,skipPlaceholder,skipReason,originalName (read-only),originLocations (read-only) - empty,,,,,,Active,false,,,,[] - full,full,sourcePath,Jpeg,targetDir,Alt text for full,Active,true,placeholder,reason,,[foo; bar] - overridden empty,,,,,,Active,false,,,,[] - overridden full,full,sourcePath,Gif,targetDir,Alt text for overridden,Active,false,,,originalFull,[foo; bar] + id,name,sourcePath,imageType,targetFolder,alternateText,targetAttachmentId,status,skip,skipPlaceholder,skipReason,originalName (read-only),originLocations (read-only) + empty,,,,,,,Active,false,,,,[] + full,full,sourcePath,Jpeg,targetDir,Alt text for full,,Active,true,placeholder,reason,,[foo; bar] + with-target-attachment,with-target,,Png,,,att123,Active,false,,,,[] + overridden empty,,,,,,,Active,false,,,,[] + overridden full,full,sourcePath,Gif,targetDir,Alt text for overridden,,Active,false,,,originalFull,[foo; bar] """.stripIndent() Assertions.assertEquals(expected, mappingFile.toFile().text.replaceAll("\\r\\n|\\r", "\n")) } diff --git a/migration-examples/src/test/groovy/ImagesMappingImportTest.groovy b/migration-examples/src/test/groovy/ImagesMappingImportTest.groovy index 3d6749a..6e71269 100644 --- a/migration-examples/src/test/groovy/ImagesMappingImportTest.groovy +++ b/migration-examples/src/test/groovy/ImagesMappingImportTest.groovy @@ -13,7 +13,7 @@ import static org.mockito.Mockito.* class ImagesMappingImportTest { @TempDir - java.io.File dir + File dir @Test void overridesImageName() { @@ -35,11 +35,11 @@ class ImagesMappingImportTest { ImagesImport.run(migration, mappingFile) - verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("unchanged") - verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image("keptName", null, null, null, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image("keptName", null, null, null, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("kept") - verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image("someName", null, null, null, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image("someName", null, null, null, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("overridden") } @@ -63,11 +63,11 @@ class ImagesMappingImportTest { ImagesImport.run(migration, mappingFile) - verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("unchanged") - verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, "keptFolder", null, null, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, "keptFolder", null, null, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("kept") - verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, "overrideFolder", null, null, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, "overrideFolder", null, null, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("overridden") } @@ -91,11 +91,11 @@ class ImagesMappingImportTest { ImagesImport.run(migration, mappingFile) - verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("unchanged") - verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, "keptPath", null, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, "keptPath", null, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("kept") - verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, "overridePath", null, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, "overridePath", null, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("overridden") } @@ -119,11 +119,11 @@ class ImagesMappingImportTest { ImagesImport.run(migration, mappingFile) - verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("unchanged") - verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, null, ImageType.Jpeg, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, null, ImageType.Jpeg, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("kept") - verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, null, ImageType.Gif, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, null, ImageType.Gif, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("overridden") } @@ -147,11 +147,11 @@ class ImagesMappingImportTest { ImagesImport.run(migration, mappingFile) - verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("unchanged") - verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, null, ImageType.Jpeg, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, null, ImageType.Jpeg, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("kept") - verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, null, ImageType.Gif, new SkipOptions(true, "placeholder", "reason"), null)) + verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, null, ImageType.Gif, new SkipOptions(true, "placeholder", "reason"), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("overridden") } @@ -175,16 +175,44 @@ class ImagesMappingImportTest { ImagesImport.run(migration, mappingFile) - verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null, null)) verify(migration.mappingRepository, times(1)).applyImageMapping("unchanged") - verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), "keptAltText")) + verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), "keptAltText", null)) verify(migration.mappingRepository, times(1)).applyImageMapping("kept") - verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), "overriddenAltText")) + verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), "overriddenAltText", null)) + verify(migration.mappingRepository, times(1)).applyImageMapping("overridden") + } + + @Test + void overridesTargetAttachmentId() { + def migration = Utils.mockMigration() + Path mappingFile = Paths.get(dir.path, "testProject.csv") + def input = """\ + id,name,sourcePath,imageType,targetFolder,alternateText,targetAttachmentId,status,originLocations + unchanged,,,,,,,Active,[] + kept,,,,,,att123,Active,[] + overridden,,,,,,att456,Active,[] + """.stripIndent() + mappingFile.toFile().write(input) + givenExistingImage(migration, "unchanged", null, null, null, null) + givenExistingImageMapping(migration, "unchanged", null, null, null, null) + givenExistingImage(migration, "kept", null, null, null, null) + givenExistingImageMapping(migration, "kept", null, null, null, null) + givenExistingImage(migration, "overridden", null, null, null, null) + givenExistingImageMapping(migration, "overridden", null, null, null, null) + + ImagesImport.run(migration, mappingFile) + + verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null, null)) + verify(migration.mappingRepository, times(1)).applyImageMapping("unchanged") + verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null, "att123")) + verify(migration.mappingRepository, times(1)).applyImageMapping("kept") + verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null, "att456")) verify(migration.mappingRepository, times(1)).applyImageMapping("overridden") } static void givenExistingImage(Migration mig, String id, String name, String targetFolder, String sourcePath, ImageType imageType) { - when(mig.imageRepository.find(id)).thenReturn(new Image(id, name, [], new CustomFieldMap([:]), sourcePath, null, imageType, targetFolder, [:], new SkipOptions(false, null, null), null)) + when(mig.imageRepository.find(id)).thenReturn(new Image(id, name, [], new CustomFieldMap([:]), sourcePath, null, imageType, targetFolder, [:], new SkipOptions(false, null, null), null, null)) } static void givenExistingImageMapping(Migration mig, @@ -194,6 +222,6 @@ class ImagesMappingImportTest { String sourcePath, ImageType imageType) { when(mig.mappingRepository.getImageMapping(id)) - .thenReturn(new MappingItem.Image(name, targetFolder, sourcePath, imageType, null, null)) + .thenReturn(new MappingItem.Image(name, targetFolder, sourcePath, imageType, null, null, null)) } } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Attachment.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Attachment.kt index 52c7e2d..338435e 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Attachment.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Attachment.kt @@ -13,6 +13,7 @@ data class Attachment @JvmOverloads constructor( var targetFolder: String?, var attachmentType: AttachmentType, val skip: SkipOptions, + var targetImageId: String? = null, override var created: Instant? = null, override var lastUpdated: Instant? = null, ) : MigrationObject, RefValidatable { diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Image.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Image.kt index 1996805..f1c6a4b 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Image.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Image.kt @@ -18,6 +18,7 @@ data class Image @JvmOverloads constructor( val metadata: Map>, val skip: SkipOptions, var alternateText: String? = null, + var targetAttachmentId: String? = null, override var created: Instant? = null, override var lastUpdated: Instant? = null, ) : MigrationObject, RefValidatable { diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Mapping.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Mapping.kt index e3dff02..cac9928 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Mapping.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Mapping.kt @@ -31,6 +31,7 @@ sealed class MappingItem { var imageType: ImageType?, var skip: SkipOptions? = null, var alternateText: String? = null, + var targetAttachmentId: String? = null, ) : MappingItem() data class Attachment( @@ -39,6 +40,7 @@ sealed class MappingItem { var sourcePath: String?, var attachmentType: AttachmentType?, var skip: SkipOptions? = null, + var targetImageId: String? = null, ) : MappingItem() data class ParagraphStyle(override var name: String?, var definition: Definition?) : MappingItem() { @@ -110,6 +112,7 @@ sealed class MappingItem { imageType = this.imageType, skip = this.skip, alternateText = this.alternateText, + targetAttachmentId = this.targetAttachmentId, ) } @@ -120,6 +123,7 @@ sealed class MappingItem { sourcePath = this.sourcePath, attachmentType = this.attachmentType, skip = this.skip, + targetImageId = this.targetImageId, ) } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Ref.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Ref.kt index 10ce226..f638f3b 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Ref.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Ref.kt @@ -100,7 +100,9 @@ data class DisplayRuleRef(override val id: String) : Ref { fun toDb() = DisplayRuleEntityRef(id) } -data class ImageRef(override val id: String) : Ref, DocumentContent, TextContent, RefValidatable { +sealed interface ResourceRef : Ref, DocumentContent, TextContent, RefValidatable + +data class ImageRef(override val id: String) : ResourceRef { companion object { fun fromDb(entity: ImageEntityRef) = ImageRef(entity.id) } @@ -108,7 +110,7 @@ data class ImageRef(override val id: String) : Ref, DocumentContent, TextContent fun toDb() = ImageEntityRef(id) } -data class AttachmentRef(override val id: String) : Ref, DocumentContent, TextContent, RefValidatable { +data class AttachmentRef(override val id: String) : ResourceRef { companion object { fun fromDb(entity: AttachmentEntityRef) = AttachmentRef(entity.id) } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/AttachmentBuilder.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/AttachmentBuilder.kt index 8c9b9fc..0b6da06 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/AttachmentBuilder.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/AttachmentBuilder.kt @@ -11,6 +11,7 @@ class AttachmentBuilder(id: String) : DtoBuilderBase(id) { var placeholder: String? = null var reason: String? = null var alternateText: String? = null + var targetAttachmentId: String? = null /** * Sets source path of the image. This path is relative to the storage root folder. @@ -84,6 +85,14 @@ class ImageBuilder(id: String) : DtoBuilderBase(id) { */ fun alternateText(alternateText: String) = apply { this.alternateText = alternateText } + /** + * Sets the target attachment ID for alias resolution. + * When set, this image reference will resolve to the specified attachment. + * @param targetAttachmentId the ID of the attachment to resolve to + * @return the builder instance for chaining + */ + fun targetAttachmentId(targetAttachmentId: String) = apply { this.targetAttachmentId = targetAttachmentId } + /** * Builds the Image instance with the provided properties. * @return the built Image instance @@ -101,6 +110,7 @@ class ImageBuilder(id: String) : DtoBuilderBase(id) { metadata = metadata, skip = SkipOptions(skipped = skip, reason = reason, placeholder = placeholder), alternateText = alternateText, + targetAttachmentId = targetAttachmentId, ) } } \ No newline at end of file diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/repository/AttachmentRepository.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/repository/AttachmentRepository.kt index a0f9534..4dd1385 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/repository/AttachmentRepository.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/repository/AttachmentRepository.kt @@ -31,6 +31,7 @@ class AttachmentRepository(table: AttachmentTable, projectName: String) : Reposi targetFolder = row[AttachmentTable.targetFolder], attachmentType = AttachmentType.valueOf(row[AttachmentTable.attachmentType]), skip = row[AttachmentTable.skip], + targetImageId = row[AttachmentTable.targetImageId], ) } @@ -64,6 +65,7 @@ class AttachmentRepository(table: AttachmentTable, projectName: String) : Reposi it[AttachmentTable.targetFolder] = dto.targetFolder it[AttachmentTable.attachmentType] = dto.attachmentType.name it[AttachmentTable.skip] = dto.skip + it[AttachmentTable.targetImageId] = dto.targetImageId }.first() } } @@ -73,7 +75,7 @@ class AttachmentRepository(table: AttachmentTable, projectName: String) : Reposi val columns = listOf( "id", "project_name", "name", "origin_locations", "custom_fields", - "created", "last_updated", "source_path", "target_folder", "attachment_type", "skip" + "created", "last_updated", "source_path", "target_folder", "attachment_type", "skip", "target_image_id" ) val sql = createSql(columns, dtos.size) val now = Clock.System.now() @@ -99,6 +101,7 @@ class AttachmentRepository(table: AttachmentTable, projectName: String) : Reposi stmt.setString(index++, dto.targetFolder) stmt.setString(index++, dto.attachmentType.name) stmt.setObject(index++, Json.encodeToString(dto.skip), Types.OTHER) + stmt.setString(index++, dto.targetImageId) } stmt.executeUpdate() diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/repository/ImageRepository.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/repository/ImageRepository.kt index 47a004e..0967e6a 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/repository/ImageRepository.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/repository/ImageRepository.kt @@ -35,6 +35,7 @@ class ImageRepository(table: ImageTable, projectName: String) : Repository("skip", Json) + val targetImageId = varchar("target_image_id", 255).nullable() } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/persistence/table/ImageTable.kt b/migration-library/src/main/kotlin/com/quadient/migration/persistence/table/ImageTable.kt index 3ae4f2c..08b1e35 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/persistence/table/ImageTable.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/persistence/table/ImageTable.kt @@ -14,4 +14,5 @@ object ImageTable : MigrationObjectTable("image") { val metadata = jsonb>>("metadata", Json) val skip = jsonb("skip", Json) val alternateText = varchar("alternate_text", 255).nullable() + val targetAttachmentId = varchar("target_attachment_id", 255).nullable() } \ No newline at end of file diff --git a/migration-library/src/main/kotlin/com/quadient/migration/persistence/upgrade/V10__add_file_table.kt b/migration-library/src/main/kotlin/com/quadient/migration/persistence/upgrade/V10__add_file_table.kt index 16b62ee..9f6ea1b 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/persistence/upgrade/V10__add_file_table.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/persistence/upgrade/V10__add_file_table.kt @@ -5,11 +5,19 @@ import org.flywaydb.core.api.migration.BaseJavaMigration import org.flywaydb.core.api.migration.Context import org.jetbrains.exposed.v1.jdbc.SchemaUtils import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import java.sql.Connection class V10__add_attachment_table : BaseJavaMigration() { override fun migrate(context: Context) { transaction { SchemaUtils.create(AttachmentTable) } + + val connection: Connection = context.connection + val stmt = connection.prepareStatement( + "ALTER TABLE image ADD COLUMN IF NOT EXISTS target_attachment_id varchar(255)" + ) + stmt.executeUpdate() + stmt.close() } } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/service/DeployPhaseUtils.kt b/migration-library/src/main/kotlin/com/quadient/migration/service/DeployPhaseUtils.kt index 173b5e7..3584f5c 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/service/DeployPhaseUtils.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/service/DeployPhaseUtils.kt @@ -1,11 +1,42 @@ package com.quadient.migration.service import com.quadient.migration.api.ProjectConfig +import com.quadient.migration.api.dto.migrationmodel.Attachment +import com.quadient.migration.api.dto.migrationmodel.AttachmentRef import com.quadient.migration.api.dto.migrationmodel.Image +import com.quadient.migration.api.dto.migrationmodel.ImageRef +import com.quadient.migration.api.dto.migrationmodel.ResourceRef +import com.quadient.migration.api.repository.Repository import com.quadient.migration.shared.IcmPath import com.quadient.migration.shared.ImageType import com.quadient.migration.shared.isNullOrBlank import com.quadient.migration.shared.toIcmPath +import org.slf4j.LoggerFactory + +private val logger = LoggerFactory.getLogger("com.quadient.migration.service.DeployPhaseUtils") + +fun resolveAlias( + ref: ResourceRef, + imageRepository: Repository, + attachmentRepository: Repository +): ResourceRef { + return when (ref) { + is ImageRef -> { + val image = imageRepository.find(ref.id) ?: return ref + image.targetAttachmentId?.let { targetId -> + logger.info("Resolving image '${ref.id}' to attachment '$targetId' via alias") + AttachmentRef(targetId) + } ?: ref + } + is AttachmentRef -> { + val attachment = attachmentRepository.find(ref.id) ?: return ref + attachment.targetImageId?.let { targetId -> + logger.info("Resolving attachment '${ref.id}' to image '$targetId' via alias") + ImageRef(targetId) + } ?: ref + } + } +} fun resolveTargetDir(defaultTargetFolder: IcmPath? = null, specificTargetFolder: IcmPath? = null): IcmPath? { return when { diff --git a/migration-library/src/main/kotlin/com/quadient/migration/service/deploy/DeployClient.kt b/migration-library/src/main/kotlin/com/quadient/migration/service/deploy/DeployClient.kt index e2e051e..5c4e653 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/service/deploy/DeployClient.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/service/deploy/DeployClient.kt @@ -15,6 +15,7 @@ import com.quadient.migration.api.dto.migrationmodel.Image import com.quadient.migration.api.dto.migrationmodel.ImageRef import com.quadient.migration.api.dto.migrationmodel.ParagraphStyleRef import com.quadient.migration.api.dto.migrationmodel.Ref +import com.quadient.migration.api.dto.migrationmodel.ResourceRef import com.quadient.migration.api.dto.migrationmodel.TextStyleRef import com.quadient.migration.api.dto.migrationmodel.VariableRef import com.quadient.migration.api.dto.migrationmodel.VariableStructureRef @@ -26,6 +27,7 @@ import com.quadient.migration.service.Storage import com.quadient.migration.service.inspirebuilder.InspireDocumentObjectBuilder import com.quadient.migration.service.ipsclient.IpsService import com.quadient.migration.service.ipsclient.OperationResult +import com.quadient.migration.service.resolveAlias import com.quadient.migration.shared.DocumentObjectType import com.quadient.migration.shared.IcmFileMetadata import com.quadient.migration.shared.ImageType @@ -235,24 +237,21 @@ sealed class DeployClient( val deploymentResult = DeploymentResult(deploymentId) val tracker = ResultTracker(statusTrackingRepository, deploymentResult, deploymentId, deploymentTimestamp, output) - val allRefs = documentObjects.map { + val allResourceRefs = documentObjects.flatMap { try { - it.getAllDocumentObjectImageAndAttachmentRefs() + it.getAllDocumentObjectResourceRefs() } catch (e: IllegalStateException) { deploymentResult.errors.add(DeploymentError(it.id, e.message ?: "")) - Pair(emptyList(), emptyList()) + emptyList() } - } - - val imageRefs = allRefs.flatMap { pair -> pair.first }.distinct() - val attachmentRefs = allRefs.flatMap { pair -> pair.second }.distinct() - - for (imageRef in imageRefs) { - deployImage(imageRef, deploymentResult, tracker) - } + }.map { resolveAlias(it, imageRepository, attachmentRepository) } + .distinct() - for (attachmentRef in attachmentRefs) { - deployAttachment(attachmentRef, deploymentResult, tracker) + for (resourceRef in allResourceRefs) { + when (resourceRef) { + is ImageRef -> deployImage(resourceRef, deploymentResult, tracker) + is AttachmentRef -> deployAttachment(resourceRef, deploymentResult, tracker) + } } return deploymentResult @@ -568,29 +567,25 @@ sealed class DeployClient( return dependencies } - private fun DocumentObject.getAllDocumentObjectImageAndAttachmentRefs(): Pair, List> { - val images = mutableListOf() - val attachments = mutableListOf() + private fun DocumentObject.getAllDocumentObjectResourceRefs(): List { + val resources = mutableListOf() this.collectRefs().forEach { ref -> when (ref) { is DisplayRuleRef, is TextStyleRef, is ParagraphStyleRef, is VariableRef, is VariableStructureRef -> {} - is ImageRef -> images.add(ref) - is AttachmentRef -> attachments.add(ref) + is ResourceRef -> resources.add(ref) is DocumentObjectRef -> { val model = documentObjectRepository.find(ref.id) - ?: error("Unable to collect image or attachment references because inner document object '${ref.id}' was not found.") + ?: error("Unable to collect resource references because inner document object '${ref.id}' was not found.") if (documentObjectBuilder.shouldIncludeInternalDependency(model)) { - val (nestedImages, nestedAttachments) = model.getAllDocumentObjectImageAndAttachmentRefs() - images.addAll(nestedImages) - attachments.addAll(nestedAttachments) + resources.addAll(model.getAllDocumentObjectResourceRefs()) } } } } - return Pair(images, attachments) + return resources } private fun getLastDeployEvent(): LastDeployment? { diff --git a/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilder.kt b/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilder.kt index 7856187..544e679 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilder.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilder.kt @@ -18,9 +18,10 @@ import com.quadient.migration.api.dto.migrationmodel.Paragraph import com.quadient.migration.api.dto.migrationmodel.Paragraph.Text import com.quadient.migration.api.dto.migrationmodel.ParagraphStyleDefinition import com.quadient.migration.api.dto.migrationmodel.ParagraphStyleRef +import com.quadient.migration.api.dto.migrationmodel.ResourceRef import com.quadient.migration.api.dto.migrationmodel.SelectByLanguage import com.quadient.migration.api.dto.migrationmodel.StringValue -import com.quadient.migration.api.dto.migrationmodel.Table as TableDTO +import com.quadient.migration.api.dto.migrationmodel.Table import com.quadient.migration.api.dto.migrationmodel.TextStyleDefinition import com.quadient.migration.api.dto.migrationmodel.TextStyleRef import com.quadient.migration.api.dto.migrationmodel.Variable @@ -34,6 +35,7 @@ import com.quadient.migration.service.inspirebuilder.InspireDocumentObjectBuilde import com.quadient.migration.service.inspirebuilder.InspireDocumentObjectBuilder.ScriptResult import com.quadient.migration.service.inspirebuilder.InspireDocumentObjectBuilder.ScriptResult.* import com.quadient.migration.service.ipsclient.IpsService +import com.quadient.migration.service.resolveAlias import com.quadient.migration.shared.Alignment import com.quadient.migration.shared.BinOp import com.quadient.migration.shared.Binary @@ -142,7 +144,7 @@ abstract class InspireDocumentObjectBuilder( collectLanguagesFromContent(item.default) } - is TableDTO -> item.rows.forEach { row -> + is Table -> item.rows.forEach { row -> row.cells.forEach { cell -> collectLanguagesFromContent(cell.content) } } @@ -161,7 +163,7 @@ abstract class InspireDocumentObjectBuilder( collectLanguagesFromContent(textContent.default) } - is TableDTO -> textContent.rows.forEach { row -> + is Table -> textContent.rows.forEach { row -> row.cells.forEach { cell -> collectLanguagesFromContent(cell.content) } } @@ -268,13 +270,18 @@ abstract class InspireDocumentObjectBuilder( flowName: String? = null, languages: List, ): List { - val mutableContent = content.toMutableList() + val mutableContent = content.map { item -> + when (item) { + is ResourceRef -> resolveAlias(item, imageRepository, attachmentRepository) + else -> item + } + }.toMutableList() var idx = 0 val flowModels = mutableListOf() while (idx < mutableContent.size) { when (val contentPart = mutableContent[idx]) { - is TableDTO, is Paragraph, is ImageRef -> { + is Table, is Paragraph, is ImageRef -> { val flowParts = gatherFlowParts(mutableContent, idx) idx += flowParts.size - 1 flowModels.add(Composite(flowParts)) @@ -657,7 +664,7 @@ abstract class InspireDocumentObjectBuilder( documentContentModelParts.forEach { when (it) { is Paragraph -> buildParagraph(layout, variableStructure, flow, it, languages) - is TableDTO -> flow.addParagraph().addText() + is Table -> flow.addParagraph().addText() .appendTable(buildTable(layout, variableStructure, it, languages)) is ImageRef -> buildAndAppendImage(layout, flow.addParagraph().addText(), it) @@ -722,7 +729,7 @@ abstract class InspireDocumentObjectBuilder( do { val contentPart = content[index] - if (contentPart is TableDTO || contentPart is Paragraph || contentPart is ImageRef) { + if (contentPart is Table || contentPart is Paragraph || contentPart is ImageRef) { flowParts.add(contentPart) index++ } else { @@ -768,25 +775,29 @@ abstract class InspireDocumentObjectBuilder( var currentText = baseText - textModel.content.forEach { - when (it) { - is StringValue -> currentText.appendText(it.value) - is VariableRef -> currentText.appendVariable(it, layout, variableStructure) - is TableDTO -> currentText.appendTable(buildTable(layout, variableStructure, it, languages)) + textModel.content.forEach { textContent -> + when (textContent) { + is ResourceRef -> { + when (val resolved = resolveAlias(textContent, imageRepository, attachmentRepository)) { + is ImageRef -> buildAndAppendImage(layout, currentText, resolved) + is AttachmentRef -> buildAttachmentRef(layout, resolved)?.also { flow -> + currentText.appendFlow(flow) + } + } + } + + is StringValue -> currentText.appendText(textContent.value) + is VariableRef -> currentText.appendVariable(textContent, layout, variableStructure) + is Table -> currentText.appendTable(buildTable(layout, variableStructure, textContent, languages)) is DocumentObjectRef -> buildDocumentObjectRef( - layout, variableStructure, it, languages + layout, variableStructure, textContent, languages )?.also { flow -> currentText.appendFlow(flow) } - is AttachmentRef -> buildAttachmentRef(layout, it)?.also { flow -> - currentText.appendFlow(flow) - } - - is ImageRef -> buildAndAppendImage(layout, currentText, it) - is Hyperlink -> currentText = buildAndAppendHyperlink(layout, paragraph, baseTextStyleModel, it) + is Hyperlink -> currentText = buildAndAppendHyperlink(layout, paragraph, baseTextStyleModel, textContent) is FirstMatch -> currentText.appendFlow( - buildFirstMatch(layout, variableStructure, it, true, null, languages) + buildFirstMatch(layout, variableStructure, textContent, true, null, languages) ) } } @@ -919,7 +930,7 @@ abstract class InspireDocumentObjectBuilder( } private fun buildTable( - layout: Layout, variableStructure: VariableStructure, model: TableDTO, languages: List + layout: Layout, variableStructure: VariableStructure, model: Table, languages: List ): WfdXmlTable { val table = layout.addTable().setDisplayAsImage(false) diff --git a/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/DesignerDeployClientTest.kt b/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/DesignerDeployClientTest.kt index a2a1162..eb4ef31 100644 --- a/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/DesignerDeployClientTest.kt +++ b/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/DesignerDeployClientTest.kt @@ -404,6 +404,7 @@ class DesignerDeployClientTest { every { statusTrackingRepository.error(any(), any(), any(), any(), any(), any(), any(), any()) } returns aErrorStatus("id") every { statusTrackingRepository.active(any(), any()) } returns aActiveStatus("id") every { documentObjectBuilder.getDocumentObjectPath(any()) } returns "icm://path" + every { imageRepository.find(any()) } returns null } @Test @@ -426,7 +427,7 @@ class DesignerDeployClientTest { // then verify(exactly = 0) { documentObjectBuilder.buildDocumentObject(any(), any()) } - verify(exactly = 0) { imageRepository.find(any()) } + verify(exactly = 3) { imageRepository.find(any()) } } @Test diff --git a/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/InteractiveDeployClientTest.kt b/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/InteractiveDeployClientTest.kt index c38f857..da74000 100644 --- a/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/InteractiveDeployClientTest.kt +++ b/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/InteractiveDeployClientTest.kt @@ -806,6 +806,7 @@ class InteractiveDeployClientTest { every { ipsService.setProductionApprovalState(any()) } returns OperationResult.Success every { ipsService.tryUpload(any(), any()) } returns OperationResult.Success every { ipsService.deployJld(any(), any(), any(), any(), any()) } returns OperationResult.Success + every { imageRepository.find(any()) } returns null } @Test @@ -828,7 +829,7 @@ class InteractiveDeployClientTest { // then verify(exactly = 0) { documentObjectBuilder.buildDocumentObject(any(), any()) } - verify(exactly = 0) { imageRepository.find(any()) } + verify(exactly = 3) { imageRepository.find(any()) } } @Test diff --git a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilderTest.kt b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilderTest.kt index 11e8aed..e72ed29 100644 --- a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilderTest.kt +++ b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilderTest.kt @@ -10,6 +10,9 @@ import com.quadient.migration.api.dto.migrationmodel.Hyperlink import com.quadient.migration.api.dto.migrationmodel.StringValue import com.quadient.migration.api.dto.migrationmodel.TextStyle import com.quadient.migration.api.dto.migrationmodel.TextStyleRef +import com.quadient.migration.api.dto.migrationmodel.builder.AttachmentBuilder +import com.quadient.migration.api.dto.migrationmodel.builder.DocumentObjectBuilder +import com.quadient.migration.api.dto.migrationmodel.builder.ImageBuilder import com.quadient.migration.api.repository.AttachmentRepository import com.quadient.migration.api.repository.DisplayRuleRepository import com.quadient.migration.api.repository.DocumentObjectRepository @@ -20,7 +23,9 @@ import com.quadient.migration.api.repository.VariableRepository import com.quadient.migration.api.repository.VariableStructureRepository import com.quadient.migration.service.ipsclient.IpsService import com.quadient.migration.shared.BinOp +import com.quadient.migration.shared.DocumentObjectType import com.quadient.migration.shared.Function +import com.quadient.migration.shared.ImageType.* import com.quadient.migration.shared.Literal import com.quadient.migration.shared.LiteralDataType import com.quadient.migration.shared.Size @@ -29,12 +34,14 @@ import com.quadient.migration.tools.aProjectConfig import com.quadient.migration.tools.getFlowAreaContentFlow import com.quadient.migration.tools.model.* import com.quadient.migration.tools.shouldBeEqualTo +import com.quadient.migration.tools.shouldNotBeNull import com.quadient.wfdxml.api.layoutnodes.TextStyleInheritFlag import com.quadient.wfdxml.internal.module.layout.LayoutImpl import io.mockk.every import io.mockk.mockk import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import kotlin.collections.last class InspireDocumentObjectBuilderTest { private val documentObjectRepository = mockk() @@ -168,8 +175,7 @@ class InspireDocumentObjectBuilderTest { @Test fun `attachment reference creates DirectExternal flow with correct structure`() { // given - val attachment = aAttachment("Attachment_1", name = "document", sourcePath = "C:/attachments/document.pdf") - every { attachmentRepository.findOrFail(attachment.id) } returns attachment + val attachment = mockAttachment(aAttachment("Attachment_1", name = "document", sourcePath = "C:/attachments/document.pdf")) val block = mockObj( aBlock("B_1", listOf(aParagraph(aText(listOf(StringValue("See attached: "), AttachmentRef(attachment.id)))))) ) @@ -191,8 +197,7 @@ class InspireDocumentObjectBuilderTest { @Test fun `attachment reference with skip and placeholder creates simple flow with placeholder text`() { // given - val attachment = aAttachment("Attachment_1", skip = SkipOptions(true, "Attachment not available", "Missing source")) - every { attachmentRepository.findOrFail(attachment.id) } returns attachment + val attachment = mockAttachment(aAttachment("Attachment_1", skip = SkipOptions(true, "Attachment not available", "Missing source"))) val block = mockObj( aBlock("B_1", listOf(aParagraph(aText(listOf(AttachmentRef(attachment.id)))))) ) @@ -233,6 +238,57 @@ class InspireDocumentObjectBuilderTest { flow["FlowContent"]["P"]["T"][""].textValue().shouldBeEqualTo("Text more text") } + @Test + fun `image reference with targetAttachmentId resolves to attachment in wfd-xml`() { + // given + val targetAttachment = mockAttachment( + AttachmentBuilder("Attachment_Target").name("resolved").sourcePath("C:/attachments/resolved.pdf").build() + ) + val image = mockImage( + ImageBuilder("Image_1").sourcePath("C:/images/original.png") + .imageType(Png).targetAttachmentId(targetAttachment.id).build() + ) + val block = + mockObj(DocumentObjectBuilder("B_1", DocumentObjectType.Block).string("Image: ").imageRef(image.id).build()) + + // when + val result = + subject.buildDocumentObject(block, null).let { xmlMapper.readTree(it.trimIndent()) }["Layout"]["Layout"] + + // then + val flowId = result["FlowArea"].last()["FlowId"].textValue() + val flowAreaFlow = result["Flow"].last { it["Id"].textValue() == flowId } + val refIds = flowAreaFlow["FlowContent"]["P"]["T"]["O"] + val attachmentFlow = result["Flow"].last { it["Id"].textValue() == refIds[1]["Id"].textValue() } + + attachmentFlow["Type"].textValue().shouldBeEqualTo("DirectExternal") + attachmentFlow["ExternalLocation"].textValue().shouldBeEqualTo("icm://resolved.pdf") + } + + @Test + fun `attachment reference with targetImageId resolves to image in wfd-xml`() { + // given + val targetImage = mockImage( + ImageBuilder("Image_Target").name("resolved").sourcePath("C:/images/resolved.png").imageType(Png).build() + ) + val attachment = mockAttachment( + AttachmentBuilder("Attachment_1").sourcePath("C:/attachments/original.pdf").targetImageId(targetImage.id) + .build() + ) + val block = mockObj( + DocumentObjectBuilder("B_1", DocumentObjectType.Block).attachmentRef(attachment.id).build() + ) + + // when + val result = + subject.buildDocumentObject(block, null).let { xmlMapper.readTree(it.trimIndent()) }["Layout"]["Layout"] + + // then + val contentFlow = getFlowAreaContentFlow(result) + val image = result["Image"].last { it["Id"].textValue() == contentFlow["FlowContent"]["P"]["T"]["O"]["Id"].textValue() } + image["ImageLocation"].textValue().shouldBeEqualTo("VCSLocation,icm://resolved.png") + } + private fun mockObj(documentObject: DocumentObject): DocumentObject { every { documentObjectRepository.findOrFail(documentObject.id) } returns documentObject return documentObject @@ -240,6 +296,7 @@ class InspireDocumentObjectBuilderTest { private fun mockAttachment(attachment: Attachment): Attachment { every { attachmentRepository.findOrFail(attachment.id) } returns attachment + every { attachmentRepository.find(attachment.id) } returns attachment return attachment } @@ -250,6 +307,12 @@ class InspireDocumentObjectBuilderTest { return textStyle } + private fun mockImage(image: com.quadient.migration.api.dto.migrationmodel.Image): com.quadient.migration.api.dto.migrationmodel.Image { + every { imageRepository.findOrFail(image.id) } returns image + every { imageRepository.find(image.id) } returns image + return image + } + @Test fun `simple display rule with single expression`() { val rule = aDisplayRule( diff --git a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilderTest.kt b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilderTest.kt index b06f258..123eaa8 100644 --- a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilderTest.kt +++ b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilderTest.kt @@ -725,11 +725,9 @@ class InteractiveDocumentObjectBuilderTest { @Test fun `build block with image`() { // given - val image = aImage("Dog", options = ImageOptions(Size.ofPoints(120), Size.ofPoints(90))) + val image = mockImage(aImage("Dog", options = ImageOptions(Size.ofPoints(120), Size.ofPoints(90)))) val block = aBlock("1", listOf(ImageRef(image.id))) - every { imageRepository.findOrFail(image.id) } returns image - // when val result = subject.buildDocumentObject(block, null).let { xmlMapper.readTree(it.trimIndent()) } @@ -748,11 +746,9 @@ class InteractiveDocumentObjectBuilderTest { @Test fun `build block with image uses alternateText from Image`() { // given - val image = aImage("Dog", alternateText = "A cute dog picture") + val image = mockImage(aImage("Dog", alternateText = "A cute dog picture")) val block = aBlock("1", listOf(ImageRef(image.id))) - every { imageRepository.findOrFail(image.id) } returns image - // when val result = subject.buildDocumentObject(block, null).let { xmlMapper.readTree(it.trimIndent()) } @@ -798,16 +794,14 @@ class InteractiveDocumentObjectBuilderTest { @Test fun `build block with unknown image and image with missing source path renders placeholder texts instead`() { // given - val catImage = aImage("Cat", imageType = ImageType.Unknown, skip = SkipOptions(true, "Cat placeholder", null)) - val dogImage = aImage("Dog", sourcePath = "", skip = SkipOptions(true, "Dog placeholder", null)) + val catImage = + mockImage(aImage("Cat", imageType = ImageType.Unknown, skip = SkipOptions(true, "Cat placeholder", null))) + val dogImage = mockImage(aImage("Dog", sourcePath = "", skip = SkipOptions(true, "Dog placeholder", null))) val block = aBlock( "1", listOf(ImageRef(catImage.id), aParagraph(aText(ImageRef(dogImage.id)))) ) - every { imageRepository.findOrFail(catImage.id) } returns catImage - every { imageRepository.findOrFail(dogImage.id) } returns dogImage - // when val result = subject.buildDocumentObject(block, null).let { xmlMapper.readTree(it.trimIndent()) } @@ -1460,6 +1454,7 @@ class InteractiveDocumentObjectBuilderTest { private fun mockImage(image: Image): Image { every { imageRepository.findOrFail(image.id) } returns image + every { imageRepository.find(image.id) } returns image return image }