Skip to content

Commit 1ed0c5d

Browse files
committed
✨📝 (appsettings.json, docs): add FieldCalculationMap and update documentation
Introduce a new FieldCalculationMap feature to the configuration, allowing calculations based on field values. This enhances the flexibility of field mapping by enabling dynamic calculations for fields like Custom.EstimatedCost. The documentation is updated to include examples and descriptions of the new feature, ensuring users understand how to implement and utilize it effectively. The removal of the redundant appsettings.json in the ConsoleDataGenerator streamlines the configuration process.
1 parent 49c93c6 commit 1ed0c5d

File tree

7 files changed

+217
-597
lines changed

7 files changed

+217
-597
lines changed

appsettings.json

Lines changed: 91 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -230,90 +230,100 @@
230230
}
231231
],
232232
"FieldMapSamples": {
233-
"FieldClearMap": {
234-
"ApplyTo": [ "SomeWorkItemType" ],
235-
"targetField": "Custom.FieldC"
236-
},
237-
"FieldMergeMap": {
238-
"ApplyTo": [ "SomeWorkItemType" ],
239-
"sourceFields": [ "Custom.FieldA", "Custom.FieldB" ],
240-
"targetField": "Custom.FieldC",
241-
"formatExpression": "{0} \n {1}"
242-
},
243-
"FieldLiteralMap": {
244-
"ApplyTo": [ "SomeWorkItemType" ],
245-
"targetField": "Custom.SomeField",
246-
"value": "New field value"
247-
},
248-
"MultiValueConditionalMap": {
249-
"ApplyTo": [ "SomeWorkItemType" ],
250-
"sourceFieldsAndValues": {
251-
"Field1": "Value1",
252-
"Field2": "Value2"
233+
"FieldCalculationMap": {
234+
"FieldMapType": "FieldCalculationMap",
235+
"ApplyTo": [ "Bug", "Task" ],
236+
"expression": "[effort] * [rate]",
237+
"parameters": {
238+
"effort": "Custom.EstimatedHours",
239+
"rate": "Custom.HourlyRate"
240+
},
241+
"targetField": "Custom.EstimatedCost"
242+
},
243+
"FieldClearMap": {
244+
"ApplyTo": [ "SomeWorkItemType" ],
245+
"targetField": "Custom.FieldC"
246+
},
247+
"FieldMergeMap": {
248+
"ApplyTo": [ "SomeWorkItemType" ],
249+
"sourceFields": [ "Custom.FieldA", "Custom.FieldB" ],
250+
"targetField": "Custom.FieldC",
251+
"formatExpression": "{0} \n {1}"
252+
},
253+
"FieldLiteralMap": {
254+
"ApplyTo": [ "SomeWorkItemType" ],
255+
"targetField": "Custom.SomeField",
256+
"value": "New field value"
257+
},
258+
"MultiValueConditionalMap": {
259+
"ApplyTo": [ "SomeWorkItemType" ],
260+
"sourceFieldsAndValues": {
261+
"Field1": "Value1",
262+
"Field2": "Value2"
263+
},
264+
"targetFieldsAndValues": {
265+
"Field1": "Value1",
266+
"Field2": "Value2"
267+
}
253268
},
254269
"targetFieldsAndValues": {
255-
"Field1": "Value1",
256-
"Field2": "Value2"
257-
}
258-
},
259-
"targetFieldsAndValues": {
260-
"ApplyTo": [ "SomeWorkItemType" ],
261-
"targetField": "Custom.ReflectedWorkItemId"
262-
},
263-
"FieldValueMap": {
264-
"ApplyTo": [ "SomeWorkItemType" ],
265-
"sourceField": "System.State",
266-
"targetField": "System.State",
267-
"defaultValue": "StateB",
268-
"valueMapping": {
269-
"StateA": "StateB"
270-
}
271-
},
272-
"FieldToFieldMap": {
273-
"ApplyTo": [ "SomeWorkItemType" ],
274-
"sourceField": "Microsoft.VSTS.Common.BacklogPriority",
275-
"targetField": "Microsoft.VSTS.Common.StackRank",
276-
"defaultValue": 42
277-
},
278-
"FieldToFieldMultiMap": {
279-
"ApplyTo": [ "SomeWorkItemType", "SomeOtherWorkItemType" ],
280-
"SourceToTargetMappings": {
281-
"SourceField1": "TargetField1",
282-
"SourceField2": "TargetField2"
270+
"ApplyTo": [ "SomeWorkItemType" ],
271+
"targetField": "Custom.ReflectedWorkItemId"
272+
},
273+
"FieldValueMap": {
274+
"ApplyTo": [ "SomeWorkItemType" ],
275+
"sourceField": "System.State",
276+
"targetField": "System.State",
277+
"defaultValue": "StateB",
278+
"valueMapping": {
279+
"StateA": "StateB"
280+
}
281+
},
282+
"FieldToFieldMap": {
283+
"ApplyTo": [ "SomeWorkItemType" ],
284+
"sourceField": "Microsoft.VSTS.Common.BacklogPriority",
285+
"targetField": "Microsoft.VSTS.Common.StackRank",
286+
"defaultValue": 42
287+
},
288+
"FieldToFieldMultiMap": {
289+
"ApplyTo": [ "SomeWorkItemType", "SomeOtherWorkItemType" ],
290+
"SourceToTargetMappings": {
291+
"SourceField1": "TargetField1",
292+
"SourceField2": "TargetField2"
293+
}
294+
},
295+
"FieldToTagMap": {
296+
"ApplyTo": [ "SomeWorkItemType" ],
297+
"sourceField": "System.State",
298+
"formatExpression": "ScrumState:{0}"
299+
},
300+
"FieldToTagFieldMap": {
301+
"ApplyTo": [ "SomeWorkItemType" ],
302+
"sourceFields": [
303+
"System.Description",
304+
"Microsoft.VSTS.Common.AcceptanceCriteria"
305+
],
306+
"targetField": "System.Description",
307+
"formatExpression": "{0} <br/><br/><h3>Acceptance Criteria</h3>{1}"
308+
},
309+
"RegexFieldMap": {
310+
"ApplyTo": [ "SomeWorkItemType" ],
311+
"sourceField": "COMPANY.PRODUCT.Release",
312+
"targetField": "COMPANY.DEVISION.MinorReleaseVersion",
313+
"pattern": "PRODUCT \\d{4}.(\\d{1})",
314+
"replacement": "$1"
315+
},
316+
"FieldValueToTagMap": {
317+
"ApplyTo": [ "SomeWorkItemType" ],
318+
"sourceField": "Microsoft.VSTS.CMMI.Blocked",
319+
"pattern": "Yes",
320+
"formatExpression": "{0}"
321+
},
322+
"TreeToTagMap": {
323+
"ApplyTo": [ "SomeWorkItemType" ],
324+
"toSkip": 3,
325+
"timeTravel": 1
283326
}
284-
},
285-
"FieldToTagMap": {
286-
"ApplyTo": [ "SomeWorkItemType" ],
287-
"sourceField": "System.State",
288-
"formatExpression": "ScrumState:{0}"
289-
},
290-
"FieldToTagFieldMap": {
291-
"ApplyTo": [ "SomeWorkItemType" ],
292-
"sourceFields": [
293-
"System.Description",
294-
"Microsoft.VSTS.Common.AcceptanceCriteria"
295-
],
296-
"targetField": "System.Description",
297-
"formatExpression": "{0} <br/><br/><h3>Acceptance Criteria</h3>{1}"
298-
},
299-
"RegexFieldMap": {
300-
"ApplyTo": [ "SomeWorkItemType" ],
301-
"sourceField": "COMPANY.PRODUCT.Release",
302-
"targetField": "COMPANY.DEVISION.MinorReleaseVersion",
303-
"pattern": "PRODUCT \\d{4}.(\\d{1})",
304-
"replacement": "$1"
305-
},
306-
"FieldValueToTagMap": {
307-
"ApplyTo": [ "SomeWorkItemType" ],
308-
"sourceField": "Microsoft.VSTS.CMMI.Blocked",
309-
"pattern": "Yes",
310-
"formatExpression": "{0}"
311-
},
312-
"TreeToTagMap": {
313-
"ApplyTo": [ "SomeWorkItemType" ],
314-
"toSkip": 3,
315-
"timeTravel": 1
316-
}
317327
}
318328
},
319329
"TfsChangeSetMappingTool": {

docs/_data/reference.fieldmaps.fieldcalculationmap.yaml

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,48 @@ configurationSamples:
2626
- name: sample
2727
order: 1
2828
description:
29-
code: There is no sample, but you can check the classic below for a general feel.
29+
code: >-
30+
{
31+
"MigrationTools": {
32+
"Version": "16.0",
33+
"CommonTools": {
34+
"FieldMappingTool": {
35+
"FieldMaps": [
36+
{
37+
"ApplyTo": [
38+
"Bug",
39+
"Task"
40+
],
41+
"expression": "[effort] * [rate]",
42+
"FieldMapType": "FieldCalculationMap",
43+
"parameters": {
44+
"effort": "Custom.EstimatedHours",
45+
"rate": "Custom.HourlyRate"
46+
},
47+
"targetField": "Custom.EstimatedCost"
48+
}
49+
]
50+
}
51+
}
52+
}
53+
}
3054
sampleFor: MigrationTools.Tools.FieldCalculationMapOptions
3155
- name: classic
3256
order: 3
3357
description:
3458
code: >-
3559
{
3660
"$type": "FieldCalculationMapOptions",
37-
"expression": null,
38-
"parameters": {},
39-
"targetField": null,
61+
"expression": "[effort] * [rate]",
62+
"parameters": {
63+
"effort": "Custom.EstimatedHours",
64+
"rate": "Custom.HourlyRate"
65+
},
66+
"targetField": "Custom.EstimatedCost",
4067
"ApplyTo": [
41-
"*"
68+
"*",
69+
"Bug",
70+
"Task"
4271
]
4372
}
4473
sampleFor: MigrationTools.Tools.FieldCalculationMapOptions

docs/_data/reference.tools.fieldmappingtool.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ configurationSamples:
3333
"FieldMappingTool": {
3434
"Enabled": "True",
3535
"FieldMaps": [
36+
{
37+
"ApplyTo": [
38+
"Bug",
39+
"Task"
40+
],
41+
"expression": "[effort] * [rate]",
42+
"FieldMapType": "FieldCalculationMap",
43+
"parameters": {
44+
"effort": "Custom.EstimatedHours",
45+
"rate": "Custom.HourlyRate"
46+
},
47+
"targetField": "Custom.EstimatedCost"
48+
},
3649
{
3750
"ApplyTo": [
3851
"SomeWorkItemType"
@@ -70,6 +83,19 @@ configurationSamples:
7083
}
7184
],
7285
"FieldMapSamples": {
86+
"FieldCalculationMap": {
87+
"ApplyTo": [
88+
"Bug",
89+
"Task"
90+
],
91+
"expression": "[effort] * [rate]",
92+
"FieldMapType": "FieldCalculationMap",
93+
"parameters": {
94+
"effort": "Custom.EstimatedHours",
95+
"rate": "Custom.HourlyRate"
96+
},
97+
"targetField": "Custom.EstimatedCost"
98+
},
7399
"FieldClearMap": {
74100
"ApplyTo": [
75101
"SomeWorkItemType"

docs/collections/_reference/reference.fieldmaps.fieldcalculationmap.md

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,48 @@ configurationSamples:
2727
- name: sample
2828
order: 1
2929
description:
30-
code: There is no sample, but you can check the classic below for a general feel.
30+
code: >-
31+
{
32+
"MigrationTools": {
33+
"Version": "16.0",
34+
"CommonTools": {
35+
"FieldMappingTool": {
36+
"FieldMaps": [
37+
{
38+
"ApplyTo": [
39+
"Bug",
40+
"Task"
41+
],
42+
"expression": "[effort] * [rate]",
43+
"FieldMapType": "FieldCalculationMap",
44+
"parameters": {
45+
"effort": "Custom.EstimatedHours",
46+
"rate": "Custom.HourlyRate"
47+
},
48+
"targetField": "Custom.EstimatedCost"
49+
}
50+
]
51+
}
52+
}
53+
}
54+
}
3155
sampleFor: MigrationTools.Tools.FieldCalculationMapOptions
3256
- name: classic
3357
order: 3
3458
description:
3559
code: >-
3660
{
3761
"$type": "FieldCalculationMapOptions",
38-
"expression": null,
39-
"parameters": {},
40-
"targetField": null,
62+
"expression": "[effort] * [rate]",
63+
"parameters": {
64+
"effort": "Custom.EstimatedHours",
65+
"rate": "Custom.HourlyRate"
66+
},
67+
"targetField": "Custom.EstimatedCost",
4168
"ApplyTo": [
42-
"*"
69+
"*",
70+
"Bug",
71+
"Task"
4372
]
4473
}
4574
sampleFor: MigrationTools.Tools.FieldCalculationMapOptions

docs/collections/_reference/reference.tools.fieldmappingtool.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ configurationSamples:
3434
"FieldMappingTool": {
3535
"Enabled": "True",
3636
"FieldMaps": [
37+
{
38+
"ApplyTo": [
39+
"Bug",
40+
"Task"
41+
],
42+
"expression": "[effort] * [rate]",
43+
"FieldMapType": "FieldCalculationMap",
44+
"parameters": {
45+
"effort": "Custom.EstimatedHours",
46+
"rate": "Custom.HourlyRate"
47+
},
48+
"targetField": "Custom.EstimatedCost"
49+
},
3750
{
3851
"ApplyTo": [
3952
"SomeWorkItemType"
@@ -71,6 +84,19 @@ configurationSamples:
7184
}
7285
],
7386
"FieldMapSamples": {
87+
"FieldCalculationMap": {
88+
"ApplyTo": [
89+
"Bug",
90+
"Task"
91+
],
92+
"expression": "[effort] * [rate]",
93+
"FieldMapType": "FieldCalculationMap",
94+
"parameters": {
95+
"effort": "Custom.EstimatedHours",
96+
"rate": "Custom.HourlyRate"
97+
},
98+
"targetField": "Custom.EstimatedCost"
99+
},
74100
"FieldClearMap": {
75101
"ApplyTo": [
76102
"SomeWorkItemType"

src/MigrationTools.ConsoleDataGenerator/ClassDataLoader.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,12 @@ static string ConvertSectionWithPathToJson(IConfiguration configuration, IConfig
188188
if (currentObject[key] is JArray array)
189189
{
190190
JObject itemObject = sectionObject as JObject ?? new JObject();
191-
// Add ObjectName and OptionFor to the object
192-
itemObject.AddFirst(new JProperty(option.ConfigurationMetadata.ObjectName, option.ConfigurationMetadata.OptionFor));
191+
if (!itemObject.ContainsKey(option.ConfigurationMetadata.ObjectName))
192+
{
193+
// Add ObjectName and OptionFor to the object
194+
itemObject.AddFirst(new JProperty(option.ConfigurationMetadata.ObjectName, option.ConfigurationMetadata.OptionFor));
195+
}
196+
193197
array.Add(itemObject);
194198
}
195199
}

0 commit comments

Comments
 (0)