public WitDiffPair(WitDiffType diffType, string side1Name, IWITDiffItem side1DiffItem, string side2Name, IWITDiffItem side2DiffItem)
 {
     DiffType      = diffType;
     Side1Name     = side1Name;
     Side1DiffItem = side1DiffItem;
     Side2Name     = side2Name;
     Side2DiffItem = side2DiffItem;
 }
Example #2
0
        /// <summary>
        /// Enumerate the diff items found based on the query passed in as well as the filterString and version passed
        /// to InitializeForDiff.  The return type is IEnumerable<> so that adapter implementations do not need download and keep
        /// all of the IWITDiffItems in memory at once.
        /// </summary>
        /// <param name="queryCondition">A string that specifies a query used to select a subset of the work items defined by
        /// the set that the filter string identified.</param>
        /// <returns>An enumeration of IWITDiffItems each representing a work item to be compared by the WIT Diff operation</returns>
        public IEnumerable <IWITDiffItem> GetWITDiffItems(string queryCondition)
        {
            if (null == m_workItemPager)
            {
                StringBuilder conditionBuilder = new StringBuilder(m_filterString);

                if (!string.IsNullOrEmpty(queryCondition))
                {
                    if (conditionBuilder.Length > 0)
                    {
                        conditionBuilder.Append(" AND ");
                    }
                    conditionBuilder.Append(queryCondition);
                }

                m_workItemPager = new TfsWorkItemPager(m_projectName, m_workItemStore, conditionBuilder.ToString());
            }

            WorkItemCollection workItems;
            bool workItemFromProjectFound = false;

            do
            {
                workItems = m_workItemPager.GetNextPage();
                if (null == workItems)
                {
                    yield break;
                }
                else
                {
                    foreach (WorkItem workItem in workItems)
                    {
                        IWITDiffItem witDiffItem = null;
                        try
                        {
                            if (string.Equals(workItem.Project.Name, m_projectName, StringComparison.OrdinalIgnoreCase))
                            {
                                workItemFromProjectFound = true;
                                witDiffItem = (IWITDiffItem) new TfsWITDiffItem(this, workItem);
                            }
                        }
                        catch (DeniedOrNotExistException)
                        {
                            // Ignore this work item and continue
                        }
                        if (witDiffItem != null)
                        {
                            yield return(witDiffItem);
                        }
                    }
                }
            }
            // Continue with the next page if none of the work item in the current page are in the configured project
            while (workItems != null && !workItemFromProjectFound);
        }
Example #3
0
        /// <summary>
        /// Return a IWITDiffItem representing a single work item as identified by the adapter specific workItemId string
        /// </summary>
        /// <param name="workItemId"></param>
        /// <returns></returns>
        public IWITDiffItem GetWITDiffItem(string workItemIdStr)
        {
            IWITDiffItem tfsWitDiffItem = null;
            int          workItemId;

            if (int.TryParse(workItemIdStr, out workItemId))
            {
                try
                {
                    WorkItem workItem = m_workItemStore.GetWorkItem(workItemId);
                    tfsWitDiffItem = new TfsWITDiffItem(this, workItem);
                }
                catch (DeniedOrNotExistException)
                {
                    // Treat this as not found rather than an Exception so that processing continues
                    return(null);
                }
            }
            return(tfsWitDiffItem);
        }
        private void CompareWorkItemsFromOneSideToTheOther(
            string side1FilterString,
            string side1MigrationSourceId,
            MigrationSource side1MigrationSource,
            MigrationSource side2MigrationSource,
            IWITDiffProvider side1DiffProvider,
            IWITDiffProvider side2DiffProvider,
            string side1QueryCondition,
            HashSet <string> moreSide1FieldNamesToIgnore,
            HashSet <string> moreSide2FieldNamesToIgnore)
        {
            // Only left query condition specified
            side1DiffProvider.InitializeForDiff(side1FilterString, !m_serverDiffEngine.NoContentComparison);
            side2DiffProvider.InitializeForDiff(string.Empty, !m_serverDiffEngine.NoContentComparison);

            if (m_serverDiffEngine.NoContentComparison)
            {
                m_serverDiffEngine.LogVerbose(String.Format(CultureInfo.InvariantCulture, MigrationToolkitResources.WitDiffNoContentComparison));
            }

            foreach (IWITDiffItem side1WitDiffItem in side1DiffProvider.GetWITDiffItems(side1QueryCondition))
            {
                bool skipCurrentWorkItem      = false;
                bool fieldValuesMismatch      = false;
                Guid side1MigrationSourceGuid = new Guid(side1MigrationSourceId);

                IWITDiffItem side2WitDiffItem = null;
                string       side2WorkItemId  = TranslationService.TryGetTargetItemId(side1WitDiffItem.WorkItemId, side1MigrationSourceGuid);
                if (!string.IsNullOrEmpty(side2WorkItemId))
                {
                    side2WitDiffItem = side2DiffProvider.GetWITDiffItem(side2WorkItemId);
                }
                if (side2WitDiffItem == null)
                {
                    HandleDifference(new WitDiffPair(WitDiffType.NotMirrored,
                                                     side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem));
                    continue;
                }

                if (m_serverDiffEngine.NoContentComparison)
                {
                    /* Uncomment for debugging
                     * m_serverDiffEngine.LogVerbose(String.Format(CultureInfo.InvariantCulture, "Corresponding work item with Id {0} found on '{1}' for work item with Id {2} on '{3}",
                     *  side2WorkItemId, side2MigrationSource.FriendlyName, side1WitDiffItem.WorkItemId, side1MigrationSource.FriendlyName));
                     */
                }
                else
                {
                    TranslationService.MapWorkItemTypeFieldValues(side1WitDiffItem.WorkItemId, side1WitDiffItem.WorkItemDetails, side1MigrationSourceGuid);

                    XmlElement       side1RootElement     = side1WitDiffItem.WorkItemDetails.DocumentElement;
                    XmlElement       side2RootElement     = side2WitDiffItem.WorkItemDetails.DocumentElement;
                    HashSet <string> side2AttributesFound = new HashSet <string>();
                    foreach (XmlAttribute side1Attribute in side1RootElement.Attributes)
                    {
                        if (side1DiffProvider.IgnoreFieldInComparison(side1Attribute.Name) ||
                            side2DiffProvider.IgnoreFieldInComparison(side1Attribute.Name) ||
                            moreSide1FieldNamesToIgnore.Contains(side1Attribute.Name) ||
                            moreSide2FieldNamesToIgnore.Contains(side1Attribute.Name))
                        {
                            continue;
                        }

                        /* Uncomment for debugging
                         * m_serverDiffEngine.LogVerbose(String.Format(CultureInfo.InvariantCulture,
                         *  "Comparing field '{0}' from source '{1}'",
                         *  side1Attribute.Name, side1MigrationSource.FriendlyName));
                         */

                        string side2AttributeValue = side2RootElement.GetAttribute(side1Attribute.Name);
                        if (string.IsNullOrEmpty(side2AttributeValue))
                        {
                            WitDiffPair diffPair = new WitDiffPair(WitDiffType.DefinitionMismatch,
                                                                   side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem);
                            diffPair.AddMissingField(new WitDiffMissingField(side1Attribute.Name, side1MigrationSource.FriendlyName));
                            skipCurrentWorkItem = HandleDifference(diffPair);
                            break;
                        }
                        side2AttributesFound.Add(side1Attribute.Name);
                        if (!string.Equals(side1Attribute.Value, side2AttributeValue, StringComparison.OrdinalIgnoreCase))
                        {
                            WitDiffPair diffPair = new WitDiffPair(WitDiffType.DataMismatch,
                                                                   side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem);
                            diffPair.AddMismatchField(new WitDiffField(side1Attribute.Name, side1Attribute.Value, side2AttributeValue));
                            skipCurrentWorkItem = HandleDifference(diffPair);
                            break;
                        }
                        else
                        {
                            /* Uncomment for debugging
                             * m_serverDiffEngine.LogVerbose(String.Format(CultureInfo.InvariantCulture, "Match: The header field '{0}' for work item {1} has value '{2}' on both sides",
                             *  side1Attribute.Name, side1WitDiffItem.WorkItemId, side1Attribute.Value));
                             */
                        }
                    }
                    if (skipCurrentWorkItem)
                    {
                        continue;
                    }

                    foreach (XmlAttribute side2Attribute in side2RootElement.Attributes)
                    {
                        if (!side1DiffProvider.IgnoreFieldInComparison(side2Attribute.Name) &&
                            !side2DiffProvider.IgnoreFieldInComparison(side2Attribute.Name) &&
                            !moreSide1FieldNamesToIgnore.Contains(side2Attribute.Name) &&
                            !moreSide2FieldNamesToIgnore.Contains(side2Attribute.Name) &&
                            !side2AttributesFound.Contains(side2Attribute.Name))
                        {
                            WitDiffPair diffPair = new WitDiffPair(WitDiffType.DefinitionMismatch,
                                                                   side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem);
                            diffPair.AddMissingField(new WitDiffMissingField(side2Attribute.Name, side2MigrationSource.FriendlyName));
                            skipCurrentWorkItem = HandleDifference(diffPair);
                            break;
                        }
                    }
                    if (skipCurrentWorkItem)
                    {
                        continue;
                    }

                    XmlNodeList side1Columns = side1RootElement.SelectNodes("/WorkItemChanges/Columns/Column");
                    if (null == side1Columns)
                    {
                        m_diffResult.ProcessingErrors.Add(String.Format(CultureInfo.InvariantCulture, ServerDiffResources.InvalidXMLDocumentForDiffItem,
                                                                        side1MigrationSource.FriendlyName));
                        continue;
                    }

                    XmlNodeList side2Columns = side2RootElement.SelectNodes("/WorkItemChanges/Columns/Column");
                    if (null == side2Columns)
                    {
                        m_diffResult.ProcessingErrors.Add(String.Format(CultureInfo.InvariantCulture, ServerDiffResources.InvalidXMLDocumentForDiffItem,
                                                                        side2MigrationSource.FriendlyName));
                        continue;
                    }

                    Dictionary <string, XmlNode> side2ColumnsByReferenceName = new Dictionary <string, XmlNode>();
                    foreach (XmlNode side2Column in side2Columns)
                    {
                        string referenceName = GetReferenceNameFromFieldColumn(side2Column);
                        if (referenceName != null)
                        {
                            if (!side2ColumnsByReferenceName.ContainsKey(referenceName))
                            {
                                side2ColumnsByReferenceName.Add(referenceName, side2Column);
                            }
                        }
                    }

                    for (int i = 0; i < side1Columns.Count; i++)
                    {
                        XmlNode side1Column        = side1Columns[i];
                        string  side1ReferenceName = GetReferenceNameFromFieldColumn(side1Column);
                        if (side1ReferenceName == null)
                        {
                            WitDiffPair diffPair = new WitDiffPair(WitDiffType.InvalidDefinition, side1MigrationSource.FriendlyName, side1WitDiffItem);
                            skipCurrentWorkItem = HandleDifference(diffPair, true);
                            break;
                        }
                        XmlNode side2Column;
                        if (!side2ColumnsByReferenceName.TryGetValue(side1ReferenceName, out side2Column))
                        {
                            WitDiffPair diffPair = new WitDiffPair(WitDiffType.DefinitionMismatch,
                                                                   side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem);

                            diffPair.AddMissingField(new WitDiffMissingField(side1ReferenceName, side1MigrationSource.FriendlyName));

                            skipCurrentWorkItem = HandleDifference(diffPair, true);
                            break;
                        }
                        string side2ReferenceName = GetReferenceNameFromFieldColumn(side2Column);
                        // Remove the side2Column from the Dictionary
                        side2ColumnsByReferenceName.Remove(side2ReferenceName);

                        string fieldDisplayName = null;

                        const string c_displayName    = "DisplayName";
                        XmlAttribute side1DisplayName = side1Column.Attributes[c_displayName];
                        if (side1DisplayName != null && !string.IsNullOrEmpty(side1DisplayName.Value))
                        {
                            fieldDisplayName = side1DisplayName.Value;
                        }
                        else
                        {
                            fieldDisplayName = side1ReferenceName;
                        }

                        if (!side1DiffProvider.IgnoreFieldInComparison(side1ReferenceName) &&
                            !side2DiffProvider.IgnoreFieldInComparison(side1ReferenceName) &&
                            !moreSide1FieldNamesToIgnore.Contains(side1ReferenceName) &&
                            !moreSide2FieldNamesToIgnore.Contains(side1ReferenceName))
                        {
                            if (!side1Column.HasChildNodes)
                            {
                                m_diffResult.ProcessingErrors.Add(String.Format(MigrationToolkitResources.WitDiffWorkItemDescriptionMissingChildNodes,
                                                                                side1WitDiffItem.WorkItemId, side1MigrationSource.FriendlyName, side1Column.Name));
                                continue;
                            }
                            if (!side2Column.HasChildNodes)
                            {
                                m_diffResult.ProcessingErrors.Add(String.Format(MigrationToolkitResources.WitDiffWorkItemDescriptionMissingChildNodes,
                                                                                side2WitDiffItem.WorkItemId, side2MigrationSource.FriendlyName, side2Column.Name));
                                continue;
                            }

                            XmlNode side1ValueNode = side1Column.FirstChild;
                            XmlNode side2ValueNode = side2Column.FirstChild;
                            if (!string.Equals(side1ValueNode.InnerText.Replace("\r\n", "\n"), side2ValueNode.InnerText.Replace("\r\n", "\n"), StringComparison.OrdinalIgnoreCase))
                            {
                                WitDiffPair diffPair = new WitDiffPair(WitDiffType.DataMismatch,
                                                                       side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem);
                                diffPair.AddMismatchField(new WitDiffField(fieldDisplayName, side1ValueNode.InnerText, side2ValueNode.InnerText));
                                skipCurrentWorkItem = HandleDifference(diffPair);
                                // If there's a data mismatch, continue to compare fields to report additional data mismatches
                                // unless HandleDifference returns true to skip the entire work item
                                if (skipCurrentWorkItem)
                                {
                                    break;
                                }
                                fieldValuesMismatch = true;
                            }
                            else
                            {
                                /* Uncomment for debugging
                                 * m_serverDiffEngine.LogVerbose(String.Format(CultureInfo.InvariantCulture, "Match: The field '{0}' for work item {1} has value '{2}' on both sides",
                                 *  fieldDisplayName, side1WitDiffItem.WorkItemId, side1ValueNode.InnerText));
                                 */
                            }
                        }
                    }

                    bool attachmentsMismatch = false;
                    bool linksMismatch       = false;
                    if (!skipCurrentWorkItem)
                    {
                        // Compare Attachments
                        if (side1WitDiffItem.Attachments.Count != side2WitDiffItem.Attachments.Count)
                        {
                            WitDiffPair diffPair = new WitDiffPair(WitDiffType.AttachmentCount,
                                                                   side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem);
                            skipCurrentWorkItem = HandleDifference(diffPair);
                            attachmentsMismatch = true;
                        }
                        else
                        {
                            Dictionary <string, IMigrationFileAttachment> side2AttachmentsById = new Dictionary <string, IMigrationFileAttachment>();
                            foreach (IMigrationFileAttachment side2Attachment in side2WitDiffItem.Attachments)
                            {
                                string attachmentId = GetAttachmentId(side2Attachment);
                                if (!side2AttachmentsById.ContainsKey(attachmentId))
                                {
                                    side2AttachmentsById.Add(attachmentId, side2Attachment);
                                }
                            }
                            HashSet <string> side1AttachmentIds = new HashSet <string>();
                            foreach (IMigrationFileAttachment side1Attachment in side1WitDiffItem.Attachments)
                            {
                                string side1AttachmentId = GetAttachmentId(side1Attachment);
                                if (side1AttachmentIds.Contains(side1AttachmentId))
                                {
                                    // This is a duplicate attachment; continue to ignore the duplicate
                                    continue;
                                }
                                side1AttachmentIds.Add(side1AttachmentId);
                                IMigrationFileAttachment side2Attachment;
                                if (side2AttachmentsById.TryGetValue(side1AttachmentId, out side2Attachment))
                                {
                                    side2AttachmentsById.Remove(side1AttachmentId);
                                    WitDiffAttachment diffAttachment;
                                    if (!AttachmentsMatch(side1Attachment, side2Attachment, out diffAttachment))
                                    {
                                        WitDiffPair diffPair = new WitDiffPair(WitDiffType.AttachmentMismatch,
                                                                               side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem);
                                        diffPair.AddMistmatchedAttachment(diffAttachment);
                                        skipCurrentWorkItem = HandleDifference(diffPair);
                                        attachmentsMismatch = true;
                                    }
                                }
                                else
                                {
                                    WitDiffPair diffPair = new WitDiffPair(WitDiffType.AttachmentMissing,
                                                                           side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem);
                                    diffPair.AddMissingAttachment(side1Attachment.Name);
                                    skipCurrentWorkItem = HandleDifference(diffPair);
                                    attachmentsMismatch = true;
                                }
                                if (skipCurrentWorkItem)
                                {
                                    break;
                                }
                            }

                            if (!skipCurrentWorkItem)
                            {
                                // Any attachments still in side2AttachmentsByKey were not in side1
                                foreach (IMigrationFileAttachment side2Attachment in side2AttachmentsById.Values)
                                {
                                    WitDiffPair diffPair = new WitDiffPair(WitDiffType.AttachmentMissing,
                                                                           side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem);
                                    diffPair.AddMissingAttachment(side2Attachment.Name);
                                    skipCurrentWorkItem = HandleDifference(diffPair);
                                    attachmentsMismatch = true;
                                }
                            }
                        }
                    }

                    if (!skipCurrentWorkItem)
                    {
                        // Compare links
                        if (side1WitDiffItem.Links.Count != side2WitDiffItem.Links.Count)
                        {
                            WitDiffPair diffPair = new WitDiffPair(WitDiffType.LinkCount,
                                                                   side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem);
                            skipCurrentWorkItem = HandleDifference(diffPair);
                            linksMismatch       = true;
                        }

                        /* Commenting out the detailed comparison of each link for now as it does not work because the link artifact URIs
                         * need to be translated.  This requires some refactoring of translation methods current embedded in the LinkEngine
                         * that are not easily used outside the context of a full running migration/sync session.
                         * else
                         * {
                         *  Dictionary<string, ILink> side2LinksById = new Dictionary<string, ILink>();
                         *  foreach (ILink side2Link in side2WitDiffItem.Links)
                         *  {
                         *      string side2LinkId = GetLinkId(side2Link);
                         *      if (!side2LinksById.ContainsKey(side2LinkId))
                         *      {
                         *          side2LinksById.Add(side2LinkId, side2Link);
                         *      }
                         *  }
                         *  foreach (ILink side1Link in side1WitDiffItem.Links)
                         *  {
                         *      ILink side2Link;
                         *      string side1LinkId = GetLinkId(side1Link);
                         *      if (side2LinksById.TryGetValue(side1LinkId, out side2Link))
                         *      {
                         *          side2LinksById.Remove(side1LinkId);
                         *          WitDiffLink diffLink;
                         *          if (!LinksMatch(new Guid(side1MigrationSource.InternalUniqueId), side1Link, new Guid(side2MigrationSource.InternalUniqueId), side2Link, out diffLink))
                         *          {
                         *              WitDiffPair diffPair = new WitDiffPair(WitDiffType.LinkMismatch,
                         *                  side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem);
                         *              diffPair.AddMistmatchedLink(diffLink);
                         *              skipCurrentWorkItem = HandleDifference(diffPair);
                         *              linksMismatch = true;
                         *              break;
                         *          }
                         *
                         *      }
                         *      else
                         *      {
                         *          WitDiffPair diffPair = new WitDiffPair(WitDiffType.LinkMisssing,
                         *              side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem);
                         *          diffPair.AddMissingLink(side1LinkId);
                         *          skipCurrentWorkItem = HandleDifference(diffPair);
                         *          linksMismatch = true;
                         *      }
                         *  }
                         *
                         *  if (!skipCurrentWorkItem)
                         *  {
                         *      // Any links still in side2LinksById were not in side1
                         *      foreach (ILink side2link in side2LinksById.Values)
                         *      {
                         *          WitDiffPair diffPair = new WitDiffPair(WitDiffType.LinkMisssing,
                         *              side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem);
                         *          diffPair.AddMissingLink(GetLinkId(side2link));
                         *          skipCurrentWorkItem = HandleDifference(diffPair);
                         *          linksMismatch = true;
                         *      }
                         *  }
                         * }
                         */
                    }

                    if (skipCurrentWorkItem || fieldValuesMismatch || attachmentsMismatch || linksMismatch)
                    {
                        continue;
                    }

                    m_serverDiffEngine.LogVerbose(String.Format(CultureInfo.InvariantCulture, MigrationToolkitResources.WitDiffWorkItemsMatch,
                                                                side1WitDiffItem.WorkItemId, side1MigrationSource.FriendlyName, side2WorkItemId, side2MigrationSource.FriendlyName));
                }
            }
        }