/// <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); } }
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); }
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); }
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); } } }
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); }