From 9a977a24811ba997bd36c8819607d8819e926623 Mon Sep 17 00:00:00 2001 From: "d.svitak" Date: Thu, 6 Aug 2026 15:55:53 +0200 Subject: [PATCH 1/2] MIG-585 Areas import - introducing index into the areas/layout mappings to be resistant to sorting and other potential discrepancies --- migration-examples/layout/index.html | 14 +++- .../common/mapping/LayoutExport.groovy | 9 ++- .../common/mapping/LayoutImport.groovy | 62 ++++++++------- .../migration/example/common/util/Csv.groovy | 3 + .../src/test/groovy/LayoutExportTest.groovy | 36 ++++----- .../src/test/groovy/LayoutImportTest.groovy | 78 +++++++++++++------ 6 files changed, 126 insertions(+), 76 deletions(-) diff --git a/migration-examples/layout/index.html b/migration-examples/layout/index.html index 4708347b..a7082ef6 100644 --- a/migration-examples/layout/index.html +++ b/migration-examples/layout/index.html @@ -775,6 +775,10 @@ const w = parseSizeMm(col(row, 'width')); const h = parseSizeMm(col(row, 'height')); if (x == null || y == null || w == null || h == null) return null; + const areaIndex = Number(col(row, 'areaIndex').trim()); + if (Number.isNaN(areaIndex)) { + throw new Error(`Row ${lineIndices[idx] + 1} is missing a valid areaIndex value.`); + } return { area: { x, y, w, h, @@ -783,8 +787,10 @@ contentPreview: col(row, 'contentPreview') || '', }, lineIndex: lineIndices[idx], + areaIndex, }; }).filter(a => a != null); + rawAreasWithLine.sort((a, b) => a.areaIndex - b.areaIndex); const rawAreas = /** @type {{x:number,y:number,w:number,h:number,flowToNextPage:boolean,interactiveFlowName:string,contentPreview:string}[]} */ ( rawAreasWithLine.map(a => a.area) ); @@ -949,6 +955,7 @@ baseRows.push([ baseTemplateId, baseTemplateName, basePageId, basePageName, pageWidthMm, pageHeightMm, + String(areaGroupIndex), flowName, String(flowToNextPage), `${draft.position.x}mm`, `${draft.position.y}mm`, `${draft.position.w}mm`, `${draft.position.h}mm`, "Base", "", "", @@ -1020,9 +1027,10 @@ baseRows.forEach(baseRow => { const byLogicalName = { templateId: baseRow[0], templateName: baseRow[1], pageId: baseRow[2], pageName: baseRow[3], - pageWidth: baseRow[4], pageHeight: baseRow[5], interactiveFlowName: baseRow[6], flowToNextPage: baseRow[7], - x: baseRow[8], y: baseRow[9], width: baseRow[10], height: baseRow[11], - type: baseRow[12], targetId: baseRow[13], contentPreview: baseRow[14], + pageWidth: baseRow[4], pageHeight: baseRow[5], areaIndex: baseRow[6], + interactiveFlowName: baseRow[7], flowToNextPage: baseRow[8], + x: baseRow[9], y: baseRow[10], width: baseRow[11], height: baseRow[12], + type: baseRow[13], targetId: baseRow[14], contentPreview: baseRow[15], }; const row = headers.map(h => byLogicalName[normalizeHeaderName(h)] ?? ""); rows.push(row); diff --git a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/LayoutExport.groovy b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/LayoutExport.groovy index 801e4743..01d686a2 100644 --- a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/LayoutExport.groovy +++ b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/LayoutExport.groovy @@ -46,6 +46,7 @@ static void run(Migration migration, Path path) { Mapping.displayHeader("pageName", true), Mapping.displayHeader("pageWidth", true), Mapping.displayHeader("pageHeight", true), + Mapping.displayHeader("areaIndex", true), Mapping.displayHeader("interactiveFlowName", false), Mapping.displayHeader("flowToNextPage", false), Mapping.displayHeader("x", true), @@ -91,8 +92,8 @@ static void run(Migration migration, Path path) { baseTemplates.each { baseTemplate -> baseTemplate.pages.eachWithIndex { page, pageIdx -> def pageId = "page-${pageIdx + 1}" - page.areas.each { area -> - writer.writeLine(buildBaseTemplateArea(baseTemplate, pageId, page, area)) + page.areas.eachWithIndex { area, areaIdx -> + writer.writeLine(buildBaseTemplateArea(baseTemplate, pageId, page, area, areaIdx)) } } } @@ -109,6 +110,7 @@ static String buildArea(Migration migration, Number idx, Area area, DocumentObje def pageOptions = page?.options instanceof PageOptions ? page.options as PageOptions : null builder.append(Csv.serialize(pageOptions?.width) + ",") builder.append(Csv.serialize(pageOptions?.height) + ",") + builder.append(Csv.serialize(idx) + ",") builder.append(Csv.serialize(area.interactiveFlowName) + ",") builder.append(Csv.serialize(area.flowToNextPage) + ",") builder.append(Csv.serialize(area.position.x) + ",") @@ -124,7 +126,7 @@ static String buildArea(Migration migration, Number idx, Area area, DocumentObje return builder.toString() } -static String buildBaseTemplateArea(BaseTemplate baseTemplate, String pageId, BaseTemplatePage page, BaseTemplateArea area) { +static String buildBaseTemplateArea(BaseTemplate baseTemplate, String pageId, BaseTemplatePage page, BaseTemplateArea area, Number idx) { def builder = new StringBuilder() builder.append(Csv.serialize(baseTemplate.id) + ",") builder.append(Csv.serialize(baseTemplate.name) + ",") @@ -132,6 +134,7 @@ static String buildBaseTemplateArea(BaseTemplate baseTemplate, String pageId, Ba builder.append(Csv.serialize(page.name) + ",") builder.append(Csv.serialize(page.pageWidth) + ",") builder.append(Csv.serialize(page.pageHeight) + ",") + builder.append(Csv.serialize(idx) + ",") builder.append(Csv.serialize(area.interactiveFlowName) + ",") builder.append(Csv.serialize(area.flowToNextPage) + ",") builder.append(Csv.serialize(area.position?.x) + ",") diff --git a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/LayoutImport.groovy b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/LayoutImport.groovy index c75d3ceb..d6b0352f 100644 --- a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/LayoutImport.groovy +++ b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/LayoutImport.groovy @@ -9,7 +9,6 @@ package com.quadient.migration.example.common.mapping import com.quadient.migration.api.Migration import com.quadient.migration.api.dto.migrationmodel.BaseTemplateLocation -import com.quadient.migration.api.dto.migrationmodel.DocumentObject import com.quadient.migration.api.dto.migrationmodel.builder.BaseTemplateBuilder import com.quadient.migration.api.dto.migrationmodel.MappingItem import com.quadient.migration.example.common.util.Csv @@ -36,13 +35,10 @@ static void run(Migration migration, Path path) { def fileLines = path.toFile().readLines() def columnNames = Csv.parseColumnNames(fileLines.removeFirst()).collect { Mapping.normalizeHeader(it) } - def areaMappings = new HashMap() + def areaMappings = new HashMap() def docObjectsToTargetIds = new LinkedHashMap() def baseTemplateDrafts = new LinkedHashMap() - DocumentObject currentDocumentObject = null - MappingItem.Area areaMapping = null - int areaIndex = 0 for (line in fileLines) { def values = Csv.getCells(line, columnNames) @@ -56,19 +52,16 @@ static void run(Migration migration, Path path) { def templateId = Csv.deserialize(values.get("templateId"), String.class) def documentObjectId = pageId ?: templateId - if (currentDocumentObject?.id != documentObjectId) { - if (currentDocumentObject != null) { - areaMappings[currentDocumentObject.id] = areaMapping - } - - def documentObjectModel = migration.documentObjectRepository.find(documentObjectId) - if (!documentObjectModel) { + def areaMapping = areaMappings.computeIfAbsent(documentObjectId) { + if (!migration.documentObjectRepository.find(documentObjectId)) { throw new IllegalStateException("Document object '${documentObjectId}' not found.") } + migration.mappingRepository.getAreaMapping(documentObjectId) + } - areaMapping = migration.mappingRepository.getAreaMapping(documentObjectId) - currentDocumentObject = documentObjectModel - areaIndex = 0 + def areaIndex = Csv.deserialize(values.get("areaIndex"), Integer.class) + if (areaIndex == null) { + throw new IllegalStateException("Row for document object '${documentObjectId}' is missing an areaIndex value.") } def interactiveFlowName = Csv.deserialize(values.get("interactiveFlowName"), String.class) @@ -86,12 +79,6 @@ static void run(Migration migration, Path path) { docObjectsToTargetIds[templateId] = targetId } } - - areaIndex++ - } - - if (currentDocumentObject != null) { - areaMappings[currentDocumentObject.id] = areaMapping } Mapping.upsertBatched(migration.mappingRepository, areaMappings, "area mappings", log) @@ -113,17 +100,20 @@ private static void assignAreaToBaseTemplateDraft(Map baseTemplateDraft.name = Csv.deserialize(values.get("templateName"), String.class) } - def page = baseTemplateDraft.pages.computeIfAbsent(pageGroupId) { - new BaseTemplateBuilder.Page() - .name(Csv.deserialize(values.get("pageName"), String.class)) - .pageWidth(Csv.deserialize(values.get("pageWidth"), Size.class)) - .pageHeight(Csv.deserialize(values.get("pageHeight"), Size.class)) + def pageDraft = baseTemplateDraft.pages.computeIfAbsent(pageGroupId) { + new BaseTemplatePageDraft(name: Csv.deserialize(values.get("pageName"), String.class), + pageWidth: Csv.deserialize(values.get("pageWidth"), Size.class), + pageHeight: Csv.deserialize(values.get("pageHeight"), Size.class)) } def interactiveFlowName = Csv.deserialize(values.get("interactiveFlowName"), String.class) if (!interactiveFlowName) { throw new IllegalStateException("Rows of type 'Base' must specify an interactiveFlowName for the consolidated area.") } + def areaIndex = Csv.deserialize(values.get("areaIndex"), Integer.class) + if (areaIndex == null) { + throw new IllegalStateException("Rows of type 'Base' must specify an areaIndex identifying the area's position.") + } def x = Csv.deserialize(values.get("x"), Size.class) def y = Csv.deserialize(values.get("y"), Size.class) def width = Csv.deserialize(values.get("width"), Size.class) @@ -131,7 +121,7 @@ private static void assignAreaToBaseTemplateDraft(Map def position = (x != null && y != null && width != null && height != null) ? new Position(x, y, width, height) : null def flowToNextPage = Csv.deserialize(values.get("flowToNextPage"), Boolean.class) ?: false - page.addArea(interactiveFlowName) + pageDraft.areasByIndex[areaIndex] = new BaseTemplateBuilder.Area(interactiveFlowName) .position(position) .flowToNextPage(flowToNextPage) } @@ -171,7 +161,14 @@ private static void applyBaseTemplateDraftMappings(Migration migration, Map + mapping.pages = draft.pages.values().collect { pageDraft -> + new BaseTemplateBuilder.Page() + .name(pageDraft.name) + .pageWidth(pageDraft.pageWidth) + .pageHeight(pageDraft.pageHeight) + .addAreas(pageDraft.areasByIndex.values() as List) + .build() + } as List mappings[baseTemplateId] = mapping } @@ -182,5 +179,12 @@ private static void applyBaseTemplateDraftMappings(Migration migration, Map pages = new LinkedHashMap<>() + Map pages = new LinkedHashMap<>() +} + +class BaseTemplatePageDraft { + String name + Size pageWidth + Size pageHeight + Map areasByIndex = new TreeMap<>() } diff --git a/migration-examples/src/main/groovy/com/quadient/migration/example/common/util/Csv.groovy b/migration-examples/src/main/groovy/com/quadient/migration/example/common/util/Csv.groovy index fb138349..c5573fd1 100644 --- a/migration-examples/src/main/groovy/com/quadient/migration/example/common/util/Csv.groovy +++ b/migration-examples/src/main/groovy/com/quadient/migration/example/common/util/Csv.groovy @@ -145,6 +145,9 @@ static T deserialize(String value, Class cls) { return (trimmed.isEmpty() ? null : trimmed) as T } case IcmPath: return IcmPath.from(value) as T + case Integer: + case int: + return value.trim().toInteger() as T case Color: return Color.fromHex(value) as T case Size: return Size.fromString(value) as T case BaseTemplateLocation: { diff --git a/migration-examples/src/test/groovy/LayoutExportTest.groovy b/migration-examples/src/test/groovy/LayoutExportTest.groovy index 27571d0d..b0156e8c 100644 --- a/migration-examples/src/test/groovy/LayoutExportTest.groovy +++ b/migration-examples/src/test/groovy/LayoutExportTest.groovy @@ -48,12 +48,12 @@ class LayoutExportTest { LayoutExport.run(migration, mappingFile) def expected = """\ - templateId,templateName (read-only),pageId,pageName (read-only),pageWidth (read-only),pageHeight (read-only),interactiveFlowName,flowToNextPage,x (read-only),y (read-only),width (read-only),height (read-only),type,targetId,contentPreview (read-only) - full tmpl,,full page,,,,test flow2,false,0mm,0mm,0mm,0mm,Standard,, - full tmpl,,full page,,,,test flow3,true,0mm,0mm,0mm,0mm,Standard,, - full tmpl,,full page,,,,,false,0mm,0mm,0mm,0mm,Standard,, - full tmpl,,full page,,,,test flow5,false,0mm,0mm,0mm,0mm,Standard,, - ,,unreferenced page,,,,test flow,true,0mm,0mm,0mm,0mm,Standard,, + templateId,templateName (read-only),pageId,pageName (read-only),pageWidth (read-only),pageHeight (read-only),areaIndex (read-only),interactiveFlowName,flowToNextPage,x (read-only),y (read-only),width (read-only),height (read-only),type,targetId,contentPreview (read-only) + full tmpl,,full page,,,,0,test flow2,false,0mm,0mm,0mm,0mm,Standard,, + full tmpl,,full page,,,,1,test flow3,true,0mm,0mm,0mm,0mm,Standard,, + full tmpl,,full page,,,,2,,false,0mm,0mm,0mm,0mm,Standard,, + full tmpl,,full page,,,,3,test flow5,false,0mm,0mm,0mm,0mm,Standard,, + ,,unreferenced page,,,,0,test flow,true,0mm,0mm,0mm,0mm,Standard,, """.stripIndent() Assertions.assertEquals(expected, mappingFile.toFile().text.replaceAll("\\r\\n|\\r", "\n")) } @@ -71,10 +71,10 @@ class LayoutExportTest { LayoutExport.run(migration, mappingFile) def expected = """\ - templateId,templateName (read-only),pageId,pageName (read-only),pageWidth (read-only),pageHeight (read-only),interactiveFlowName,flowToNextPage,x (read-only),y (read-only),width (read-only),height (read-only),type,targetId,contentPreview (read-only) - tmpl with areas,,,,,,Address Content,false,0mm,0mm,0mm,0mm,Standard,, - tmpl with areas,,,,,,,true,0mm,0mm,0mm,0mm,Standard,, - tmpl with areas,,,,,,Footer,false,0mm,0mm,0mm,0mm,Standard,, + templateId,templateName (read-only),pageId,pageName (read-only),pageWidth (read-only),pageHeight (read-only),areaIndex (read-only),interactiveFlowName,flowToNextPage,x (read-only),y (read-only),width (read-only),height (read-only),type,targetId,contentPreview (read-only) + tmpl with areas,,,,,,0,Address Content,false,0mm,0mm,0mm,0mm,Standard,, + tmpl with areas,,,,,,1,,true,0mm,0mm,0mm,0mm,Standard,, + tmpl with areas,,,,,,2,Footer,false,0mm,0mm,0mm,0mm,Standard,, """.stripIndent() Assertions.assertEquals(expected, mappingFile.toFile().text.replaceAll("\\r\\n|\\r", "\n")) } @@ -97,8 +97,8 @@ class LayoutExportTest { LayoutExport.run(migration, mappingFile) def expected = """\ - templateId,templateName (read-only),pageId,pageName (read-only),pageWidth (read-only),pageHeight (read-only),interactiveFlowName,flowToNextPage,x (read-only),y (read-only),width (read-only),height (read-only),type,targetId,contentPreview (read-only) - tmpl with base,,page with own base,,,,test flow,false,0mm,0mm,0mm,0mm,Standard,\$G1, + templateId,templateName (read-only),pageId,pageName (read-only),pageWidth (read-only),pageHeight (read-only),areaIndex (read-only),interactiveFlowName,flowToNextPage,x (read-only),y (read-only),width (read-only),height (read-only),type,targetId,contentPreview (read-only) + tmpl with base,,page with own base,,,,0,test flow,false,0mm,0mm,0mm,0mm,Standard,\$G1, """.stripIndent() Assertions.assertEquals(expected, mappingFile.toFile().text.replaceAll("\\r\\n|\\r", "\n")) } @@ -123,10 +123,10 @@ class LayoutExportTest { LayoutExport.run(migration, mappingFile) def expected = """\ - templateId,templateName (read-only),pageId,pageName (read-only),pageWidth (read-only),pageHeight (read-only),interactiveFlowName,flowToNextPage,x (read-only),y (read-only),width (read-only),height (read-only),type,targetId,contentPreview (read-only) - bt-1,Base template 1,page-1,Page 1,210mm,297mm,address,false,1cm,1cm,190mm,20mm,Base,, - bt-1,Base template 1,page-1,Page 1,210mm,297mm,Area 2,true,1cm,30mm,190mm,50mm,Base,, - bt-1,Base template 1,page-2,Page 2,210mm,99mm,Area 1,false,0mm,0mm,210mm,99mm,Base,, + templateId,templateName (read-only),pageId,pageName (read-only),pageWidth (read-only),pageHeight (read-only),areaIndex (read-only),interactiveFlowName,flowToNextPage,x (read-only),y (read-only),width (read-only),height (read-only),type,targetId,contentPreview (read-only) + bt-1,Base template 1,page-1,Page 1,210mm,297mm,0,address,false,1cm,1cm,190mm,20mm,Base,, + bt-1,Base template 1,page-1,Page 1,210mm,297mm,1,Area 2,true,1cm,30mm,190mm,50mm,Base,, + bt-1,Base template 1,page-2,Page 2,210mm,99mm,0,Area 1,false,0mm,0mm,210mm,99mm,Base,, """.stripIndent() Assertions.assertEquals(expected, mappingFile.toFile().text.replaceAll("\\r\\n|\\r", "\n")) } @@ -154,8 +154,8 @@ class LayoutExportTest { LayoutExport.run(migration, mappingFile) def expected = """\ - templateId,templateName (read-only),pageId,pageName (read-only),pageWidth (read-only),pageHeight (read-only),interactiveFlowName,flowToNextPage,x (read-only),y (read-only),width (read-only),height (read-only),type,targetId,contentPreview (read-only) - ,,page with preview,,,,test flow,false,0mm,0mm,0mm,0mm,Standard,,docRef: Block One;imageRef: Image One;docRef: Block Two;(+2 more) + templateId,templateName (read-only),pageId,pageName (read-only),pageWidth (read-only),pageHeight (read-only),areaIndex (read-only),interactiveFlowName,flowToNextPage,x (read-only),y (read-only),width (read-only),height (read-only),type,targetId,contentPreview (read-only) + ,,page with preview,,,,0,test flow,false,0mm,0mm,0mm,0mm,Standard,,docRef: Block One;imageRef: Image One;docRef: Block Two;(+2 more) """.stripIndent() Assertions.assertEquals(expected, mappingFile.toFile().text.replaceAll("\\r\\n|\\r", "\n")) } diff --git a/migration-examples/src/test/groovy/LayoutImportTest.groovy b/migration-examples/src/test/groovy/LayoutImportTest.groovy index 804038f3..837bb358 100644 --- a/migration-examples/src/test/groovy/LayoutImportTest.groovy +++ b/migration-examples/src/test/groovy/LayoutImportTest.groovy @@ -45,16 +45,16 @@ class LayoutImportTest { givenPageExists("page3", [null, "beta", null, "delta"]) def input = """\ - templateId,templateName,pageId,pageName,interactiveFlowName,flowToNextPage,x,y,width,height,contentPreview - ,,page1,,flow1,false,0.0mm,0.0mm,0.0mm,0.0mm, - ,,page1,,new flow2,false,0.0mm,0.0mm,0.0mm,0.0mm, - ,,page1,,flow3,true,0.0mm,0.0mm,0.0mm,0.0mm, - tmpl2,,page2,,flowA,true,0.0mm,0.0mm,0.0mm,0.0mm, - tmpl2,,page2,,modified flowB,false,0.0mm,0.0mm,0.0mm,0.0mm, - tmpl3,,page3,,new alpha,false,0.0mm,0.0mm,0.0mm,0.0mm, - tmpl3,,page3,,beta,true,0.0mm,0.0mm,0.0mm,0.0mm, - tmpl3,,page3,,new gamma,false,0.0mm,0.0mm,0.0mm,0.0mm, - tmpl3,,page3,,modified delta,true,0.0mm,0.0mm,0.0mm,0.0mm, + templateId,templateName,pageId,pageName,areaIndex,interactiveFlowName,flowToNextPage,x,y,width,height,contentPreview + ,,page1,,0,flow1,false,0.0mm,0.0mm,0.0mm,0.0mm, + ,,page1,,1,new flow2,false,0.0mm,0.0mm,0.0mm,0.0mm, + ,,page1,,2,flow3,true,0.0mm,0.0mm,0.0mm,0.0mm, + tmpl2,,page2,,0,flowA,true,0.0mm,0.0mm,0.0mm,0.0mm, + tmpl2,,page2,,1,modified flowB,false,0.0mm,0.0mm,0.0mm,0.0mm, + tmpl3,,page3,,0,new alpha,false,0.0mm,0.0mm,0.0mm,0.0mm, + tmpl3,,page3,,1,beta,true,0.0mm,0.0mm,0.0mm,0.0mm, + tmpl3,,page3,,2,new gamma,false,0.0mm,0.0mm,0.0mm,0.0mm, + tmpl3,,page3,,3,modified delta,true,0.0mm,0.0mm,0.0mm,0.0mm, """.stripIndent() mappingFile.toFile().write(input) @@ -68,6 +68,38 @@ class LayoutImportTest { verify(migration.mappingRepository).applyAllAreaMappings() } + @Test + void importIsRobustToReorderedAndNonContiguousRows() { + // Simulates a user sorting/filtering the exported CSV in Excel: rows for the same page are + // no longer adjacent, and their relative order no longer matches the areaIndex column. The + // import must still assign each row to the correct area and must not drop any of them. + Path mappingFile = Paths.get(dir.path, "testProject.csv") + + when(migration.mappingRepository.getAreaMapping("page1")).thenReturn(new MappingItem.Area(null, [:], [:])) + when(migration.mappingRepository.getAreaMapping("page2")).thenReturn(new MappingItem.Area(null, [:], [:])) + + givenPageExists("page1", ["flow1", "flow2", "flow3"], [false, false, false]) + givenPageExists("page2", ["flowA", "flowB"], [false, false]) + + def input = """\ + templateId,templateName,pageId,pageName,areaIndex,interactiveFlowName,flowToNextPage,x,y,width,height,contentPreview + tmpl2,,page2,,1,modified flowB,false,0.0mm,0.0mm,0.0mm,0.0mm, + ,,page1,,2,flow3,true,0.0mm,0.0mm,0.0mm,0.0mm, + ,,page1,,0,flow1,false,0.0mm,0.0mm,0.0mm,0.0mm, + tmpl2,,page2,,0,flowA,true,0.0mm,0.0mm,0.0mm,0.0mm, + ,,page1,,1,new flow2,false,0.0mm,0.0mm,0.0mm,0.0mm, + """.stripIndent() + mappingFile.toFile().write(input) + + LayoutImport.run(migration, mappingFile) + + verify(migration.mappingRepository).upsertBatch([ + "page1": new MappingItem.Area(null, [0: "flow1", 1: "new flow2", 2: "flow3"], [0: false, 1: false, 2: true]), + "page2": new MappingItem.Area(null, [0: "flowA", 1: "modified flowB"], [0: true, 1: false]) + ]) + verify(migration.mappingRepository).applyAllAreaMappings() + } + @Test void importTemplateDirectAreas() { Path mappingFile = Paths.get(dir.path, "testProject.csv") @@ -81,10 +113,10 @@ class LayoutImportTest { ) def input = """\ - templateId,templateName,pageId,pageName,interactiveFlowName,flowToNextPage,x,y,width,height,contentPreview - tmpl1,,,,Updated Address,true,0.0mm,0.0mm,0.0mm,0.0mm, - tmpl1,,,,New Header,false,0.0mm,0.0mm,0.0mm,0.0mm, - tmpl1,,,,Footer,true,0.0mm,0.0mm,0.0mm,0.0mm, + templateId,templateName,pageId,pageName,areaIndex,interactiveFlowName,flowToNextPage,x,y,width,height,contentPreview + tmpl1,,,,0,Updated Address,true,0.0mm,0.0mm,0.0mm,0.0mm, + tmpl1,,,,1,New Header,false,0.0mm,0.0mm,0.0mm,0.0mm, + tmpl1,,,,2,Footer,true,0.0mm,0.0mm,0.0mm,0.0mm, """.stripIndent() mappingFile.toFile().write(input) @@ -110,9 +142,9 @@ class LayoutImportTest { givenPageExists("page1", ["flow1", "flow2"], [false, false]) def input = """\ - templateId,templateName,pageId,pageName,interactiveFlowName,flowToNextPage,x,y,width,height,type,targetId,contentPreview - tmpl1,,page1,,flow1,false,0.0mm,0.0mm,0.0mm,0.0mm,Standard,\$G1, - tmpl1,,page1,,flow2,false,0.0mm,0.0mm,0.0mm,0.0mm,Standard,\$G1, + templateId,templateName,pageId,pageName,areaIndex,interactiveFlowName,flowToNextPage,x,y,width,height,type,targetId,contentPreview + tmpl1,,page1,,0,flow1,false,0.0mm,0.0mm,0.0mm,0.0mm,Standard,\$G1, + tmpl1,,page1,,1,flow2,false,0.0mm,0.0mm,0.0mm,0.0mm,Standard,\$G1, """.stripIndent() mappingFile.toFile().write(input) @@ -141,10 +173,10 @@ class LayoutImportTest { when(migration.mappingRepository.getBaseTemplateMapping("G1")).thenReturn(new MappingItem.BaseTemplate(null, null, [])) def input = """\ - templateId,templateName,pageId,pageName,pageWidth,pageHeight,interactiveFlowName,flowToNextPage,x,y,width,height,type,targetId,contentPreview - tmpl1,,page1,,,,flow1,false,0.0mm,0.0mm,0.0mm,0.0mm,Standard,\$G1, - G1,Base template 1,G1-P1,Page group 1,210mm,297mm,G1-P1.Area1,false,1cm,1cm,190mm,20mm,Base,, - G1,Base template 1,G1-P1,Page group 1,210mm,297mm,G1-P1.Area2,true,1cm,30mm,190mm,50mm,Base,, + templateId,templateName,pageId,pageName,pageWidth,pageHeight,areaIndex,interactiveFlowName,flowToNextPage,x,y,width,height,type,targetId,contentPreview + tmpl1,,page1,,,,0,flow1,false,0.0mm,0.0mm,0.0mm,0.0mm,Standard,\$G1, + G1,Base template 1,G1-P1,Page group 1,210mm,297mm,1,G1-P1.Area2,true,1cm,30mm,190mm,50mm,Base,, + G1,Base template 1,G1-P1,Page group 1,210mm,297mm,0,G1-P1.Area1,false,1cm,1cm,190mm,20mm,Base,, """.stripIndent() mappingFile.toFile().write(input) @@ -173,8 +205,8 @@ class LayoutImportTest { when(migration.mappingRepository.getBaseTemplateMapping("G1")).thenReturn(new MappingItem.BaseTemplate(null, null, [])) def input = """\ - templateId,templateName,pageId,pageName,pageWidth,pageHeight,interactiveFlowName,flowToNextPage,x,y,width,height,type,targetId,contentPreview - G1,,G1-P1,Page group 1,210mm,297mm,G1-P1.Area1,false,1cm,1cm,190mm,20mm,Base,, + templateId,templateName,pageId,pageName,pageWidth,pageHeight,areaIndex,interactiveFlowName,flowToNextPage,x,y,width,height,type,targetId,contentPreview + G1,,G1-P1,Page group 1,210mm,297mm,0,G1-P1.Area1,false,1cm,1cm,190mm,20mm,Base,, """.stripIndent() mappingFile.toFile().write(input) From ffd1f54336f04742a11b5ec21ea4b01b4d56490e Mon Sep 17 00:00:00 2001 From: "d.svitak" Date: Fri, 7 Aug 2026 07:34:27 +0200 Subject: [PATCH 2/2] MIG-585 Areas import - changelog update --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8705b2f..faf20dca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) - **Breaking** - Removed `TableBuilder.Row.addCells(List)` overload and replaced it by one that takes list of already built cells instead of cell builders. You will need to adjust your code to build the cells before adding them to the row. +- introducing index into the areas/layout mappings to be resistant to sorting and other potential discrepancies ### Fixed