How to create a map for deleted iterations

196 Views Asked by At

I currently try to migrate a old project with XML based projects to a new collection with inherited proceses.

While running the migration on the project I used the following settings, shortened to the essential for this case:

[...]
"Source": {
"Project": "Project",
[...]
},
"Target": {
"Project": "Test",
[...]
},
"Processors": [
  {
    "$type": "WorkItemMigrationConfig",
    "Enabled": true,
    "ReplayRevisions": true,
    "PrefixProjectToNodes": false,
    "MaxRevisions": 0,
    "StopMigrationOnMissingAreaIterationNodes": false,
    "AreaMaps": {},
    "IterationMaps": {},
    "SkipRevisionWithInvalidIterationPath": false,
    "SkipRevisionWithInvalidAreaPath": false
    [...]
  }
],
"Version": "13.0",
[...]

While validaiting the data I noticeds that the team has deleted some of there iterations, so that in the revision I got the "(removed iteration path)" display. As those iterations are not in the source project they do not get auto created in the target.

One way as far as I know would be to add the missing iteration manually to new target, let the migration happen and delete them afterwards.

While checking the migration log I noticed the following block

!! There are 13 IterationPaths found in the history of the Source that are missing from the Target. These MUST be added or mapped with a fieldMap before we can continue.
MISSING Iteration: Test\Phase 2 - Sprint 2
[...]
ValidateTargetNodesExist::StopMigrationOnMissingAreaIterationNodes:False

I just can't figure out how to configure the mentioned fieldMap. So far I have tried the following ways:

As FieldMap: (To be on the safe side, I tried it with both the original value and the logged missing value)

{
  "$type": "FieldValueMapConfig",
  "WorkItemTypeName": "*",
  "sourceField": "System.IterationPath",
  "targetField": "System.IterationPath",
  "defaultValue": "",
  "valueMapping": {
    "Project\\Phase 2 - Sprint 2": "Test\\Phase 2",
    "Test\\Phase 2 - Sprint 2": "Test\\Phase 2",
  }
}

About the IterationMaps in the processor

"IterationMaps": {
    "Project\\\\Phase 2 - Sprint 2": "Test\\Phase 2",
    "Test\\\\Phase 2 - Sprint 2": "Test\\Phase 2",
  }

It always end as TF237124 with the items which have used the deleted iteration in the history. Since changes other than just the iteration path have been made in the revision, I would be reluctant to set SkipRevisionWithInvalidIterationPath to true.

Do you have any idea where I am making the mistake? Or is there a processor which support in this case?

1

There are 1 best solutions below

0
MrHinsh - Martin Hinshelwood On

As per the documentation, you need to add Iteration Maps and Area Maps that adapt the old locations to new ones that are valid in the Target.

Before your migration starts it will validate that all of the Areas and Iterations from the Source work items revisions exist on the Target. Any that do not exist will be flagged in the logs and if you have "StopMigrationOnMissingAreaIterationNodes": true, set the migration will stop just after it outputs a list of the missing nodes.

Our algorithm that converts the Source nodes to Target nodes processes the mappings at that time. This means that any valid mapped nodes will never be caught by the This path is not anchored in the source project message as they are already altered to be valid.

We recently updated the logging for this part of the system to more easily debug both your mappings and to see what they system is doing with the nodes and their current state. You can set "LogLevel": "Debug" to see the details.

To add a mapping, you can follow the documentation with this being the simplest way:

"IterationMaps": {
  "WorkItemMovedFromProjectName\\\\Iteration 1": "TargetProject\\Sprint 1",
},
"AreaMaps": {
   "WorkItemMovedFromProjectName\\\\Team 2": "TargetProject\\ProductA\\Team 2",
}

Or you can use regular expressions to match the missing area or iteration paths:

"IterationMaps": {
  "^OriginalProject\\\\Path1(?=\\\\Sprint 2022)": "TargetProject\\AnotherPath\\NewTeam",
  "^OriginalProject\\\\Path1(?=\\\\Sprint 2020)": "TargetProject\\AnotherPath\\Archives\\Sprints 2020",
  "^OriginalProject\\\\Path2": "TargetProject\\YetAnotherPath\\Path2",
},
"AreaMaps": {
  "^OriginalProject\\\\(DescopeThis|DescopeThat)": "TargetProject\\Archive\\Descoped\\",
  "^OriginalProject\\\\(?!DescopeThis|DescopeThat)": "TargetProject\\NewArea\\",
}

Regular expressions are much more difficult to build and debug so it is a good idea to use a regular expression tester to check that you are matching the right things.

NOTE: You need \\ to escape a \ the pattern, and \\ to escape a \ in JSON. Therefor on the left of the match you need 4 \ to represent the \\ for the pattern and only 2 \ in the match

image