public ClearQuestAttachmentItem( OAdEntity aHostRecord, OAdAttachmentField aHostField, OAdAttachment aAttachment, ClearQuestConnectionConfig connectionConfiguration) { // gather info to query for the record EntityDefName = CQWrapper.GetEntityDefName(aHostRecord); EntityDispName = CQWrapper.GetEntityDisplayName(aHostRecord); // gather info to query for attachment field FieldName = CQWrapper.GetAttachmentFieldName(aHostField); string name; string comment; string dispName; int fileSize; CQWrapper.GetAttachmentMetadata(aAttachment, out name, out comment, out dispName, out fileSize); Name = name; Comment = comment; DisplayName = dispName; Length = (long)fileSize; // fileSize returned by CQ API is in bytes ConnectionConfiguration = connectionConfiguration; }
bool HasDuplicateRecord( OAdEntity hostRecord, string childRecordEntityTypeName, string childRecordDisplayName) { // check if hostRecord already has a duplicate of this childRecord if (!CQWrapper.HasDuplicates(hostRecord)) { return(false); } object[] dupRecObjs = CQWrapper.GetDuplicates(hostRecord) as object[]; foreach (object dupRecObj in dupRecObjs) { OAdLink aLink = dupRecObj as OAdLink; if (null != aLink) { OAdEntity record = CQWrapper.GetChildEntity(aLink) as OAdEntity; if (null != record) { string recDispName = CQWrapper.GetEntityDisplayName(record); string recEntityDefName = CQWrapper.GetEntityDefName(record); if (CQStringComparer.EntityName.Equals(recEntityDefName, childRecordEntityTypeName) && CQStringComparer.RecordName.Equals(recDispName, childRecordDisplayName)) { return(true); } } } } return(false); }
public ClearQuestRecordItem(OAdEntity aRecord, string version) { Initialize(CQWrapper.GetEntityDefName(aRecord), CQWrapper.GetEntityDisplayName(aRecord), string.Empty, false); Version = version; }
private void SetRecordEditable(OAdEntity cqEntity) { string entityDefName = CQWrapper.GetEntityDefName(cqEntity); OAdEntityDef entityDef = CQWrapper.GetEntityDef(m_userSession, entityDefName); string modifyActionDefName = FindCQActionDefName(entityDef, CQConstants.ACTION_MODIFY); // open the record with the modify action CQWrapper.EditEntity(m_userSession, cqEntity, modifyActionDefName); }
public void ExtractLinkChangeActions( Session session, OAdEntity hostRecord, List <LinkChangeGroup> linkChangeGroups) { string hostRecDispName = CQWrapper.GetEntityDisplayName(hostRecord); string hostRecEntityDefName = CQWrapper.GetEntityDefName(hostRecord); string hostRecMigrItemId = UtilityMethods.CreateCQRecordMigrationItemId(hostRecEntityDefName, hostRecDispName); var linkChangeGroup = new LinkChangeGroup(hostRecMigrItemId, LinkChangeGroup.LinkChangeGroupStatus.Created, false); if (!CQWrapper.HasDuplicates(hostRecord)) { return; } object[] dupRecObjs = CQWrapper.GetDuplicates(hostRecord) as object[]; foreach (object dupRecObj in dupRecObjs) { OAdLink aLink = dupRecObj as OAdLink; if (null != aLink) { OAdEntity childRecord = CQWrapper.GetChildEntity(aLink) as OAdEntity; if (null != childRecord) { string childRecDispName = CQWrapper.GetEntityDisplayName(childRecord); string childRecEntityDefName = CQWrapper.GetEntityDefName(childRecord); string childRecMigrItemId = UtilityMethods.CreateCQRecordMigrationItemId(childRecEntityDefName, childRecDispName); ILink dupLink = new ArtifactLink(hostRecDispName, new Artifact(hostRecMigrItemId, new ClearQuestRecordArtifactType()), new Artifact(childRecMigrItemId, new ClearQuestRecordArtifactType()), string.Empty, this); LinkChangeAction action = new LinkChangeAction(WellKnownChangeActionId.Add, dupLink, LinkChangeAction.LinkChangeActionStatus.Created, false); linkChangeGroup.AddChangeAction(action); } else { // [teyang] TODO replace debug assertion with a conflict? Debug.Assert(false, "null == childRecord"); } } else { // [teyang] TODO replace debug assertion with a conflict? Debug.Assert(false, "null == aLink"); } } linkChangeGroups.Add(linkChangeGroup); }
public ClearQuestRecordItem( OAdEntity aRecord, OAdHistory aHistory, string historyFieldName, string historyIndex) { Initialize(CQWrapper.GetEntityDefName(aRecord), CQWrapper.GetEntityDisplayName(aRecord), CQWrapper.HistoryValue(aHistory), true); Version = historyIndex; HistoryFieldName = historyFieldName; }
public static string[] FindAllChangeActionNamesByType( Session userSession, OAdEntity entity, int changeType) { // find the entity type OAdEntityDef entityDef = CQWrapper.GetEntityDef(userSession, CQWrapper.GetEntityDefName(entity)); // find the MODIFY action def name to open the record object[] actionDefNames = CQWrapper.GetActionDefNames(entityDef) as object[]; string[] allValidActionNames = CQUtilityMethods.FindActionNameByType(entityDef, actionDefNames, changeType); return(allValidActionNames); }
private OAdEntity CreateTestEntity(OAdEntity record, ClearQuestMigrationContext migrationContext) { string entityName = CQWrapper.GetEntityDefName(record); if (m_perEntityTypeTestEntities.ContainsKey(entityName)) { return(m_perEntityTypeTestEntities[entityName]); } var aTestEntity = CQWrapper.BuildEntity(migrationContext.UserSession, entityName); m_perEntityTypeTestEntities.Add(entityName, aTestEntity); return(aTestEntity); }
public void ExtractLinkChangeActions(ClearQuestOleServer.Session session, ClearQuestOleServer.OAdEntity hostRecord, List <LinkChangeGroup> linkChangeGroups) { string recordId = CQWrapper.GetFieldValue(CQWrapper.GetEntityFieldValue(hostRecord, CQIdFieldName)); string url = string.Format(m_urlFormat, recordId); var linkChangeGroup = new LinkChangeGroup(recordId, LinkChangeGroup.LinkChangeGroupStatus.Created, false); string hostEntityDefName = CQWrapper.GetEntityDefName(hostRecord); string hostEntityDispName = CQWrapper.GetEntityDisplayName(hostRecord); string hostRecordId = UtilityMethods.CreateCQRecordMigrationItemId(hostEntityDefName, hostEntityDispName); ArtifactLink link = new ArtifactLink(hostRecordId, new Artifact(hostRecordId, s_sourceArtifactType), new Artifact(url, s_targetArtifactType), string.Empty, this); LinkChangeAction action = new LinkChangeAction(WellKnownChangeActionId.Add, link, LinkChangeAction.LinkChangeActionStatus.Created, false); linkChangeGroup.AddChangeAction(action); linkChangeGroups.Add(linkChangeGroup); }
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); }
public XmlDocument CreateRecordDesc( OAdEntity record, string versionStr, ClearQuestMigrationContext migrationContext, bool isLastRevOfThisSyncCycle) { string lastAuthor; DateTime lastChangeDate; FindLastRevDtls(record, out lastAuthor, out lastChangeDate); ClearQuestRecordDescription recordDesc = new ClearQuestRecordDescription(); recordDesc.CreateHeader(lastAuthor, lastChangeDate, MigrationRecordId, EntityDefName, versionStr, isLastRevOfThisSyncCycle); object[] fieldNames = (object[])CQWrapper.GetEntityFieldNames(record); foreach (object fldName in fieldNames) { string fieldName = (string)fldName; if (s_skipAlgorithm.SkipField(fieldName)) { TraceManager.TraceInformation("Skipping Field '{0}' while migrating data for Entity '{1}'", fieldName, EntityDefName); continue; } OAdFieldInfo fldInfo = CQWrapper.GetEntityFieldValue(record, fieldName); int cqFieldType = CQWrapper.GetFieldType(fldInfo); switch (cqFieldType) { case CQConstants.FIELD_INT: case CQConstants.FIELD_ID: case CQConstants.FIELD_SHORT_STRING: case CQConstants.FIELD_STATE: case CQConstants.FIELD_DBID: case CQConstants.FIELD_STATETYPE: case CQConstants.FIELD_RECORDTYPE: { string fldValue = CQWrapper.GetFieldValue(fldInfo); recordDesc.AddField(fieldName, string.Empty, fldValue ?? String.Empty); } break; case CQConstants.FIELD_MULTILINE_STRING: { string fldValue = CQWrapper.GetFieldValue(fldInfo); if (migrationContext == null || !CQStringComparer.FieldName.Equals(migrationContext.NotesLogFieldName, fieldName)) { // non-log field try { var aTestEntity = CreateTestEntity(record, migrationContext); object[] choices = (object[])CQWrapper.GetFieldChoiceList(aTestEntity, fieldName); if (choices != null && choices.Length > 0) { // Multi Line String with List of Allowed/Suggested Values.. replace all '\n' with comma // fix for bug# 429098 if (fldValue != null) { fldValue = fldValue.Replace("\n", ","); } } } catch (Exception ex) { // NOTE: // This feature of testing the multiline string choices create a dummy CQ record // The API call CreateTestEntity requires WRITE permission to CQ // If the migration account doesn't have the permission, we will simply use // the field's current value TraceManager.TraceInformation( "Skipping retrieval of Allowed Values for field '{0}' - Write permission is needed. Error: {1}", fieldName, ex.Message); } recordDesc.AddField(fieldName, string.Empty, fldValue); } else if (fldValue != null) { // log field StringBuilder sb = new StringBuilder(); List <CQNotesLog> notes = CQNotesLog.Parse(fldValue); foreach (CQNotesLog note in notes) { if (string.IsNullOrEmpty(note.Content) || note.Content.Contains(Constants.PlatformCommentSuffixMarker)) { // skip empty logs or those generated by this adapter continue; } if (note.Header.ChangeDate.CompareTo(migrationContext.CurrentHWMBaseLine) <= 0) { // skip the logs before the hwm continue; } sb.AppendFormat("{0} {1} {2}\n", CQNotesLogHeader.NotesLogHeaderIdentifier, note.HeaderString, CQNotesLogHeader.NotesLogHeaderIdentifier); sb.AppendLine(note.Content); } string extractedLog = sb.ToString(); if (!string.IsNullOrEmpty(extractedLog)) { recordDesc.AddField(fieldName, string.Empty, extractedLog); } } } break; case CQConstants.FIELD_DATE_TIME: { string fldValue = CQWrapper.GetFieldValue(fldInfo); if (fldValue != null) { // the time returned from CQ API is the local time.. DateTime fldVal = DateTime.Parse(fldValue, CultureInfo.CurrentCulture); //convert it in UTC DateTime utcTime = UtilityMethods.ConvertLocalToUTC(fldVal); TraceManager.TraceInformation("Field [{0}], CQ Time [{1}], UTC Time [{2}]", fieldName, fldVal.ToString(), utcTime.ToString()); recordDesc.AddField(fieldName, string.Empty, utcTime.ToString()); } else { recordDesc.AddField(fieldName, string.Empty, string.Empty); } } break; case CQConstants.FIELD_REFERENCE: { // get the current entity def handle OAdEntityDef curEntityDef = CQWrapper.GetEntityDef(CQSession, EntityDefName); OAdEntityDef refEntityDef = CQWrapper.GetFieldReferenceEntityDef(curEntityDef, fieldName); string refEntityName = CQWrapper.GetEntityDefName(refEntityDef); if (CQWrapper.GetFieldValueStatus(fldInfo) == (int)CQConstants.FieldStatus.HAS_VALUE) { // single value required string refFldVal = CQWrapper.GetFieldValue(fldInfo); recordDesc.AddField(fieldName, string.Empty, refFldVal ?? string.Empty); } else { recordDesc.AddField(fieldName, string.Empty, string.Empty); } } break; case CQConstants.FIELD_REFERENCE_LIST: { // get the current entity def handle OAdEntityDef curEntityDef = CQWrapper.GetEntityDef(CQSession, EntityDefName); OAdEntityDef refEntityDef = CQWrapper.GetFieldReferenceEntityDef(curEntityDef, fieldName); string refEntityName = CQWrapper.GetEntityDefName(refEntityDef); object[] refFldValues = CQWrapper.GetFieldValueAsList(fldInfo); if (refFldValues != null) { StringBuilder userList = new StringBuilder(); for (int valueIndex = 0; valueIndex < refFldValues.Length; valueIndex++) { object refFldObj = refFldValues[valueIndex]; if (valueIndex > 0) { userList.Append(","); } userList.Append((string)refFldObj); } recordDesc.AddField(fieldName, string.Empty, userList.ToString()); } else { recordDesc.AddField(fieldName, string.Empty, string.Empty); } } break; case CQConstants.FIELD_ATTACHMENT_LIST: case CQConstants.FIELD_JOURNAL: TraceManager.TraceInformation("Skipping the Field migration for Internal Field Type '{0}'", cqFieldType); // not migrating these fields as they are CQ internal fields continue; default: TraceManager.TraceInformation("Skipping the Field migration for Unkknown Field Type '{0}'", cqFieldType); break; } // end of switch cqFieldType } // end of foreach fieldNames return(recordDesc.DescriptionDocument); }
public bool Update( ClearQuestMigrationContext migrationContext, Session session, OAdEntity hostRecord, LinkChangeAction linkChangeAction) { if (null == linkChangeAction) { throw new ArgumentNullException("linkChangeAction"); } if (!linkChangeAction.Link.LinkType.ReferenceName.StartsWith(ReferenceNameQualifier)) { 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); } string hostRecEntityDefName = CQWrapper.GetEntityDefName(hostRecord); if (string.IsNullOrEmpty(hostRecEntityDefName) || !CQStringComparer.EntityName.Equals(hostRecEntityDefName, this.m_hostRecordType)) { return(false); } string refFieldName = linkChangeAction.Link.LinkType.ReferenceName.Substring(ReferenceNameQualifier.Length); if (string.IsNullOrEmpty(refFieldName)) { return(false); } // retrieve reference field information OAdFieldInfo refFieldInfo = CQWrapper.GetEntityFieldValue(hostRecord, refFieldName); int cqFieldType = CQWrapper.GetFieldType(refFieldInfo); if (cqFieldType != CQConstants.FIELD_REFERENCE) { // the field is not of the reference type // [teyang] TODO conflict? return(false); } // get the current entity def OAdEntityDef hostRecordEntityDef = CQWrapper.GetEntityDef(session, CQWrapper.GetEntityDefName(hostRecord)); OAdEntityDef childRecordEntityDef = CQWrapper.GetFieldReferenceEntityDef(hostRecordEntityDef, refFieldName); string childRecordEntityDefName = CQWrapper.GetEntityDefName(childRecordEntityDef); if (!CQStringComparer.EntityName.Equals(childRecordEntityDefName, childRecEntityDefName)) { // the field is not designated to hold reference to the EntityType of the target artifact // [teyang] TODO conflict? return(false); } int valueStatus = CQWrapper.GetFieldValueStatus(refFieldInfo); if (valueStatus == (int)CQConstants.FieldStatus.HAS_VALUE) { // the field already has a reference value set // single value required string refFldVal = CQWrapper.GetFieldValue(refFieldInfo); if (CQStringComparer.RecordName.Equals(refFldVal, childRecDispName)) { // the target artifact is already referenced in the field // [teyang] TODO conflict? return(false); } else { // field currently holds a reference to another entity // [teyang] TODO conflict? return(false); } } string[] modifyActionNames = CQUtilityMethods.FindAllChangeActionNamesByType( session, hostRecord, CQConstants.ACTION_MODIFY); if (modifyActionNames.Length == 0) { // [teyang] TODO conflict? return(false); } else if (modifyActionNames.Length > 1) { // [teyang] TODO conflict? return(false); } else { string modAction = modifyActionNames[0]; CQWrapper.EditEntity(session, hostRecord, modAction); string retVal = CQWrapper.SetFieldValue(hostRecord, refFieldName, childRecDispName); retVal = CQWrapper.Validate(hostRecord); if (string.IsNullOrEmpty(retVal)) { // [teyang] TODO conflict return(false); } retVal = CQWrapper.Commmit(hostRecord); if (string.IsNullOrEmpty(retVal)) { // [teyang] TODO conflict return(false); } 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 ReadOnlyCollection <LinkChangeGroup> GenerateNextLinkDeltaSlice( LinkService linkService, int maxDeltaSliceSize) { try { var linkChangeGroups = new List <LinkChangeGroup>(); if (null == ExtractLinkChangeActionsCallback) { return(linkChangeGroups.AsReadOnly()); } // load high watermark; as for CQ, we store local time for queries m_hwmLink.Reload(); DateTime hwmDeltaValue = m_hwmLink.Value; if (hwmDeltaValue.Equals(default(DateTime))) { hwmDeltaValue = new DateTime(1900, 1, 1); } hwmDeltaValue = hwmDeltaValue.AddSeconds(-1); // go back 1 second as we'll drop the millisec below // HACK HACK HACK //string hwmDeltaValueStr = hwmDeltaValue.ToString("o"); // using "ISO 8601" DateTime string format string hwmDeltaValueStr = hwmDeltaValue.ToString("u").Replace("Z", ""); // using "ISO 8601" DateTime string format if (hwmDeltaValueStr.LastIndexOf('.') >= 0) { hwmDeltaValueStr = hwmDeltaValueStr.Substring(0, hwmDeltaValueStr.LastIndexOf('.')); // drop the millisec } // HACK HACK HACK // record current time to update HWM after processing DateTime newHwmValue = DateTime.Now; // ??? // store to be used for analysis WorkItemLinkStore store = new WorkItemLinkStore(m_configurationService.SourceId); // extract links var inMaxDeltaSliceSize = maxDeltaSliceSize; foreach (CQRecordFilter filter in m_filters) { CQRecordQueryBase recordQuery = CQRecordQueryFactory.CreatQuery(m_userSession, filter, hwmDeltaValueStr, this); foreach (ClearQuestOleServer.OAdEntity record in recordQuery) { // HACK HACK if (record == null) { continue; } // HACK HACK string recDispName = CQWrapper.GetEntityDisplayName(record); TraceManager.TraceInformation("Generating linking delta for CQ Record: {0}", recDispName); var perWorkItemlinkChangeGroups = new List <LinkChangeGroup>(); ExtractLinkChangeActionsCallback(m_userSession, record, perWorkItemlinkChangeGroups); if (perWorkItemlinkChangeGroups.Count == 0) { TraceManager.TraceInformation("Number of links: {0}", 0); continue; } LinkChangeGroup consolidatedLinkChangeGroup = perWorkItemlinkChangeGroups[0]; for (int i = 1; i < perWorkItemlinkChangeGroups.Count; ++i) { foreach (LinkChangeAction action in perWorkItemlinkChangeGroups[i].Actions) { consolidatedLinkChangeGroup.AddChangeAction(action); } } TraceManager.TraceInformation("Number of links: {0}", consolidatedLinkChangeGroup.Actions.Count.ToString()); // VERY IMPORTANT STEP: update the link delta to store string hostRecDispName = CQWrapper.GetEntityDisplayName(record); string hostRecEntityDefName = CQWrapper.GetEntityDefName(record); string hostRecMigrItemId = UtilityMethods.CreateCQRecordMigrationItemId(hostRecEntityDefName, hostRecDispName); store.UpdatePerItemLinkChangeGroupsByCheckingRelatedItemRecords( hostRecMigrItemId, consolidatedLinkChangeGroup, this); if (consolidatedLinkChangeGroup.Actions.Count > 0) { linkChangeGroups.Add(consolidatedLinkChangeGroup); } maxDeltaSliceSize -= consolidatedLinkChangeGroup.Actions.Count; if (maxDeltaSliceSize <= 0) { // size limit reached - persist groups to DB, then empty the slice and process next slice linkService.AddChangeGroups(linkChangeGroups); linkChangeGroups.Clear(); maxDeltaSliceSize = inMaxDeltaSliceSize; } } } // persist remaining groups to DB linkService.AddChangeGroups(linkChangeGroups); // clean up the returned link change group collection // when the caller (toolkit) receives an empty collection, it understands there is no more // delta to generate for the moment, and proceeds to next phase linkChangeGroups.Clear(); // update primary Highwater Mark m_hwmLink.Update(newHwmValue); TraceManager.TraceInformation("Persisted CQ linking HWM: {0}", ClearQuestConstants.CqLinkHwm); TraceManager.TraceInformation("Updated CQ linking HWM: {0}", newHwmValue.ToString()); return(linkChangeGroups.AsReadOnly()); } catch (Exception exception) { // [teyang] TODO CONFLICT HANDLING //MigrationConflict genericeConflict = WitGeneralConflictType.CreateConflict(exception); //var conflictManager = m_conflictManager.GetService(typeof(ConflictManager)) as ConflictManager; //Debug.Assert(null != conflictManager); //List<MigrationAction> resolutionActions; //ConflictResolutionResult resolveRslt = // conflictManager.TryResolveNewConflict(conflictManager.SourceId, genericeConflict, out resolutionActions); //Debug.Assert(!resolveRslt.Resolved); TraceManager.TraceException(exception); return(new List <LinkChangeGroup>().AsReadOnly()); } }
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); }
private void ModifyRecordContent( OAdEntity entity, IMigrationAction action, ConversionResult convRslt, List <string> skipFields) { XmlNodeList columns = action.MigrationActionDescription.SelectNodes("/WorkItemChanges/Columns/Column"); if (null == columns) { throw new MigrationException(ClearQuestResource.ClearQuest_Error_InvalidActionDescription, action.ActionId); } // get the record's display name for updating conversion history string recordDisplayName = CQWrapper.GetEntityDisplayName(entity); // ********* // 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); SetRecordEditable(entity); StringBuilder updateLog = new StringBuilder(); PrintUpdateLogHeader(action, updateLog); string entityDefName = CQWrapper.GetEntityDefName(entity); string stateTransitionFieldDefName = m_migrationContext.GetStateField(entityDefName); string retVal; bool recordIsUpdated = false; foreach (XmlNode columnData in columns) { string stringVal = columnData.FirstChild.InnerText; string fieldName = columnData.Attributes["ReferenceName"].Value; Debug.Assert(!string.IsNullOrEmpty(fieldName), "Field ReferenceName is absent in the Migration Description"); if (CQStringComparer.FieldName.Equals(fieldName, stateTransitionFieldDefName) || (null != skipFields && skipFields.Contains(fieldName, CQStringComparer.FieldName))) { // skip or "State" field, as it has already been submitted in a separate history/revision continue; } bool setFieldValue = false; OAdFieldInfo aFieldInfo = CQWrapper.GetEntityFieldValue(entity, fieldName); int fieldRequiredness = CQWrapper.GetRequiredness(aFieldInfo); switch (fieldRequiredness) { case CQConstants.MANDATORY: case CQConstants.OPTIONAL: setFieldValue = true; break; case CQConstants.READONLY: // [teyang] TODO conflict handling TraceManager.TraceWarning("Field {0} is READONLY", fieldName); setFieldValue = false; break; case CQConstants.USEHOOK: // [teyang] TODO conflict handling TraceManager.TraceWarning("Field {0} is USEHOOK", fieldName); setFieldValue = false; break; default: throw new InvalidOperationException(); } if (setFieldValue) { int attempt1Count = 0; if (!SetFieldValue(action, entity, fieldName, stringVal, ref attempt1Count)) { return; } AddFieldToUpdateLog(fieldName, stringVal, updateLog); recordIsUpdated = true; } } if (!recordIsUpdated) { // no update has been made to the record, mark this action to be skipped CQWrapper.Revert(entity); if (action.State == ActionState.Pending) { action.State = ActionState.Skipped; } return; } AddLineToUpdateLog(updateLog); int attempt2Count = 0; if (!SetFieldValue(action, entity, NoteEntryFieldName, updateLog.ToString(), ref attempt2Count)) { return; } retVal = CQWrapper.Validate(entity); if (!string.IsNullOrEmpty(retVal)) { IEnumerable <Microsoft.TeamFoundation.Migration.ClearQuestAdapter.CQTextParser.RecordValidationResult> validationResults; if (CQTextParser.RecordValidationTextParser.TryParse(retVal, out validationResults)) { foreach (CQTextParser.RecordValidationResult rslt in validationResults) { MigrationConflict conflict = ClearQuestInvalidFieldValueConflictType.CreateConflict(rslt, action); List <MigrationAction> actions; var resolutionRslt = m_conflictManagerService.TryResolveNewConflict(m_conflictManagerService.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { action.ChangeGroup.ContainsBackloggedAction = true; return; } } } else { throw new InvalidOperationException(retVal); } } retVal = CQWrapper.Commmit(entity); if (!string.IsNullOrEmpty(retVal)) { // [teyang] TODO: invalid update conflict handling throw new InvalidOperationException(retVal); } 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, recordDisplayName, CQHistoryMigrationItem.CreateHistoryItemVersion(histFieldName, histIndex), convRslt); } } }
/// <summary> /// /// </summary> /// <param name="entity"></param> /// <param name="action"></param> /// <param name="convRslt"></param> /// <param name="stateTransitFieldNode"></param> /// <param name="skipFields"></param> /// <returns></returns> private bool ChangeRecordState( OAdEntity entity, IMigrationAction action, ConversionResult convRslt, XmlNode stateTransitFieldNode, out List <string> processedFields) { processedFields = new List <string>(); string destState = UtilityMethods.ExtractSingleFieldValue(stateTransitFieldNode); Debug.Assert(!string.IsNullOrEmpty(destState), "string.IsNullOrEmpty(newState)"); // get the record's display name for updating conversion history string recordDisplayName = CQWrapper.GetEntityDisplayName(entity); string entityDefName = CQWrapper.GetEntityDefName(entity); // find the current state OAdFieldInfo aFldInfo = CQWrapper.GetEntityFieldValue(entity, m_migrationContext.GetStateField(entityDefName)); string srcState = CQWrapper.GetFieldValue(aFldInfo); if (CQStringComparer.StateName.Equals(srcState, destState)) { // state does not change, skip this action return(false); } // find action def name OAdEntityDef entityDef = CQWrapper.GetEntityDef(m_userSession, entityDefName); string[] changeActionNames = CQUtilityMethods.FindAllActionNameByTypeAndStateTransition(entityDef, srcState, destState, CQConstants.ACTION_CHANGE); if (changeActionNames.Length == 0) { // [teyang] todo error handling throw new InvalidOperationException(); } string changeActionName = changeActionNames[0]; // ********* // 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); StringBuilder updateLog = new StringBuilder(); PrintUpdateLogHeader(action, updateLog); // mark entity to be editable with the desired state-change action) CQWrapper.EditEntity(m_userSession, entity, changeActionName); XmlNodeList columns = action.MigrationActionDescription.SelectNodes("/WorkItemChanges/Columns/Column"); if (null == columns) { throw new MigrationException(ClearQuestResource.ClearQuest_Error_InvalidActionDescription, action.ActionId); } foreach (XmlNode columnData in columns) { string stringVal = columnData.FirstChild.InnerText; string fieldName = columnData.Attributes["ReferenceName"].Value; Debug.Assert(!string.IsNullOrEmpty(fieldName), "Field ReferenceName is absent in the Migration Description"); OAdFieldInfo aFieldInfo = CQWrapper.GetEntityFieldValue(entity, fieldName); int fieldRequiredness = CQWrapper.GetRequiredness(aFieldInfo); if (fieldRequiredness != CQConstants.MANDATORY) { // skipping all non-mandatory fields continue; } int attempt1Count = 0; if (!SetFieldValue(action, entity, fieldName, stringVal, ref attempt1Count)) { return(false); } AddFieldToUpdateLog(fieldName, stringVal, updateLog); processedFields.Add(fieldName); } AddLineToUpdateLog(updateLog); int attempt2Count = 0; if (!SetFieldValue(action, entity, NoteEntryFieldName, updateLog.ToString(), ref attempt2Count)) { return(false); } string retVal = CQWrapper.Validate(entity); if (!string.IsNullOrEmpty(retVal)) { // [teyang] TODO conflict handling throw new InvalidOperationException(retVal); } retVal = CQWrapper.Commmit(entity); if (!string.IsNullOrEmpty(retVal)) { // [teyang] TODO conflict handling throw new InvalidOperationException(retVal); } 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, recordDisplayName, CQHistoryMigrationItem.CreateHistoryItemVersion(histFieldName, histIndex), convRslt); } } return(true); }
private void ComputeDeltaPerRecord( OAdEntity aRecord) { OAdEntityDef aEntityDef = CQWrapper.GetEntityDef(m_userSession, CQWrapper.GetEntityDefName(aRecord)); string recordDispName = CQWrapper.GetEntityDisplayName(aRecord); string recordEntDefName = CQWrapper.GetEntityDefName(aRecord); #region process history bool recordContentIsModified = false; bool maybeNewRecord = false; Dictionary <string, List <ClearQuestRecordItem> > historyDelta = new Dictionary <string, List <ClearQuestRecordItem> >(); Dictionary <string, int> perHistoryFieldLastIndex = new Dictionary <string, int>(); // needed for updating processed delta // find all history fields OAdHistoryFields aHistFields = CQWrapper.GetHistoryFields(aRecord); int historyFldCount = CQWrapper.HistoryFieldsCount(aHistFields); bool containsNewHistory = false; for (int histFldIndex = 0; histFldIndex < historyFldCount; histFldIndex++) { object ob = (object)histFldIndex; OAdHistoryField aHistoryField = CQWrapper.HistoryFieldsItem(aHistFields, ref ob); string historyFieldName = CQWrapper.GetHistoryFieldName(aHistoryField); // find last processed history entry for this history field string lookupItemId = CQDeltaComputationProgressLookupService.CreateHistoryItemId(recordEntDefName, recordDispName, historyFieldName); int startHistIndex = 1 + DeltaComputeProgressService.GetLastProcessedItemVersion(lookupItemId); // find all history in a particular history field int historyCount = CQWrapper.HistoryFieldHistoriesCount(aHistoryField); for (int histIndex = startHistIndex; histIndex < historyCount; histIndex++) { object obHistIndex = (object)histIndex; OAdHistory aHistory = CQWrapper.HistoryFieldHistoriesItem(aHistoryField, ref obHistIndex); CQHistory cqHistory = new CQHistory(aHistory); CQMigrationItem migrationItem = new CQHistoryMigrationItem(recordDispName, historyFieldName, histIndex); if (TranslationService.IsSyncGeneratedItemVersion(ClearQuestRecordItem.GetMigrationRecordId(recordEntDefName, recordDispName), migrationItem.MigrationItemVersion, m_configurationService.SourceId)) { continue; } if (histIndex == 0) { maybeNewRecord = true; } // add unprocessed history fields for processing if (!historyDelta.ContainsKey(historyFieldName)) { historyDelta.Add(historyFieldName, new List <ClearQuestRecordItem>(historyCount)); } historyDelta[aHistoryField.fieldname].Add(new ClearQuestRecordItem(aRecord, aHistory, historyFieldName, histIndex.ToString())); containsNewHistory = true; // based on action type, we decide whether content change is needed int actionType = CQWrapper.GetActionDefType(aEntityDef, cqHistory.Action); switch (actionType) { case CQConstants.ACTION_SUBMIT: break; case CQConstants.ACTION_MODIFY: recordContentIsModified = true; break; case CQConstants.ACTION_CHANGE: break; case CQConstants.ACTION_DUPLICATE: break; case CQConstants.ACTION_UNDUPLICATE: break; case CQConstants.ACTION_IMPORT: break; case CQConstants.ACTION_DELETE: TraceManager.TraceInformation(ClearQuestResource.ClearQuest_Msg_RecordDeleted, recordEntDefName, recordDispName); break; case CQConstants.ACTION_BASE: break; case CQConstants.ACTION_RECORD_SCRIPT_ALIAS: break; } } perHistoryFieldLastIndex.Add(historyFieldName, historyCount - 1); } #endregion #region generate delta for content and history if (maybeNewRecord || recordContentIsModified) { // the first revision, i.e. "Submit", of a CQ record is always hard-coded to be '1' CQMigrationItem contentMigrationAction = new CQMigrationItem(recordDispName, ClearQuestRecordItem.NewRecordVersion); bool isNewRecord = false; if (maybeNewRecord) { isNewRecord = !(DeltaComputeProgressService.IsMigrationItemProcessed(recordDispName, ClearQuestRecordItem.NewRecordVersionValue)); } if (!isNewRecord) { // all subsequent record "MODIFICATIONs" are hard-coded to be "update@<Now.Ticks>" contentMigrationAction.MigrationItemVersion = ClearQuestRecordItem.RecordUpdateVersion + "@" + DateTime.Now.Ticks; } ClearQuestRecordItem recordContentItem = new ClearQuestRecordItem(aRecord, contentMigrationAction.MigrationItemVersion); recordContentItem.CQSession = m_userSession; recordContentItem.Version = contentMigrationAction.MigrationItemVersion; ChangeGroup contentChangeGroup = recordContentItem.CreateChangeGroup( m_changeGroupService, m_migrationContext, isNewRecord && m_isLastRevisionAutoCorrectionEnabled); contentChangeGroup.Save(); if (isNewRecord && !containsNewHistory) { DeltaComputeProgressService.UpdateCache(recordDispName, ClearQuestRecordItem.NewRecordVersionValue); } } var lastHistoryRecordItem = historyDelta[historyDelta.Keys.Last()].Last(); foreach (string histFieldName in historyDelta.Keys) { foreach (ClearQuestRecordItem recordHistItem in historyDelta[histFieldName]) { recordHistItem.CQSession = m_userSession; ChangeGroup changeGroup = recordHistItem.CreateChangeGroup( m_changeGroupService, m_migrationContext, (CQStringComparer.FieldName.Equals(recordHistItem.HistoryFieldName, lastHistoryRecordItem.HistoryFieldName) && recordHistItem.Version.Equals(lastHistoryRecordItem.Version, StringComparison.OrdinalIgnoreCase) && m_isLastRevisionAutoCorrectionEnabled)); changeGroup.Save(); } Debug.Assert(perHistoryFieldLastIndex.ContainsKey(histFieldName), "perHistoryFieldLastIndex.ContainsKey(histFieldName) returns false"); string deltaComputeProcessLookupId = CQDeltaComputationProgressLookupService.CreateHistoryItemId(recordEntDefName, recordDispName, histFieldName); DeltaComputeProgressService.UpdateCache(deltaComputeProcessLookupId, perHistoryFieldLastIndex[histFieldName]); } #endregion #region process attachment OAdAttachmentFields aAttachmentFields = CQWrapper.GetAttachmentFields(aRecord); for (int aAttachmentFieldsIndex = 0; aAttachmentFieldsIndex < CQWrapper.AttachmentsFieldsCount(aAttachmentFields); aAttachmentFieldsIndex++) { object ob = (object)aAttachmentFieldsIndex; OAdAttachmentField aAttachmentField = CQWrapper.AttachmentsFieldsItem(aAttachmentFields, ref ob); string fieldName = CQWrapper.GetAttachmentFieldName(aAttachmentField); ChangeGroup changeGroup = m_changeGroupService.CreateChangeGroupForDeltaTable( string.Format("{0}:{1}:{2}", recordDispName, "Attachments", fieldName)); // process all attachments OAdAttachments attachments = CQWrapper.GetAttachments(aAttachmentField); for (int attachmentIndex = 0; attachmentIndex < CQWrapper.AttachmentsCount(attachments); attachmentIndex++) { object obIndex = (object)attachmentIndex; OAdAttachment aAttachment = CQWrapper.AttachmentsItem(attachments, ref obIndex); ClearQuestAttachmentItem attachmentItem = new ClearQuestAttachmentItem(aRecord, aAttachmentField, aAttachment, UserSessionConnConfig); attachmentItem.CQSession = m_userSession; attachmentItem.CreateChangeAction(changeGroup, lastHistoryRecordItem.Version); } if (changeGroup.Actions.Count > 0) { changeGroup.Save(); } } #endregion }
public static string CreateCQRecordMigrationItemId(OAdEntity record) { return(CQWrapper.GetEntityDefName(record) + MigrationItemDelimiter + CQWrapper.GetEntityDisplayName(record)); }