/// <summary>
        /// Download the XML document describing this CQ record
        /// </summary>
        /// <remarks>
        /// EntityDisplayName, EntityDefName and CQSession properties are needed to retrieve record details
        /// </remarks>
        /// <param name="localPath"></param>
        public void Download(string localPath)
        {
            if (null == CQSession)
            {
                throw new InvalidOperationException("CQSession == NULL");
            }

            if (File.Exists(localPath))
            {
                File.Delete(localPath);
            }

            if (IsRecordHistory)
            {
                // download the record history
                OAdEntity   record         = CQWrapper.GetEntity(CQSession, EntityDefName, EntityDispName);
                XmlDocument recordHistDesc = CreateRecordHistoryDesc(record, HistoryValue, HistoryFieldName + "::" + Version, false);
                recordHistDesc.Save(localPath);
            }
            else
            {
                // download the *complete* record content (field + value)
                OAdEntity   record     = CQWrapper.GetEntity(CQSession, EntityDefName, EntityDispName);
                XmlDocument recordDesc = CreateRecordDesc(record, Version, null, false);
                recordDesc.Save(localPath);
            }
        }
        private void EditRecord(IMigrationAction action, ConversionResult convRslt)
        {
            string ownerRecordDisplayName = FindTargetWorkItemId(action);
            string ownerRecordType        = UtilityMethods.ExtractRecordType(action);
            string changeAuthor           = UtilityMethods.ExtractAuthor(action);

            // find the entity
            OAdEntity entity = CQWrapper.GetEntity(m_userSession, ownerRecordType, ownerRecordDisplayName);

            XmlNodeList columns = action.MigrationActionDescription.SelectNodes("/WorkItemChanges/Columns/Column");

            if (null == columns)
            {
                throw new MigrationException(ClearQuestResource.ClearQuest_Error_InvalidActionDescription, action.ActionId);
            }

            string  stateField            = m_migrationContext.GetStateField(ownerRecordType);
            XmlNode stateTransitFieldNode = UtilityMethods.ExtractSingleFieldNodeFromMigrationDescription(
                action.MigrationActionDescription, stateField);
            bool containsStateTransit = (stateTransitFieldNode != null);

            if (containsStateTransit)
            {
                // change contains state transition
                List <string> skipFields;
                if (ChangeRecordState(entity, action, convRslt, stateTransitFieldNode, out skipFields))
                {
                    ModifyRecordContent(entity, action, convRslt, skipFields);
                }
            }
            else
            {
                ModifyRecordContent(entity, action, convRslt, null);
            }
        }
예제 #3
0
        public ChangeSummary GetSummaryOfChangesSince(string lastProcessedChangeItemId, List <string> filterStrings)
        {
            // lastProcessedChangeItemId is in a form such as "Defect:UCM0100019437:history:0"
            // Parse it and query for the record with that Id to get the time is was last changed
            string[]  identity            = UtilityMethods.ParseCQRecordMigrationItemId(lastProcessedChangeItemId);
            OAdEntity lastProcessedRecord = CQWrapper.GetEntity(m_userSession, identity[0], identity[1]);
            string    lastProcessedRecordAuthor;
            DateTime  lastProcessedRecordChangeDate;

            ClearQuestRecordItem.FindLastRevDtls(lastProcessedRecord, out lastProcessedRecordAuthor, out lastProcessedRecordChangeDate);

            string lastProcessedRecordChangeDateStr = lastProcessedRecordChangeDate.ToString("u").Replace("Z", ""); // using "ISO 8601" DateTime string format

            if (lastProcessedRecordChangeDateStr.LastIndexOf('.') >= 0)
            {
                lastProcessedRecordChangeDateStr = lastProcessedRecordChangeDateStr.Substring(0, lastProcessedRecordChangeDateStr.LastIndexOf('.')); // drop the millisec
            }

            ChangeSummary changeSummary = new ChangeSummary();

            changeSummary.ChangeCount = 0;
            changeSummary.FirstChangeModifiedTimeUtc = DateTime.MinValue;

            foreach (CQRecordFilter filter in m_filters)
            {
                CQRecordQueryBase recordQuery =
                    CQRecordQueryFactory.CreatQuery(m_userSession, filter, lastProcessedRecordChangeDateStr, this);

                foreach (OAdEntity record in recordQuery)
                {
                    // HACK HACK
                    if (record != null) // this if check is HACK
                    {
                        DateTime lastChangeDate;
                        string   lastAuthor;
                        ClearQuestRecordItem.FindLastRevDtls(record, out lastAuthor, out lastChangeDate);

                        // Make sure the lastChangeDate on this record is after the lastProcessedRecordChangeDate before counting it in the backclog
                        // because the query issued above is imprecise because the milliseconds are dropped
                        if (lastChangeDate > lastProcessedRecordChangeDate)
                        {
                            changeSummary.ChangeCount++;
                            DateTime lastChangeDateUtc = lastChangeDate.ToUniversalTime();
                            if (changeSummary.FirstChangeModifiedTimeUtc == DateTime.MinValue ||
                                lastChangeDateUtc < changeSummary.FirstChangeModifiedTimeUtc)
                            {
                                changeSummary.FirstChangeModifiedTimeUtc = lastChangeDateUtc;
                            }
                        }
                    }
                }
            }

            return(changeSummary);
        }
예제 #4
0
        public void BatchSubmitLinkChange(LinkChangeGroup linkChanges)
        {
            if (linkChanges.Actions.Count == 0)
            {
                linkChanges.Status = LinkChangeGroup.LinkChangeGroupStatus.Completed;
                return;
            }

            // group changes by work item Id
            Dictionary <string, List <LinkChangeAction> > perRecordLinkChanges = RegroupLinkChangeActions(linkChanges);

            WorkItemLinkStore relatedArtifactsStore = new WorkItemLinkStore(m_configurationService.SourceId);

            // batch-submit links of each cq record
            bool successForAllActions = true;

            foreach (var perWorkItemLinkChange in perRecordLinkChanges)
            {
                string[]  identity   = UtilityMethods.ParseCQRecordMigrationItemId(perWorkItemLinkChange.Key);
                OAdEntity hostEntity = CQWrapper.GetEntity(m_userSession, identity[0], identity[1]);

                foreach (LinkChangeAction linkChangeAction in perWorkItemLinkChange.Value)
                {
                    if (linkChangeAction.Status != LinkChangeAction.LinkChangeActionStatus.ReadyForMigration ||
                        linkChangeAction.IsConflicted)
                    {
                        continue;
                    }

                    var handler = linkChangeAction.Link.LinkType as ILinkHandler;
                    Debug.Assert(null != handler, "linktype is not an ILinkHandler");
                    if (!handler.Update(m_migrationContext, m_userSession, hostEntity, linkChangeAction))
                    {
                        successForAllActions = false;
                        // [teyang] todo conflict handling
                        linkChangeAction.Status       = LinkChangeAction.LinkChangeActionStatus.Completed;
                        linkChangeAction.IsConflicted = true;
                        TraceManager.TraceError("Failed processing link change action: {0} linked to {1}",
                                                linkChangeAction.Link.SourceArtifact.Uri,
                                                linkChangeAction.Link.TargetArtifact.Uri);
                    }
                    else
                    {
                        MarkLinkChangeActionCompleted(linkChangeAction, relatedArtifactsStore);
                    }
                }
            }

            linkChanges.Status = successForAllActions
                                 ? LinkChangeGroup.LinkChangeGroupStatus.Completed
                                 : LinkChangeGroup.LinkChangeGroupStatus.ReadyForMigration;
        }
        internal ChangeGroup CreateChangeGroup(
            ChangeGroupService changeGroupService,
            ClearQuestMigrationContext migrationContext,
            bool isLastRevOfThisSyncCycle)
        {
            ChangeGroup changeGroup = changeGroupService.CreateChangeGroupForDeltaTable(ChangeGroupName);
            OAdEntity   record      = CQWrapper.GetEntity(CQSession, EntityDefName, EntityDispName);

            if (IsRecordHistory)
            {
                XmlDocument recordHistDesc = CreateRecordHistoryDesc(record, HistoryValue, HistoryFieldName + "::" + Version, isLastRevOfThisSyncCycle);

                changeGroup.CreateAction(WellKnownChangeActionId.Edit,
                                         this,
                                         MigrationRecordId,
                                         "",
                                         Version,
                                         "",
                                         WellKnownContentType.WorkItem.ReferenceName,
                                         recordHistDesc);
            }
            else
            {
                XmlDocument recordDesc = CreateRecordDesc(record, Version, migrationContext, isLastRevOfThisSyncCycle);

                if (Version.Equals(NewRecordVersion, StringComparison.InvariantCulture))
                {
                    changeGroup.CreateAction(WellKnownChangeActionId.Add,
                                             this,
                                             MigrationRecordId,
                                             "",
                                             Version,
                                             "",
                                             WellKnownContentType.WorkItem.ReferenceName,
                                             recordDesc);
                }
                else
                {
                    changeGroup.CreateAction(WellKnownChangeActionId.Edit,
                                             this,
                                             MigrationRecordId,
                                             "",
                                             Version,
                                             "",
                                             WellKnownContentType.WorkItem.ReferenceName,
                                             recordDesc);
                }
            }


            return(changeGroup);
        }
예제 #6
0
        public List <ILink> GetLinks(
            IArtifact sourceArtifact,
            LinkType linkType)
        {
            string id;
            bool   idExtractionRslt = TryExtractArtifactId(sourceArtifact, out id);

            Debug.Assert(idExtractionRslt);

            string[]  identity   = UtilityMethods.ParseCQRecordMigrationItemId(id);
            OAdEntity hostEntity = CQWrapper.GetEntity(m_userSession, identity[0], identity[1]);

            var links = new List <ILink>();

            if (null == ExtractLinkChangeActionsCallback)
            {
                return(links);
            }

            var perWorkItemlinkChangeGroups = new List <LinkChangeGroup>();

            ExtractLinkChangeActionsCallback(m_userSession, hostEntity, perWorkItemlinkChangeGroups);

            foreach (LinkChangeGroup group in perWorkItemlinkChangeGroups)
            {
                foreach (LinkChangeAction action in group.Actions)
                {
                    if (!CQStringComparer.LinkType.Equals(action.Link.LinkType.ReferenceName, linkType.ReferenceName))
                    {
                        continue;
                    }

                    string mappedLinkType = m_linkConfigLookupService.FindMappedLinkType(m_configurationService.SourceId, action.Link.LinkType.ReferenceName);
                    if (!m_linkTranslationService.LinkTypeSupportedByOtherSide(mappedLinkType))
                    {
                        continue;
                    }

                    if (!links.Contains(action.Link))
                    {
                        links.Add(action.Link);
                    }
                }
            }

            return(links);
        }
        public bool Update(
            ClearQuestMigrationContext migrationContext,
            Session cqSession,
            OAdEntity hostRecord,
            LinkChangeAction linkChangeAction)
        {
            if (null == linkChangeAction)
            {
                throw new ArgumentNullException("linkChangeAction");
            }

            if (!linkChangeAction.Link.LinkType.ReferenceName.Equals(REFERENCE_NAME))
            {
                throw new ArgumentException("Link type mismatch.");
            }

            string childRecEntityDefName;

            if (!ClearQuestRecordArtifactHandler.TryExtractRecordDefName(linkChangeAction.Link.TargetArtifact,
                                                                         out childRecEntityDefName))
            {
                return(false);
            }
            string childRecDispName;

            if (!ClearQuestRecordArtifactHandler.TryExtractRecordDispName(linkChangeAction.Link.TargetArtifact,
                                                                          out childRecDispName))
            {
                return(false);
            }

            OAdEntity childEntity = CQWrapper.GetEntity(cqSession, childRecEntityDefName, childRecDispName);

            if (null == childEntity)
            {
                return(false);
            }

            // check if hostRecord already has a duplicate of this childRecord
            bool duplicateAlreadyExist = HasDuplicateRecord(hostRecord, childRecEntityDefName, childRecDispName);

            // find out the child entity's current state
            // find the current state
            string childEntityDefName = CQWrapper.GetEntityDefName(childEntity);

            OAdFieldInfo aFldInfo       = CQWrapper.GetEntityFieldValue(childEntity, migrationContext.GetStateField(childEntityDefName));
            string       srcState       = CQWrapper.GetFieldValue(aFldInfo);
            OAdEntityDef childEntityDef = CQWrapper.GetEntityDef(cqSession, childEntityDefName);

            if (linkChangeAction.ChangeActionId.Equals(WellKnownChangeActionId.Add))
            {
                if (duplicateAlreadyExist)
                {
                    // [teyang] TODO
                    return(false);
                }

                string[] changeActionNames = CQUtilityMethods.FindAllActionNameByTypeAndSourceState(
                    childEntityDef,
                    srcState,
                    CQConstants.ACTION_DUPLICATE);

                string changeActionName = string.Empty;
                if (changeActionNames.Length == 0)
                {
                    // [teyang] TODO conflict
                }
                else if (changeActionNames.Length > 1)
                {
                    // [teyang] TODO conflict
                }
                else
                {
                    changeActionName = changeActionNames[0];
                }

                if (!string.IsNullOrEmpty(changeActionName))
                {
                    CQWrapper.MarkEntityAsDuplicate(cqSession, childEntity, hostRecord, changeActionName);

                    string retVal = CQWrapper.Validate(childEntity);
                    if (string.IsNullOrEmpty(retVal))
                    {
                        // [teyang] TODO conflict
                    }

                    retVal = CQWrapper.Commmit(childEntity);
                    if (string.IsNullOrEmpty(retVal))
                    {
                        // [teyang] TODO conflict
                    }
                }
            }
            else if (linkChangeAction.ChangeActionId.Equals(WellKnownChangeActionId.Delete))
            {
                if (!duplicateAlreadyExist)
                {
                    // [teyang] TODO
                    return(false);
                }

                string[] changeActionNames = CQUtilityMethods.FindAllActionNameByTypeAndSourceState(
                    childEntityDef,
                    srcState,
                    CQConstants.ACTION_UNDUPLICATE);

                string changeActionName = string.Empty;
                if (changeActionNames.Length == 0)
                {
                    // [teyang] TODO conflict
                }
                else if (changeActionNames.Length > 1)
                {
                    // [teyang] TODO conflict
                }
                else
                {
                    changeActionName = changeActionNames[0];
                }

                if (!string.IsNullOrEmpty(changeActionName))
                {
                    CQWrapper.UnmarkEntityAsDuplicate(cqSession, childEntity, changeActionName);

                    string retVal = CQWrapper.Validate(childEntity);
                    if (string.IsNullOrEmpty(retVal))
                    {
                        // [teyang] TODO conflict
                    }

                    retVal = CQWrapper.Commmit(childEntity);
                    if (string.IsNullOrEmpty(retVal))
                    {
                        // [teyang] TODO conflict
                    }
                }
            }
            else
            {
                //throw new MigrationException(TfsWITAdapterResources.ErrorUnsupportedChangeAction);
            }

            return(true);
        }
        private void AddAttachment(IMigrationAction action, ConversionResult convRslt)
        {
            string attName                = UtilityMethods.ExtractAttachmentName(action);
            string lengthStr              = UtilityMethods.ExtractAttachmentLength(action);
            string attComment             = UtilityMethods.ExtractAttachmentComment(action);
            string ownerRecordDisplayName = FindTargetWorkItemId(action);
            string ownerRecordType        = UtilityMethods.ExtractRecordType(action);

            string filePath = Path.Combine(m_configurationService.WorkspaceRoot,
                                           ownerRecordDisplayName + attName);

            try
            {
                // find the entity
                OAdEntity entity = CQWrapper.GetEntity(m_userSession, ownerRecordType, ownerRecordDisplayName);

                if (AttachmentExists(entity, attName, attComment, lengthStr))
                {
                    action.State = ActionState.Skipped;
                    return;
                }


                // find the change action def name
                string       entityDefName = CQWrapper.GetEntityDefName(entity);
                OAdEntityDef entityDef     = CQWrapper.GetEntityDef(m_userSession, entityDefName);

                string modifyActionDefName = FindCQActionDefName(entityDef, CQConstants.ACTION_MODIFY);

                // mark entity to be editable
                CQWrapper.EditEntity(m_userSession, entity, modifyActionDefName);

                // cache the current history count for all "history fields"
                // i.e. pairs of HistoryFieldName, count
                Dictionary <string, int> recordHistoryCountCache = new Dictionary <string, int>();
                BuildRecordHistoryCountCache(entity, recordHistoryCountCache);

                action.SourceItem.Download(filePath);

                string attachmentField = m_migrationContext.GetAttachmentSinkField(entityDefName);
                string retVal          = CQWrapper.AddAttachmentFieldValue(entity, attachmentField, attName, filePath);
                if (!string.IsNullOrEmpty(retVal))
                {
                    // teyang_todo attachment conflict
                }

                retVal = CQWrapper.Validate(entity);
                if (!string.IsNullOrEmpty(retVal))
                {
                    // [teyang] TODO conflict management
                    throw new InvalidOperationException();
                }

                retVal = CQWrapper.Commmit(entity);
                if (!string.IsNullOrEmpty(retVal))
                {
                    // [teyang] TODO conflict management
                    throw new InvalidOperationException();
                }

                if (action.State == ActionState.Pending)
                {
                    action.State = ActionState.Complete;
                }

                // *********
                // now comparing to the cache, so that we can clearly identify the item:version pairs
                // e.g. TargetCQRecordDisplayName : HistoryFieldName::LatestHistoryIndex
                Dictionary <string, int[]> updatedHistoryIndices = new Dictionary <string, int[]>();
                FindUpdatedHistoryIndices(entity, recordHistoryCountCache, updatedHistoryIndices);
                recordHistoryCountCache.Clear();

                foreach (string histFieldName in updatedHistoryIndices.Keys)
                {
                    foreach (int histIndex in updatedHistoryIndices[histFieldName])
                    {
                        UpdateConversionHistory(action,
                                                ownerRecordDisplayName,
                                                CQHistoryMigrationItem.CreateHistoryItemVersion(histFieldName, histIndex),
                                                convRslt);
                    }
                }
            }
            finally
            {
                if (File.Exists(filePath))
                {
                    File.Delete(filePath);
                }
            }
        }
예제 #9
0
        public void Download(string localPath)
        {
            byte[] metadataHash = HashAttachmentMetadata(Name, Comment, DisplayName, Length);

            if (null == CQSession)
            {
                throw new InvalidOperationException("CQSession == NULL");
            }

            // [teyang] TODO: validation on localPath
            if (File.Exists(localPath))
            {
                File.Delete(localPath);
            }

            OAdEntity aRecord = CQWrapper.GetEntity(CQSession, EntityDefName, EntityDispName);

            OAdAttachmentFields aAllAttFields = CQWrapper.GetAttachmentFields(aRecord);

            bool attachmentFound = false;

            for (int attFieldsIndex = 0;
                 attFieldsIndex < CQWrapper.AttachmentsFieldsCount(aAllAttFields);
                 attFieldsIndex++)
            {
                object             ob        = (object)attFieldsIndex;
                OAdAttachmentField aAttField = CQWrapper.AttachmentsFieldsItem(aAllAttFields, ref ob);

                string fieldName = CQWrapper.GetAttachmentFieldName(aAttField);

                if (!CQStringComparer.FieldName.Equals(fieldName, this.FieldName))
                {
                    // not the hosting attachment field, try next one
                    continue;
                }

                // attachment field is found, now look for the attachment
                OAdAttachments aAttachments = CQWrapper.GetAttachments(aAttField);
                OAdAttachment  aAttachment  = null;
                for (int attachmentIndex = 0;
                     attachmentIndex < CQWrapper.AttachmentsCount(aAttachments);
                     attachmentIndex++)
                {
                    object obIndex = (object)attachmentIndex;
                    aAttachment = CQWrapper.AttachmentsItem(aAttachments, ref obIndex);

                    string name;
                    string comment;
                    string dispName;
                    int    fileSize;
                    CQWrapper.GetAttachmentMetadata(aAttachment, out name, out comment, out dispName, out fileSize);

                    byte[] hash = HashAttachmentMetadata(name, comment, dispName, (long)fileSize);
                    if (HashProducer.CompareMD5(metadataHash, hash) == 0)
                    {
                        attachmentFound = true;
                        break;
                    }
                }

                if (attachmentFound)
                {
                    Debug.Assert(null != aAttachment, "null == aAttachment");
                    CQWrapper.LoadAttachment(aAttachment, localPath);
                }

                // we've checked the host att field already, no need to proceed
                break;
            }

            if (!attachmentFound)
            {
                // [teyang] TODO: typed exception, AttachmentNotFound conflict handling
                throw new Exception(string.Format("attachment '{0}' is not found", Name));
            }
        }
        public void ExtractLinkChangeActions(
            Session session,
            OAdEntity hostRecord,
            List <LinkChangeGroup> linkChangeGroups)
        {
            string hostRecDispName      = CQWrapper.GetEntityDisplayName(hostRecord);
            string hostRecEntityDefName = CQWrapper.GetEntityDefName(hostRecord);

            if (string.IsNullOrEmpty(hostRecEntityDefName) ||
                !CQStringComparer.EntityName.Equals(hostRecEntityDefName, this.m_hostRecordType))
            {
                return;
            }

            string hostRecMigrItemId = UtilityMethods.CreateCQRecordMigrationItemId(hostRecEntityDefName, hostRecDispName);

            var linkChangeGroup = new LinkChangeGroup(hostRecMigrItemId, LinkChangeGroup.LinkChangeGroupStatus.Created, false);

            OAdFieldInfo fldInfo     = CQWrapper.GetEntityFieldValue(hostRecord, m_referenceFieldName);
            int          cqFieldType = CQWrapper.GetFieldType(fldInfo);

            if (cqFieldType == CQConstants.FIELD_REFERENCE)
            {
                // get the current entity def handle
                OAdEntityDef curEntityDef          = CQWrapper.GetEntityDef(session, hostRecEntityDefName);
                OAdEntityDef refEntityDef          = CQWrapper.GetFieldReferenceEntityDef(curEntityDef, m_referenceFieldName);
                string       childRecEntityDefName = CQWrapper.GetEntityDefName(refEntityDef);

                int valueStatus = CQWrapper.GetFieldValueStatus(fldInfo);
                if (valueStatus == (int)CQConstants.FieldStatus.HAS_VALUE)
                {
                    // single value required
                    string refFldVal = CQWrapper.GetFieldValue(fldInfo);
                    if (!CQStringComparer.RecordName.Equals(refFldVal, hostRecDispName))
                    {
                        // NOT a reference to self.. Note TFS cannot have a reference to self
                        OAdEntity childRecord = CQWrapper.GetEntity(session, childRecEntityDefName, refFldVal);
                        if (null != childRecord)
                        {
                            string childRecDispName   = CQWrapper.GetEntityDisplayName(childRecord);
                            string childRecMigrItemId = UtilityMethods.CreateCQRecordMigrationItemId(
                                childRecEntityDefName, childRecDispName);

                            ILink refFieldLink = new ArtifactLink(
                                hostRecDispName,
                                new Artifact(hostRecMigrItemId, new ClearQuestRecordArtifactType()),
                                new Artifact(childRecMigrItemId, new ClearQuestRecordArtifactType()),
                                string.Empty,
                                this);

                            LinkChangeAction action = new LinkChangeAction(
                                WellKnownChangeActionId.Add,
                                refFieldLink,
                                LinkChangeAction.LinkChangeActionStatus.Created,
                                false);

                            linkChangeGroup.AddChangeAction(action);
                        }
                        else
                        {
                            // [teyang] TODO replace debug assertion with a conflict?
                            Debug.Assert(false, "null == childRecord");
                        }
                    }
                }
            }

            linkChangeGroups.Add(linkChangeGroup);
        }