public override int Prepare(StageConfiguration configuration) { mapping = (WorkItemsStageConfiguration)configuration; Debug.Assert(mapping != null); sourceWIStore = sourceConn.Collection.GetService <WorkItemStore>(); if (mapping.Mode.HasFlag(WorkItemsStageConfiguration.Modes.BypassWorkItemStoreRules)) { eventSink.BypassingRulesOnDestinationWorkItemStore(destConn); // this will turn off validation! destWIStore = new WorkItemStore(destConn.Collection, WorkItemStoreFlags.BypassRules); } else { destWIStore = destConn.Collection.GetService <WorkItemStore>(); }//if mapping.SetDefaults(sourceConn, sourceWIStore, destConn, destWIStore); eventSink.DumpMapping(mapping); checker = new WorkItemsStageConfigurationChecker(sourceWIStore, sourceConn.ProjectName, destWIStore, destConn.ProjectName, eventSink); checker.AgnosticCheck(mapping); if (!checker.Passed) { // abort return(checker.ErrorCount); } return(0); }
private List <WorkItem> SaveWorkItems(WorkItemsStageConfiguration mapping, WitMappingIndex index, WorkItemStore destWIStore, List <WorkItem> changedWorkItems, bool testOnly) { var failedWorkItems = new List <WorkItem>(); if (testOnly) { eventSink.SavingSkipped(); } else { var errors = destWIStore.BatchSave(changedWorkItems.ToArray(), SaveFlags.MergeAll); failedWorkItems = ExamineSaveErrors(errors, index); }//if var validWorkItems = changedWorkItems.Except(failedWorkItems); // some succeeded: their Ids could be changed, so refresh index if (!testOnly) { UpdateIndex(index, validWorkItems, mapping); foreach (var item in validWorkItems) { this.ChangeLog.AddEntry( new WorkItemChangeEntry( index.GetSourceIdFromTargetId(item.Id), item.Id, item.IsNew ? WorkItemChangeEntry.Change.New : WorkItemChangeEntry.Change.Update)); } //for } //if return(validWorkItems.ToList()); }
private void SaveWorkItems3Passes(WorkItemsStageConfiguration mapping, WitMappingIndex index, bool testOnly, WorkItemStore destWIStore, List <WorkItem> newWorkItems, List <WorkItem> updatedWorkItems, List <WorkItem> validWorkItems) { eventSink.SaveFirstPassSavingNewWorkItems(newWorkItems); //HACK: force all new workitems to the Initial state var realStates = new Dictionary <WorkItem, string>(); newWorkItems.ForEach(w => { realStates.Add(w, w.State); w.State = GetInitialState(w); }); validWorkItems.AddRange(SaveWorkItems(mapping, index, destWIStore, newWorkItems, testOnly)); eventSink.SaveSecondPassUpdatingNewWorkItemsState(newWorkItems); // and now update the no-more-new WI with the real state newWorkItems.ForEach(w => { w.State = realStates[w]; }); validWorkItems.AddRange(SaveWorkItems(mapping, index, destWIStore, newWorkItems, testOnly)); eventSink.SaveThirdPassSavingUpdatedWorkItems(updatedWorkItems); // existing WI do not need tricks validWorkItems.AddRange(SaveWorkItems(mapping, index, destWIStore, updatedWorkItems, testOnly)); }
public void FixNulls() { SourceConnection = SourceConnection ?? new ConnectionInfo(); DestinationConnection = DestinationConnection ?? new ConnectionInfo(); PipelineStages = PipelineStages ?? new List <string>(); AreasAndIterationsStage = AreasAndIterationsStage ?? new AreasAndIterationsStageConfiguration(); GlobalListsStage = GlobalListsStage ?? new GlobalListsStageConfiguration(); WorkItemsStage = WorkItemsStage ?? new WorkItemsStageConfiguration(); }
internal SyncContext(TfsConnection sourceConnection, WorkItemStore sourceWIStore, string sourceProjectName, WorkItemStore destWIStore, string destProjectName, WorkItemsStageConfiguration mapping, WitMappingIndex index, IEngineEvents eventSink) { this.sourceConnection = sourceConnection; this.sourceWIStore = sourceWIStore; this.sourceProjectName = sourceProjectName; this.destWIStore = destWIStore; this.destProjectName = destProjectName; this.mapping = mapping; this.index = index; this.eventSink = eventSink; }
internal SyncContext(SyncContext rhs) { this.sourceConnection = rhs.sourceConnection; this.sourceWIStore = rhs.sourceWIStore; this.sourceProjectName = rhs.sourceProjectName; this.destWIStore = rhs.destWIStore; this.destProjectName = rhs.destProjectName; this.mapping = rhs.mapping; this.index = rhs.index; this.eventSink = rhs.eventSink; }
private void SaveLinks(WorkItemsStageConfiguration mapping, WitMappingIndex index, WorkItemStore destWIStore, IEnumerable <WorkItem> changedWorkItems, bool testOnly) { if (testOnly) { eventSink.SavingSkipped(); } else { var errors = destWIStore.BatchSave(changedWorkItems.ToArray(), SaveFlags.MergeAll); ExamineSaveErrors(errors, index); }//if }
internal object MapState(FieldMap rule, WorkItemMap map, WorkItemsStageConfiguration mapping, object sourceValue) { var x = map.FindMappedState(sourceValue.ToString()); if (x != null) { return(x.Destination); } else { eventSink.NoTargetState(map, sourceValue); return(string.Empty); } }
public void DumpMapping(WorkItemsStageConfiguration mapping) { var output = new System.IO.StringWriter(); output.WriteLine(); output.WriteLine("# mapping dump start #"); var serializer = new YamlDotNet.Serialization.Serializer(YamlDotNet.Serialization.SerializationOptions.EmitDefaults, new YamlDotNet.Serialization.NamingConventions.CamelCaseNamingConvention()); serializer.Serialize(output, mapping); output.WriteLine("# mapping dump end #"); output.Flush(); this.Verbose("Dumping Mapping"); base.RawOut(VerboseColor, TraceLevel.Verbose, output.ToString()); }
public bool Validate() { if (this.WorkItemsStage == null) { // mapping file could be empty this.WorkItemsStage = new WorkItemsStageConfiguration(); } foreach (var stageName in this.PipelineStages) { //TODO } // TODO more and more return(true); }
internal void Check(QueryResult sourceResult, WorkItemsStageConfiguration mapping, QueryResult destResult) { var workItemMappings = mapping.WorkItemMappings.ToList(); var sourceWorkItems = sourceResult.WorkItems.Values.ToList(); var destWorkItems = destResult.WorkItems.Values.ToList(); var sourceTypeNames = sourceWorkItems.ConvertAll(wi => wi.Type.Name).Distinct(); var mappedSourceTypeNames = workItemMappings.ConvertAll(m => m.SourceType); sourceTypeNames.Except(mappedSourceTypeNames).ToList().ForEach( t => Log("Missing mapping for source type {0}", t) ); var destTypeNames = destWorkItems.ConvertAll(wi => wi.Type.Name).Distinct(); var mappedDestTypeNames = workItemMappings.ConvertAll(m => m.DestinationType); destTypeNames.Except(mappedDestTypeNames).ToList().ForEach( t => Log("Missing mapping for destination type {0}", t)); }
public static T Generate <T>() where T : PipelineConfiguration, new() { var self = new T() { SourceConnection = new ConnectionInfo() { CollectionUrl = "http://localhost:8080/tfs/DefaultCollection", ProjectName = "yourSourceProject", User = "******", Password = "******" }, DestinationConnection = new ConnectionInfo() { CollectionUrl = "http://localhost:8080/tfs/DefaultCollection", ProjectName = "yourTargetProject", User = "******", Password = "******" }, PipelineStages = new List <string>() { "step1", "step2" }, StopPipelineOnFirstError = true, TestOnly = true, Logging = LoggingLevel.Diagnostic, ChangeLogFile = "changes.csv", LogFile = "log.txt", // let them say AreasAndIterationsStage = AreasAndIterationsStageConfiguration.Generate(), GlobalListsStage = GlobalListsStageConfiguration.Generate(), WorkItemsStage = WorkItemsStageConfiguration.Generate() }; return(self); }
internal object MapIterationPath(FieldMap rule, WorkItemMap map, WorkItemsStageConfiguration mapping, object sourceValue) { var path = sourceValue.ToString(); // search suitable mapping var x = mapping.FindExactMappedIterationPath(path); if (x == null) { x = mapping.GetDefaultIterationPathMapping(); if (x == null) { eventSink.NoWildcardIterationRule(mapping, sourceValue); } else { eventSink.IterationPathNotFoundUsingWildcardRule(mapping, sourceValue); } } if (x != null) { if (x.DestinationPath == "*") { // replace project name path = this.destProjectName + path.Substring(this.sourceProjectName.Length); } else if (!string.IsNullOrWhiteSpace(x.DestinationPath)) { path = x.DestinationPath; } else { path = this.destProjectName; } } return(path); }
public FieldCopier(WorkItemsStageConfiguration mapping, MapperFunctions functions, bool useEditableProperty, WorkItemType sourceType, WorkItemMap map, WorkItemType targetType, IEngineEvents engineEvents) { engineEvents.TraceRule("Interpreting rules for mapping '{0}' workitems to '{1}'", sourceType.Name, targetType.Name); foreach (FieldDefinition fromField in sourceType.FieldDefinitions) { var rule = map.FindFieldRule(fromField.ReferenceName); if (rule == null) { // if no rule -> skip field engineEvents.NoRuleFor(sourceType, fromField.ReferenceName); continue; } string targetFieldName = rule.IsWildcard ? fromField.ReferenceName : rule.Destination; if (string.IsNullOrWhiteSpace(rule.Destination)) { engineEvents.TraceRule("Skip {0}", fromField.ReferenceName); continue; } if (!targetType.FieldDefinitions.Contains(targetFieldName)) { engineEvents.TraceRule("Skip {0} (Target field {1} does not exist)", fromField.ReferenceName, targetFieldName); continue; } var toField = targetType.FieldDefinitions[targetFieldName]; if (!IsAssignable(useEditableProperty, fromField, toField)) { engineEvents.TraceRule("Skip {0} (Not assignable to {1})", fromField.ReferenceName, targetFieldName); continue; }//if // make the proper copier function Action <Field, Field> copyAction; if (rule.IsWildcard) { engineEvents.TraceRule("Copy {0} to {1} (Wildcard)", fromField.ReferenceName, targetFieldName); copyAction = (src, dst) => { dst.Value = src.Value; }; } else if (!string.IsNullOrWhiteSpace(rule.Set)) { engineEvents.TraceRule("Set {0} to value '{1}'", targetFieldName, rule.Set); copyAction = (src, dst) => { engineEvents.Trace(" *** converting '{0}' to {1}", rule.Set, dst.FieldDefinition.FieldType); SetFieldWithConstant(dst, rule.Set); }; } else if (!string.IsNullOrWhiteSpace(rule.SetIfNull)) { engineEvents.TraceRule("Set {0} to value '{1}' when source is null or empty", targetFieldName, rule.SetIfNull); copyAction = (src, dst) => { if (src.Value == null || string.IsNullOrEmpty(src.Value.ToString())) { SetFieldWithConstant(dst, rule.SetIfNull); } else { dst.Value = src.Value; } }; } else if (!string.IsNullOrWhiteSpace(rule.Translate)) { var flags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic; // TODO optimize var translatorMethod = functions.GetType().GetMethod(rule.Translate, flags); if (translatorMethod == null) { engineEvents.TranslatorFunctionNotFoundUsingDefault(rule); // default: no translation engineEvents.TraceRule("Copy {0} to {1} (fallback)", fromField.ReferenceName, targetFieldName); copyAction = (src, dst) => { dst.Value = src.Value; }; } else { engineEvents.TraceRule("Translate {0} via {1}", targetFieldName, rule.Translate); copyAction = (src, dst) => { dst.Value = translatorMethod.Invoke(functions, new object[] { rule, map, mapping, src.Value }); }; } } else { //engineEvents.InvalidRule(rule); engineEvents.TraceRule("Copy {0} to {1} (Explicit)", fromField.ReferenceName, targetFieldName); // crossing fingers copyAction = (src, dst) => { dst.Value = src.Value; }; }//if tasks.Add(fromField, new CopyTask() { SourceFieldName = fromField.ReferenceName, CopyAction = copyAction, TargetFieldName = targetFieldName }); }//for fields // now the Set rules! foreach (var rule in map.Fields) { if (string.IsNullOrWhiteSpace(rule.Source)) { if (!string.IsNullOrWhiteSpace(rule.Set)) { engineEvents.TraceRule("Set {0} to value '{1}'", rule.Destination, rule.Set); unboundTasks.Add(new CopyTask() { SourceFieldName = string.Empty, CopyAction = (src, dst) => { SetFieldWithConstant(dst, rule.Set); }, TargetFieldName = rule.Destination }); } else if (!string.IsNullOrWhiteSpace(rule.SetIfNull)) { engineEvents.TraceRule("Set {0} to value '{1}' when destination is null or empty", rule.Destination, rule.SetIfNull); unboundTasks.Add(new CopyTask() { SourceFieldName = string.Empty, CopyAction = (src, dst) => { if (dst.Value == null || string.IsNullOrEmpty(dst.Value.ToString())) { SetFieldWithConstant(dst, rule.SetIfNull); } }, TargetFieldName = rule.Destination }); } } //if } //for }
public static WorkItemsStageConfiguration Generate() { var self = new WorkItemsStageConfiguration() { SourceQuery = "source query", DestinationQuery = "dest query", IndexFile = "index.xml", Mode = Modes.OpenTargetWorkItem | Modes.UseEditableProperty, AreaMap = new AreaMap[] { new AreaMap() { SourcePath = "srcArea1", DestinationPath = "dstArea1" }, new AreaMap() { SourcePath = "srcArea2", DestinationPath = "dstArea2" } }, IterationMap = new IterationMap[] { new IterationMap() { SourcePath = "src", DestinationPath = "dst" }, new IterationMap() { SourcePath = "*", DestinationPath = "" } }, WorkItemMappings = new WorkItemMap[] { new WorkItemMap() { SourceType = "srctype", DestinationType = "desttype", Attachments = WorkItemMap.AttachmentMode.Sync, RollbackValidationErrors = true, IDField = new FieldMap() { Source = "srcID", Destination = "dstID" }, StateList = new StateList() { States = new StateMap[] { new StateMap() { Source = "srcstate1", Destination = "deststate1" }, new StateMap() { Source = "srcstate2", Destination = "deststate2" } } }, Fields = new FieldMap[] { new FieldMap() { Source = "src1", Destination = "dst1" }, new FieldMap() { Source = "src2", Destination = "dst2", Translate = "tranFunc2" }, new FieldMap() { Destination = "dst3", Set = "val3" }, new FieldMap() { Source = "src4", Destination = "dst4", SetIfNull = "set4" }, new FieldMap() { Source = "*", Destination = "*" }, new FieldMap() { Source = "*", Destination = "" }, } } }, LinkTypeMap = new LinkTypeMap[] { new LinkTypeMap() { SourceType = "srclnk1", DestinationType = "dstlnk1" }, new LinkTypeMap() { SourceType = "srclnk2", DestinationType = "dstlnk2" }, new LinkTypeMap() { SourceType = "*", DestinationType = "*" } } }; return(self); }
private WitMappingIndex BuildIndex(WorkItemStore destWIStore, IEnumerable <WorkItem> existingTargetWorkItems, WorkItemsStageConfiguration mapping) { var index = new WitMappingIndex(); if (mapping.HasIndex) { if (!System.IO.File.Exists(mapping.IndexFile)) { //HACK on first run the file cannot exists, so create an empty one index = WitMappingIndex.CreateEmpty(mapping.IndexFile); } else { index = WitMappingIndex.Load(mapping.IndexFile, destWIStore); }//if } else { index.Clear(); foreach (var targetWI in existingTargetWorkItems) { var originatingFieldMap = mapping.FindIdFieldForTargetWorkItemType(targetWI.Type.Name); var v = targetWI.Fields[originatingFieldMap.Destination].Value; // could be that the destination exists, with no origin (e.g. manual intervention) int originatingId = (int)(v ?? 0); index.Add(originatingId, targetWI); } //for } //if return(index); }
public void IterationPathNotFoundUsingWildcardRule(WorkItemsStageConfiguration mapping, object sourceValue) { this.UniqueVerbose(" Iteration path '{0}' not found: using wildcard rule.", sourceValue); }
public void NoWildcardIterationRule(WorkItemsStageConfiguration mapping, object sourceValue) { this.UniqueWarning(" No wildcard Iteration rule."); }
private void UpdateIndex(WitMappingIndex index, IEnumerable <WorkItem> updatedWorkItems, WorkItemsStageConfiguration mapping) { if (mapping.HasIndex) { index.Update(updatedWorkItems); index.Save(mapping.IndexFile); } else { foreach (var dst in updatedWorkItems) { var originatingFieldMap = mapping.FindIdFieldForTargetWorkItemType(dst.Type.Name); int originatingId = (int)dst.Fields[originatingFieldMap.Destination].Value; index.Update(originatingId, dst); }//for } }
public override int Execute(StageConfiguration configuration) { mapping = (WorkItemsStageConfiguration)configuration; var sourceRunner = new QueryRunner(sourceWIStore, sourceConn.ProjectName); eventSink.ExecutingSourceQuery(mapping.SourceQuery, sourceConn); var sourceResult = sourceRunner.RunQuery(mapping.SourceQuery); if (sourceResult == null) { eventSink.SourceQueryNotFound(mapping.SourceQuery); return(3); } var destRunner = new QueryRunner(destWIStore, destConn.ProjectName); eventSink.ExecutingDestinationQuery(mapping.DestinationQuery, destConn); var destResult = destRunner.RunQuery(mapping.DestinationQuery); if (destResult == null) { eventSink.DestinationQueryNotFound(mapping.DestinationQuery); return(4); } // use query data for more thorough checks checker.Check(sourceResult, mapping, destResult); if (!checker.Passed) { // abort return(checker.ErrorCount); } // this needs also connection to target, better after query execution, so we have warm caches var index = BuildIndex(destWIStore, destResult.WorkItems.Values, mapping); var context = new SyncContext(sourceConn, sourceWIStore, sourceConn.ProjectName, destWIStore, destConn.ProjectName, mapping, index, eventSink); var workItemMapper = new WorkItemMapper(context); // configure options workItemMapper.UseEditableProperty = mapping.Mode.HasFlag(WorkItemsStageConfiguration.Modes.UseEditableProperty); workItemMapper.OpenTargetWorkItem = mapping.Mode.HasFlag(WorkItemsStageConfiguration.Modes.OpenTargetWorkItem); workItemMapper.PartialOpenTargetWorkItem = mapping.Mode.HasFlag(WorkItemsStageConfiguration.Modes.PartialOpenTargetWorkItem); List <WorkItem> newWorkItems; List <WorkItem> updatedWorkItems; workItemMapper.MapWorkItems(sourceResult, destResult, out newWorkItems, out updatedWorkItems); // from http://social.msdn.microsoft.com/Forums/vstudio/en-US/0cbc378b-09ad-4899-865d-b418aecb8375/work-item-links-error-message-unexplained // "It happens when you add a link when you are creating a new work item. If you add the link after the new work item is saved then it works OK." eventSink.SavingWorkItems(newWorkItems, updatedWorkItems); var validWorkItems = new List <WorkItem>(); if (mapping.Mode.HasFlag(WorkItemsStageConfiguration.Modes.CreateThenUpdate)) { // uncommon path eventSink.UsingThreePassSavingAlgorithm(); SaveWorkItems3Passes(mapping, index, configuration.TestOnly, destWIStore, newWorkItems, updatedWorkItems, validWorkItems); // multi-pass records the same WI object multiple times validWorkItems = validWorkItems.DistinctBy(x => x.Id, null).ToList(); } else { // normal path var changedWorkItems = newWorkItems.Concat(updatedWorkItems).ToList(); var savedWorkItems = SaveWorkItems(mapping, index, destWIStore, changedWorkItems, configuration.TestOnly); validWorkItems.AddRange(savedWorkItems); }//if workItemMapper.CleanUp(); var linkMapper = new LinkMapper(context); var changedLinks = linkMapper.MapLinks(sourceResult.WorkItems.Values, validWorkItems); eventSink.SavingLinks(changedLinks, validWorkItems); SaveLinks(mapping, index, destWIStore, validWorkItems, configuration.TestOnly); return(saveErrors); }
internal void AgnosticCheck(WorkItemsStageConfiguration mapping) { if (mapping.HasIndex && System.IO.File.Exists(mapping.IndexFile)) { //TODO check indexFile is valid }//if var workItemMappings = mapping.WorkItemMappings.ToList(); var allSourceTypes = this.sourceWIStore.Projects[this.sourceProjectName].WorkItemTypes; var allDestTypes = this.destWIStore.Projects[this.destProjectName].WorkItemTypes; if (mapping.HasIndex) { // IDField is wrong workItemMappings.Where( m => m.IDField != null ) .ToList() .ForEach(t => Log("IDField cannot be used with IndexFile: found on WorkItem type '{0}' .", t.SourceType)); } else { workItemMappings .Where(m => m.IDField == null || string.IsNullOrWhiteSpace(m.IDField.Destination)) .ToList() .ForEach(t => Log("Invalid ID Field Destination on WorkItem type '{0}'." , t.DestinationType)); workItemMappings .Where(m => m.IDField == null || string.IsNullOrWhiteSpace(m.IDField.Source)) .ToList() .ForEach(t => Log("Invalid ID Field Source on WorkItem type '{0}'." , t.SourceType)); // check that all target types have an originating ID field workItemMappings .Where(m => m.IDField != null && !string.IsNullOrWhiteSpace(m.IDField.Destination) && !allDestTypes[m.DestinationType].FieldDefinitions.Contains(m.IDField.Destination)) .ToList() .ForEach(t => Log("Destination WorkItem type '{0}' has no '{1}' Field to host source ID." , t.DestinationType, t.IDField.Destination)); // check that source ID field match workItemMappings .Where(m => m.IDField != null && !string.IsNullOrWhiteSpace(m.IDField.Source) && !allSourceTypes[m.SourceType].FieldDefinitions.Contains(m.IDField.Source)) .ToList() .ForEach(t => Log("Source WorkItem type '{0}' has no source '{1}' ID Field." , t.SourceType, t.IDField.Source)); } // check Rules are valid foreach (var wiMapping in workItemMappings) { //TODO if MapState function then States is mandatory! foreach (var fieldRule in wiMapping.Fields) { bool isSetRule = !string.IsNullOrWhiteSpace(fieldRule.Set); bool isSetIfNullRule = !string.IsNullOrWhiteSpace(fieldRule.SetIfNull); // check combo, valid combos are: S+D S+D+T D+S D+Sif if (isSetRule || isSetIfNullRule) { // Set rule if (!string.IsNullOrWhiteSpace(fieldRule.Source)) { Log("Invalid Set rule for destination field '{1}/{0}'." , fieldRule.Destination, wiMapping.DestinationType); } if (!string.IsNullOrWhiteSpace(fieldRule.Source)) { Log("Invalid Set rule for destination field '{1}/{0}'." , fieldRule.Destination, wiMapping.DestinationType); } } else { if (string.IsNullOrWhiteSpace(fieldRule.Translate)) { // Copy rule if (string.IsNullOrWhiteSpace(fieldRule.Source)) { Log("Invalid Copy rule for destination field '{1}/{0}'." , fieldRule.Destination, wiMapping.DestinationType); } } else { // Translate rule if (string.IsNullOrWhiteSpace(fieldRule.Source)) { Log("Invalid Translate rule for destination field '{1}/{0}'." , fieldRule.Destination, wiMapping.DestinationType); } } }//if // check on Translator if (!string.IsNullOrWhiteSpace(fieldRule.Translate)) { var bindingFlags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic; var translatorMethod = typeof(MapperFunctions).GetMethod(fieldRule.Translate, bindingFlags); if (translatorMethod == null) { Log("Translator {0} does not exists.", fieldRule.Translate); } }//if // check on Set & SetIfNull if (isSetRule || isSetIfNullRule) { // TODO can fail! var destFieldType = this.destWIStore.FieldDefinitions[fieldRule.Destination].FieldType; string setValue = isSetRule ? fieldRule.Set : fieldRule.SetIfNull; bool parseOk = false; switch (destFieldType) { case FieldType.Boolean: bool _bool; parseOk = bool.TryParse(setValue, out _bool); break; case FieldType.DateTime: DateTime _DateTime; parseOk = DateTime.TryParse(setValue, out _DateTime); break; case FieldType.Double: double _double; parseOk = double.TryParse(setValue, out _double); break; case FieldType.Guid: Guid _Guid; parseOk = Guid.TryParse(setValue, out _Guid); break; case FieldType.History: Log("Destination field '{1}/{0}' has {2} type and cannot be set." , fieldRule.Destination, wiMapping.DestinationType, destFieldType); parseOk = false; break; case FieldType.Html: // string-like parseOk = true; break; case FieldType.Integer: int _int; parseOk = int.TryParse(setValue, out _int); break; case FieldType.Internal: Log("Destination field '{1}/{0}' has {2} type and cannot be set." , fieldRule.Destination, wiMapping.DestinationType, destFieldType); parseOk = false; break; case FieldType.PlainText: // string-like parseOk = true; break; case FieldType.String: parseOk = true; break; case FieldType.TreePath: // string-like parseOk = true; break; default: Log("Destination field '{1}/{0}' has unknown type {2}." , fieldRule.Destination, wiMapping.DestinationType, destFieldType); parseOk = false; break; }//switch if (!parseOk) { Log("Cannot set destination field '{1}/{0}' to '{2}': invalid value." , fieldRule.Destination, wiMapping.DestinationType, setValue); } //if } //if } //for } //for var allSourceLinkTypes = this.sourceWIStore.WorkItemLinkTypes; var allDestLinkTypes = this.destWIStore.WorkItemLinkTypes; foreach (var linkMap in mapping.LinkTypeMap) { if (linkMap.IsWildcard) { // * -> * === same name // * -> '' === not mapped if (linkMap.DestinationType != "*" && !string.IsNullOrWhiteSpace(linkMap.DestinationType)) { Log("Invalid Link wildcard rule."); } } if (!linkMap.IsWildcard) { if (!allSourceLinkTypes.Any(st => st.ForwardEnd.Name == linkMap.SourceType)) { Log("Source link type '{0}' does not exist.", linkMap.SourceType); } if (!allDestLinkTypes.Any(st => st.ForwardEnd.Name == linkMap.DestinationType)) { Log("Destination link type '{0}' does not exist.", linkMap.DestinationType); } // TODO check if mapping is sensible }//if } //TODO more checks, e.g. on fields and functions }