public static string[] FindAllActionNameByTypeAndSourceState( OAdEntityDef entityDef, string srcState, int matchingActionDefType) { List <string> retVal = new List <string>(); // find all possible destination state object[] allStateObjs = CQWrapper.GetStateDefNames(entityDef) as object[]; if (null != allStateObjs) { foreach (object destStateObj in allStateObjs) { string destState = destStateObj as string; if (!string.IsNullOrEmpty(destState)) { object[] transitions = CQWrapper.DoesTransitionExist(entityDef, srcState, destState) as object[]; string[] actionDefNames = FindActionNameByType(entityDef, transitions, matchingActionDefType); retVal.AddRange(actionDefNames.AsEnumerable()); } } } return(retVal.ToArray()); }
/// <summary> /// Parametrized Constructor for instantiating schema object /// No other CTors are provided as these values are required to start with /// </summary> /// <param name="xmlFile">Name of the target schema xml file</param> /// <param name="cqSess">Handle to valid CQ session</param> /// <param name="entityDef">Handle to valid CQ Entity Definition</param> public WITDXMLGenerator(string schemaXmlFile, string fieldMapXmlFile, Session cqSess, OAdEntityDef entityDef, VSTSConnection vstsConn) { Logger.EnteredMethod(LogSource.CQ, schemaXmlFile, fieldMapXmlFile, cqSess, entityDef); // create instance of WITDFieldMap to store field mappings witdFieldMap = new WITFieldMappings(); // create instance of WITDSchema to store WITD schema witdSchema = new WITDSchema(); witdSchema.SetApplication(Application.Workitemtypeeditor); // set the VSTS connection handle for finding the unique fields in VSTS system WITDSchema.VstsConn = vstsConn; // store file name to be used later to generate xml schemaXMLFileName = schemaXmlFile; fieldMapXMLFileName = fieldMapXmlFile; cqEntityDef = entityDef; cqSession = cqSess; cqEntity = CQWrapper.BuildEntity(cqSession, CQWrapper.GetEntityDefName(cqEntityDef)); Logger.ExitingMethod(LogSource.CQ); }
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); }
} // end of ValidateSchemaMapOnCQ /// <summary> /// Validates the Field Map on CQ for a Entity Type /// </summary> /// <param name="entityDef">Handle to Entity Definition</param> /// <param name="fldMaps">Deserialized Field Map data</param> /// <returns>true if successful, false in case of some error</returns> private static void ValidateFieldMapOnCQ(OAdEntityDef entityDef, FieldMapsFieldMap[] fldMaps, string fieldMapFile) { string entityName = CQWrapper.GetEntityDefName(entityDef); object[] cqFields = (object[])CQWrapper.GetFieldDefNames(entityDef); // prepare the list of fields from the current entity type Dictionary <string, bool> cqFieldsList = new Dictionary <string, bool>(TFStringComparer.OrdinalIgnoreCase); foreach (string cqFld in cqFields) { cqFieldsList.Add(cqFld, false); } Display.StartProgressDisplay(UtilityMethods.Format(CQResource.CQ_VALIDATE_FLD_MAP, fieldMapFile)); try { StringBuilder invalidFields = new StringBuilder(); foreach (FieldMapsFieldMap fldMap in fldMaps) { if (fldMap.exclude != null && !TFStringComparer.XmlAttributeValue.Equals(fldMap.exclude, "true") && !CQConstants.CQInternalFields.ContainsKey(fldMap.from)) { // this is to be included in the selected fields for migration if (!cqFieldsList.ContainsKey(fldMap.from)) { if (invalidFields.Length > 0) { invalidFields.Append(", "); } invalidFields.Append(fldMap.from); } } } if (invalidFields.Length > 0) { string errMsg = UtilityMethods.Format(CQResource.CQ_FLD_NOT_EXIST, Path.GetFileName(fieldMapFile), invalidFields.ToString(), entityName); PostMigrationReport.WriteIssue(null, null, RepStatus.Failed, ReportIssueType.Critical, String.Empty, entityName, IssueGroup.Witd, errMsg); throw new ConverterException(errMsg); } } finally { Display.StopProgressDisplay(); Display.NewLine(); } } // end of ValidateFieldMapOnCQ
internal static int GetFieldDefType(OAdEntityDef entityDef, string fieldDefName) { int retval = 0; try { retval = entityDef.GetFieldDefType(fieldDefName); } catch (COMException ex) { InteropErrorHandler.HandleCQException(ex); } return(retval); }
internal static string GetActionDestStateName(OAdEntityDef entityDef, string actionDefName) { string retval = null; try { retval = entityDef.GetActionDestStateName(actionDefName); } catch (COMException ex) { InteropErrorHandler.HandleCQException(ex); } return(retval); }
internal static int GetActionDefType(OAdEntityDef entityDef, string actionDefName) { int retval = 0; try { retval = entityDef.GetActionDefType(actionDefName); } catch (COMException ex) { HandleCQException(ex); } return(retval); }
internal static object DoesTransitionExist(OAdEntityDef entityDef, string srcState, string destState) { object retval = null; try { retval = entityDef.DoesTransitionExist(srcState, destState); } catch (COMException ex) { InteropErrorHandler.HandleCQException(ex); } return(retval); }
internal static int GetEntityDefType(OAdEntityDef entityDef) { int retval = 0; try { retval = entityDef.GetType(); } catch (COMException ex) { InteropErrorHandler.HandleCQException(ex); } return(retval); }
internal static object GetStateDefNames(OAdEntityDef entityDef) { object retval = null; try { retval = entityDef.GetStateDefNames(); } catch (COMException ex) { InteropErrorHandler.HandleCQException(ex); } return(retval); }
internal static OAdEntityDef GetEntityDef(Session cqSession, string entityDefName) { OAdEntityDef entityDef = null; try { entityDef = (OAdEntityDef)cqSession.GetEntityDef(entityDefName); } catch (COMException ex) { InteropErrorHandler.HandleCQException(ex); } return(entityDef); }
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); }
internal static object GetFieldDefNames(OAdEntityDef entityDef) { object retval = null; try { retval = entityDef.GetFieldDefNames(); } catch (COMException ex) { HandleCQException(ex); } return(retval); }
internal static OAdEntityDef GetFieldReferenceEntityDef(OAdEntityDef entityDef, string fieldName) { OAdEntityDef retval = null; try { retval = (OAdEntityDef)entityDef.GetFieldReferenceEntityDef(fieldName); } catch (COMException ex) { InteropErrorHandler.HandleCQException(ex); } return(retval); }
internal static string GetEntityDefName(OAdEntityDef entityDef) { string retval = null; try { retval = entityDef.GetName(); } catch (COMException ex) { InteropErrorHandler.HandleCQException(ex); } return(retval); }
internal static object DoesTransitionExist(OAdEntityDef entityDef, string srcState, string destState) { object retval = null; try { retval = entityDef.DoesTransitionExist(srcState, destState); if (retval == null) { throw new ClearQuestInvalidConfigurationException(String.Format( "Unable to change State for ClearQuest '{0}'; transaction from State '{1}' to '{2}' is not allowed. Consider changing the allowed States and State transitions on both sides of the sync to match.", entityDef.GetName(), srcState, destState)); } } catch (COMException ex) { InteropErrorHandler.HandleCQException(ex); } return(retval); }
public static string[] FindActionNameByType( OAdEntityDef entityDef, object[] transitions, int matchingActionDefType) { List <string> matchingActionDefNames = new List <string>(transitions.Count()); foreach (object actionNameObj in transitions) { string actionDefName = actionNameObj as string; Debug.Assert(!string.IsNullOrEmpty(actionDefName), "string.IsNullOrEmpty(actionName)"); int actionDefType = CQWrapper.GetActionDefType(entityDef, actionDefName); if (actionDefType == matchingActionDefType) { matchingActionDefNames.Add(actionDefName); } } return(matchingActionDefNames.ToArray()); }
private string FindCQActionDefName( OAdEntityDef entityDef, int actionType) { // find the MODIFY action def name to open the record object[] actionDefNames = CQWrapper.GetActionDefNames(entityDef) as object[]; string[] modifyActionDefNames = CQUtilityMethods.FindActionNameByType(entityDef, actionDefNames, actionType); if (modifyActionDefNames.Length == 0) { // [teyang] todo error handling throw new InvalidOperationException(); } else if (modifyActionDefNames.Length > 1) { // [teyang] todo error handling throw new InvalidOperationException(); } else { return(modifyActionDefNames[0]); } }
public static List <LinkType> ExtractSupportedLinkTypes( Session session, string hostRecordType) { List <LinkType> retval = new List <LinkType>(); OAdEntityDef aEntityDef = CQWrapper.GetEntityDef(session, hostRecordType); object[] fieldDefNameObjs = CQWrapper.GetFieldDefNames(aEntityDef) as object[]; foreach (object fieldDefNameObj in fieldDefNameObjs) { string fieldDefName = fieldDefNameObj as string; int fieldDefType = CQWrapper.GetFieldDefType(aEntityDef, fieldDefName); if (fieldDefType == CQConstants.FIELD_REFERENCE) { retval.Add(new ClearQuestReferenceFieldLinkTypeBase(hostRecordType, fieldDefName)); } } return(retval); }
public static string[] FindAllActionNameByTypeAndStateTransition( OAdEntityDef entityDef, string srcState, string destState, int matchingActionDefType) { if (string.IsNullOrEmpty(srcState)) { throw new ArgumentNullException("srcState"); } if (string.IsNullOrEmpty(destState)) { throw new ArgumentNullException("destState"); } List <string> retVal = new List <string>(); object[] transitions = CQWrapper.DoesTransitionExist(entityDef, srcState, destState) as object[]; string[] actionDefNames = FindActionNameByType(entityDef, transitions, matchingActionDefType); retVal.AddRange(actionDefNames.AsEnumerable()); return(retVal.ToArray()); }
/// <summary> /// This mehtod returns a list of entity definition names that are referenced (to any level) /// with the baseEntityDef. /// </summary> /// <param name="cqSession">A valid Clear Quest Session object</param> /// <param name="baseEntityDefName"> /// Entity Definition name to start with. /// This entity has to be a submit type entity /// </param> /// <returns> /// List of (non system) entity definition names that are connected (referenced) with the /// entity name passed as parameter. Also includes the starting entity definition name. /// </returns> public static string[] GetReferencedEntityDefNames(Session cqSession, string baseEntityDefName, string configFile) { Logger.EnteredMethod(LogSource.CQ, cqSession, baseEntityDefName); // fetch the submit type entities from CQ DB object[] allSubmitEntities = (object[])CQWrapper.GetSubmitEntityDefNames(cqSession); List <string> allSubmitEntitiesList = new List <string>(); foreach (object obEntity in allSubmitEntities) { allSubmitEntitiesList.Add((string)obEntity); } // ensure that the passed entity is part of Submit Entity Types if (!allSubmitEntitiesList.Contains(baseEntityDefName)) { // log the problem string errMsg = UtilityMethods.Format(CQResource.CQ_NOT_SUBMIT_ENTITY, CurConResource.Analysis); Logger.Write(LogSource.CQ, TraceLevel.Error, errMsg); Microsoft.TeamFoundation.Converters.WorkItemTracking.Common.ConverterMain.MigrationReport.WriteIssue(String.Empty, errMsg, string.Empty /* no item */, null, IssueGroup.Witd.ToString(), ReportIssueType.Critical); throw new ConverterException(errMsg); } // create a list for storing all entities name List <string> refEntities = new List <string>(); // add the base entity name to start with refEntities.Add(baseEntityDefName); int noOfEntitesDone = 0; while (noOfEntitesDone < refEntities.Count) { OAdEntityDef cqEntityDef = CQWrapper.GetEntityDef(cqSession, refEntities[noOfEntitesDone]); // we processed one entity noOfEntitesDone++; // process all the fields and find out the names for other entities object[] cqFields = (object[])cqEntityDef.GetFieldDefNames(); if (cqFields.Length > 0) { foreach (object ob in cqFields) { string cqFldName = ob as String; Debug.Assert(cqFldName != null); // get field type int fieldType = CQWrapper.GetFieldDefType(cqEntityDef, cqFldName); bool isSystem = cqEntityDef.IsSystemOwnedFieldDefName(cqFldName); // count the field only if it is not internal to CQ and is a reference field if ((fieldType == CQConstants.FIELD_REFERENCE || fieldType == CQConstants.FIELD_REFERENCE_LIST) && isSystem == false) { // add the referenced entity in the list (if it is not already there) OAdEntityDef refEntity = CQWrapper.GetFieldReferenceEntityDef(cqEntityDef, cqFldName); // the scanned entities should also be ther submit type entities only if ((allSubmitEntitiesList.Contains(refEntity.GetName())) == true && refEntities.Contains(refEntity.GetName()) == false) { refEntities.Add(refEntity.GetName()); } } } //foreach (object ob in cqFields) } //if (cqFields.Length > 0) } //while (noOfEntitesDone != refEntities.Count) Logger.ExitingMethod(LogSource.CQ); return(refEntities.ToArray()); }
/// <summary> /// Process and add Fields to the WIT /// </summary> /// <param name="wit">Handle to WIT to add the fields</param> private void ProcessFields(WorkItemType wit) { Logger.EnteredMethod(LogSource.CQ, wit); // add vsts_sourcedb and vsts_sourceid fields FieldDefinition vstsIdField = AddInternalFields(wit, CQConstants.IdFieldName, FieldType.Integer); FieldDefinition vstsSourceIdField = AddInternalFields(wit, CommonConstants.VSTSSrcIdField, FieldType.String); vstsSourceIdField.READONLY = new PlainRule(); AddInternalFields(wit, CommonConstants.VSTSSrcDbField, FieldType.String); // add id and vsts_sourceid in form CreateDefaultControl(vstsIdField.name, vstsIdField.refname, FieldType.Integer, wit); CreateDefaultControl(CQConstants.SourceFieldLabel, vstsSourceIdField.refname, FieldType.String, wit); // get all the fields from CQ object[] cqFields = (object[])CQWrapper.GetFieldDefNames(cqEntityDef); FieldDefinition witField; if (cqFields.Length > 0) { foreach (object ob in cqFields) { string fldName = (string)ob; if (CQConstants.InternalFieldTypes.ContainsKey(fldName)) { // these are internal clearquest fields // we dont want to migrate these Logger.Write(LogSource.CQ, TraceLevel.Info, "Skipping CQ Internal Field '{0}'", fldName); continue; } int cqFieldType = CQWrapper.GetFieldDefType(cqEntityDef, fldName); string suggestedFldMap = (string)CQConstants.SuggestedMap[fldName]; if (suggestedFldMap != null) { // this field name matched to one the suggested mappings to one of the core field // generate the field in schema and also a field map for this.. witField = new FieldDefinition(); witField.OldFieldName = fldName; witField.name = suggestedFldMap; witField.type = CQConstants.WITFieldTypes[cqFieldType]; // use the core field refname and type for (int coreFieldIndex = 0; coreFieldIndex < CQConstants.CurrituckCoreFields.Length; coreFieldIndex++) { string coreFieldName = CQConstants.CurrituckCoreFields[coreFieldIndex].Name; if (TFStringComparer.WorkItemFieldFriendlyName.Equals(coreFieldName, suggestedFldMap)) { // use the refname and type from the core fields witField.refname = CQConstants.CurrituckCoreFields[coreFieldIndex].ReferenceName; witField.type = (FieldType)Enum.Parse(typeof(FieldType), CQConstants.CurrituckCoreFields[coreFieldIndex].FieldType.ToString()); break; } } fieldsToComment.Add(witField.name); wit.AddField(witField); FieldMapsFieldMap fldMap = null; // process the field properties to set rules for Required/Read Only and list of values // check if it requires UserMap also if (cqFieldType == CQConstants.FIELD_REFERENCE || cqFieldType == CQConstants.FIELD_REFERENCE_LIST) { OAdEntityDef refEntity = CQWrapper.GetFieldReferenceEntityDef(cqEntityDef, witField.OldFieldName); if (TFStringComparer.WorkItemType.Equals(CQWrapper.GetEntityDefName(refEntity), "users")) { ProcessUserFieldProperties(cqFieldType, witField, ref fldMap); } } else { ProcessFieldProperties(witField, cqFieldType, false); fldMap = new FieldMapsFieldMap(); fldMap.from = witField.OldFieldName; fldMap.to = witField.name; fldMap.exclude = "false"; } Logger.Write(LogSource.CQ, TraceLevel.Info, "Using Suggested Field Map {0} to {1}", witField.OldFieldName, suggestedFldMap.ToString()); witdFieldMap.GetFieldMappings().AddFieldMap(fldMap); if (TFStringComparer.WorkItemFieldFriendlyName.Equals(fldMap.to, VSTSConstants.DescriptionField)) { CreateDefaultControl(witField.OldFieldName, witField.refname, FieldType.PlainText, wit); } else { CreateDefaultControl(witField.OldFieldName, witField.refname, FieldType.String, wit); } continue; } switch (cqFieldType) { case CQConstants.FIELD_ID: case CQConstants.FIELD_SHORT_STRING: case CQConstants.FIELD_MULTILINE_STRING: case CQConstants.FIELD_INT: case CQConstants.FIELD_DATE_TIME: { Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Migrating Field '{0}'", fldName); witField = new FieldDefinition(); witField.OldFieldName = witField.name = fldName; witField.type = CQConstants.WITFieldTypes[cqFieldType]; // find the set of allowed values and populate in the xml ProcessFieldProperties(witField, cqFieldType, false); wit.AddField(witField); FieldMapsFieldMap fldMap = new FieldMapsFieldMap(); fldMap.from = witField.OldFieldName; fldMap.to = witField.name; // new field name.. if changed fldMap.exclude = "false"; // add the field map witdFieldMap.GetFieldMappings().AddFieldMap(fldMap); // add in FORM CreateDefaultControl(witField.OldFieldName, witField.refname, witField.type, wit); } break; case CQConstants.FIELD_REFERENCE_LIST: case CQConstants.FIELD_REFERENCE: { // find the referenced entity name OAdEntityDef refEntity = CQWrapper.GetFieldReferenceEntityDef(cqEntityDef, fldName); if (TFStringComparer.WorkItemType.Equals(CQWrapper.GetEntityDefName(refEntity), "users")) { // add the refer keyword in the FieldMap file for this field // and generate the field information for this field.. // as User is a core functionality in currituck.. // handle is in special way // there are no chances that a "users" field will be of REFERENCE_LIST type.. // in case we see a requirement for REFERENCE_LIST also, add this code there also Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Migrating User Field '{0}'", fldName); FieldMapsFieldMap fldMap = null; witField = new FieldDefinition(); witField.OldFieldName = witField.name = fldName; ProcessUserFieldProperties(cqFieldType, witField, ref fldMap); // add in FIELD, FieldMap and FORM wit.AddField(witField); // fix for bug# 15769 // set new "To" field name in the field map in case the field name got changed as part of AddField() call fldMap.to = witField.name; witdFieldMap.GetFieldMappings().AddFieldMap(fldMap); CreateDefaultControl(witField.OldFieldName, witField.refname, witField.type, wit); } } break; case CQConstants.FIELD_STATE: case CQConstants.FIELD_ATTACHMENT_LIST: case CQConstants.FIELD_JOURNAL: case CQConstants.FIELD_DBID: case CQConstants.FIELD_STATETYPE: case CQConstants.FIELD_RECORDTYPE: // not migrating these fields as they are CQ internal fields Logger.Write(LogSource.CQ, TraceLevel.Info, "Skipping the Field migration for Internal Field Type '{0}'", cqFieldType); break; default: break; } // switch (cqFieldType) } // end of foreach (object ob in cqFields) } //if (cqFields.Length > 0) else { Logger.Write(LogSource.CQ, TraceLevel.Warning, "No Fields present in the current Entity Definition '{0}'", wit.name); } // add all the core fields in the FIELDS section as these fields are being used in the FORM section // as per Currituck implementation, any field existing in FORM section has to be in the FIELDS also // even if it is a core field FieldDefinition coreFldDef = null; for (int coreFieldIndex = 0; coreFieldIndex < CQConstants.CurrituckCoreFields.Length; coreFieldIndex++) { string coreFieldName = CQConstants.CurrituckCoreFields[coreFieldIndex].Name; bool fieldExist = false; foreach (FieldDefinition fldDef in wit.FIELDS) { if (TFStringComparer.WorkItemFieldFriendlyName.Equals(fldDef.name, coreFieldName)) { fieldExist = true; break; } } if (fieldExist == true) { // skip this field.. its already added in the FIELDS section continue; } coreFldDef = new FieldDefinition(); coreFldDef.name = coreFieldName; coreFldDef.refname = CQConstants.CurrituckCoreFields[coreFieldIndex].ReferenceName; coreFldDef.type = (FieldType)Enum.Parse(typeof(FieldType), CQConstants.CurrituckCoreFields[coreFieldIndex].FieldType.ToString()); if (coreFieldIndex < CQConstants.NoOfUserFldsInCoreFields) { //add VALIDUSER rule for all user fields coreFldDef.VALIDUSER = new ValidUserRule(); } wit.AddField(coreFldDef); } int pos; ControlType reasonFld = wit.FindFieldInForm(CQConstants.ReasonField, out pos); if (reasonFld == null) { // Bug# 50492: reason field is not yet added .. add it after the State field // look for state field position wit.FindFieldInForm(CQConstants.StateField, out pos); if (pos >= 0) { FieldDefinition reasonFldDef = AddInternalFields(wit, CQConstants.ReasonFieldName, FieldType.String); wit.FORM.Layout.WITDItems.Insert(pos + 1, CreateDefaultControl(reasonFldDef.name, CQConstants.ReasonField, FieldType.String, null)); } } Logger.ExitingMethod(LogSource.CQ); } // end of ProcessFields
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 }
} // end of CQEntityRec CTor /// <summary> /// Populate the current record from CQ if its not already in Currituck /// and also process all its references (recursively), Links, History and Attachments /// Else just sets the currituck id for future reference /// </summary> public bool Populate() { bool partiallyMigrated = false; // first check if it exists in the memory cache CQEntity currentEntityRecords = m_cqParams.entityRecords[m_entityName]; CQEntityRec lookupEntity = currentEntityRecords.FindEntityRec(m_entityName, m_dbid); if (lookupEntity != null) { // record already populated.. Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Already populated record '{0}' for Entity '{1}', DBID:{2}", lookupEntity.SourceId, lookupEntity.EntityName, lookupEntity.DBID); return(true); } m_CQEntity = CQWrapper.GetEntityByDbId(m_cqParams.cqSession, m_entityName, m_dbid); // get the source id m_sourceId = CQWrapper.GetEntityDisplayName(m_CQEntity); Logger.Write(LogSource.CQ, TraceLevel.Verbose, UtilityMethods.Format(CQResource.CQ_PROCESSING_REC, m_sourceId)); // check if it exist in currituck using static API VSTSWorkItemHelper wiHelper = (VSTSWorkItemHelper)m_MySchemaMap.vstsHelper; ArrayList checkList = new ArrayList(); checkList.Add(new WorkItemNameValueRelation(CommonConstants.VSTSSrcIdField, m_sourceId)); checkList.Add(new WorkItemNameValueRelation(CommonConstants.VSTSSrcDbField, m_cqParams.uniqueInstId)); wiHelper = (VSTSWorkItemHelper)m_MySchemaMap.vstsHelper; if (wiHelper.IsWIMigrated(checkList) == true) { // need not to load the data from CQ.. // just set the currituck id // not going to update this bug from CQ->Currituck even // if it is updated.. just get out from here as my population is done // with minimal required stuff string warningMsg = UtilityMethods.Format(CQResource.CQ_REC_MIGRATED, m_sourceId); Logger.Write(LogSource.CQ, TraceLevel.Warning, warningMsg); PostMigrationReport.WriteIssue(m_MySchemaMap.entity, m_MySchemaMap.WIT, Stats.MigrationStatus.Skipped, ReportIssueType.Warning, String.Empty, m_sourceId, IssueGroup.Wi, warningMsg); m_WITId = wiHelper.WorkItemId; //compact current object CompactMe(); return(true); } else if (wiHelper.IsCurrentWorkItemValid() == true) { // work item is already there.. partially migrated partiallyMigrated = true; } #if DEBUG CommonConstants.NoOfBugs++; #endif // create the required data structures m_imWorkItem = new InMemoryWorkItem(); string fldName; OAdEntityDef curEntityDef = CQWrapper.GetEntityDef(m_cqParams.cqSession, m_entityName); Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Adding record for Entity {0}, Record {1}", m_entityName, CQWrapper.GetEntityDisplayName(m_CQEntity)); object[] fields = (object[])CQWrapper.GetEntityFieldNames(m_CQEntity); foreach (object fld in fields) { fldName = (string)fld; if (CQConstants.InternalFieldTypes.ContainsKey(fldName)) { // these are internal clearquest fields // we dont want to migrate these Logger.Write(LogSource.CQ, TraceLevel.Info, "Skipping Internal Field '{0}' while migrating data for entity {1}", fldName, m_entityName); continue; } { // process this field only if it exists in the "from" side of Field Map OAdFieldInfo fldInfo = CQWrapper.GetEntityFieldValue(m_CQEntity, fldName); int cqFieldType = CQWrapper.GetFieldType(fldInfo); switch (cqFieldType) { case CQConstants.FIELD_ID: case CQConstants.FIELD_SHORT_STRING: case CQConstants.FIELD_INT: { string fldValue = CQWrapper.GetFieldValue(fldInfo); if (fldValue != null) { m_imWorkItem.InitialView.Add(fldName, fldValue); } } break; case CQConstants.FIELD_MULTILINE_STRING: { string fldValue = CQWrapper.GetFieldValue(fldInfo); if (currentEntityRecords.Entity == null) { // build entity to get the list of allowed/suggested values currentEntityRecords.Entity = CQWrapper.BuildEntity(m_cqParams.cqSession, currentEntityRecords.EntityName); } object[] choices = (object[])CQWrapper.GetFieldChoiceList(currentEntityRecords.Entity, fldName); 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", ","); } } /* no conversion shall be required.. bug# 20219 - shall be rendered in HTML as it is * // hack for Notes_Log & Description field.. Shall be converted to HTML (bug#429032) * if (fldName.Equals("Notes_Log", StringComparison.OrdinalIgnoreCase) || * fldName.Equals("Description", StringComparison.OrdinalIgnoreCase)) * { * fldValue = VSTSUtil.ConvertTextToHtml(fldValue); * } */ m_imWorkItem.InitialView.Add(fldName, fldValue); } 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 = CQConverterUtil.ConvertLocalToUTC(fldVal); Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Field [{0}], CQ Time [{1}], UTC Time [{2}]", fldName, fldVal.ToString(), utcTime.ToString()); m_imWorkItem.InitialView.Add(fldName, utcTime); } else { Logger.Write(LogSource.CQ, TraceLevel.Info, "Got null value for field {0}", fldName); } } break; case CQConstants.FIELD_REFERENCE: { // get the current entity def handle OAdEntityDef refEntityDef = CQWrapper.GetFieldReferenceEntityDef(curEntityDef, fldName); string refEntityName = CQWrapper.GetEntityDefName(refEntityDef); // special handling for users.. add the user field value also.. // we dont want to create a link in this case.. // just add the field value pair in IMWorkItem.. and // user map will be applied while saving if (TFStringComparer.WorkItemType.Equals(refEntityName, "users")) { if (CQWrapper.GetFieldValueStatus(fldInfo) == (int)CQConstants.FieldStatus.HAS_VALUE) { // single value required string refFldVal = CQWrapper.GetFieldValue(fldInfo); m_imWorkItem.InitialView.Add(fldName, refFldVal); } } else if (m_cqParams.allowedEntities.ContainsKey(refEntityName)) { int valueStatus = CQWrapper.GetFieldValueStatus(fldInfo); Logger.WriteIf((valueStatus != (int)CQConstants.FieldStatus.HAS_VALUE), LogSource.CQ, TraceLevel.Info, "No Value for Referenced field {0} in Entity {1}", refEntityName, m_entityName); if (valueStatus == (int)CQConstants.FieldStatus.HAS_VALUE) { // single value required string refFldVal = CQWrapper.GetFieldValue(fldInfo); if (String.Equals(refFldVal, SourceId, StringComparison.Ordinal)) { // reference to self.. cannot have a link on to self string warningMsg = UtilityMethods.Format(CQResource.CQ_SELF_REFERENCE, SourceId, EntityName, fldName); Logger.Write(LogSource.CQ, TraceLevel.Warning, warningMsg); PostMigrationReport.WriteIssue(m_MySchemaMap.entity, m_MySchemaMap.WIT, Stats.MigrationStatus.Warning, ReportIssueType.Warning, String.Empty, m_sourceId, IssueGroup.Wi, warningMsg ); } else { m_referencedEntities.Add(new LinkRecord(refEntityName, refFldVal)); } } } } break; case CQConstants.FIELD_REFERENCE_LIST: { // get the current entity def handle OAdEntityDef refEntityDef = CQWrapper.GetFieldReferenceEntityDef(curEntityDef, fldName); string refEntityName = CQWrapper.GetEntityDefName(refEntityDef); // special handling for user list // we dont want to create a link in this case.. // concatenate all the user names separated by comma // NO USER MAP WILL BE APPLIED WHILE SAVING (bug#400276) if (TFStringComparer.WorkItemType.Equals(refEntityName, "users")) { if (CQWrapper.GetFieldValueStatus(fldInfo) == (int)CQConstants.FieldStatus.HAS_VALUE) { object[] refFldValues = CQWrapper.GetFieldValueAsList(fldInfo); 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); } m_imWorkItem.InitialView.Add(fldName, userList.ToString()); } } else if (m_cqParams.allowedEntities.ContainsKey(refEntityName)) { int valueStatus = CQWrapper.GetFieldValueStatus(fldInfo); Logger.WriteIf((valueStatus != (int)CQConstants.FieldStatus.HAS_VALUE), LogSource.CQ, TraceLevel.Info, "No Value for Referenced field {0} in Entity {1}", fldName, m_entityName); if (valueStatus == (int)CQConstants.FieldStatus.HAS_VALUE) { // value list expected object[] refFldValues = CQWrapper.GetFieldValueAsList(fldInfo); foreach (object refFldObj in refFldValues) { string refFldVal = (string)refFldObj; if (String.Equals(refFldVal, SourceId, StringComparison.Ordinal)) { // reference to self.. cannot have a link on to self string warningMsg = UtilityMethods.Format(CQResource.CQ_SELF_REFERENCE, SourceId, EntityName, fldName); Logger.Write(LogSource.CQ, TraceLevel.Warning, warningMsg); PostMigrationReport.WriteIssue(m_MySchemaMap.entity, m_MySchemaMap.WIT, Stats.MigrationStatus.Warning, ReportIssueType.Warning, String.Empty, m_sourceId, IssueGroup.Wi, warningMsg); } else { m_referencedEntities.Add(new LinkRecord(refEntityName, refFldVal)); } } } } } break; case CQConstants.FIELD_ATTACHMENT_LIST: case CQConstants.FIELD_STATE: case CQConstants.FIELD_JOURNAL: case CQConstants.FIELD_DBID: case CQConstants.FIELD_STATETYPE: case CQConstants.FIELD_RECORDTYPE: Logger.Write(LogSource.CQ, TraceLevel.Info, "Skipping the Field migration for Internal Field Type '{0}'", cqFieldType); // not migrating these fields as they are CQ internal fields continue; default: Logger.Write(LogSource.CQ, TraceLevel.Info, "Skipping the Field migration for Unkknown Field Type '{0}'", cqFieldType); break; } } } // end of foreachfields // add the source id and db separately m_imWorkItem.InitialView.Add(CommonConstants.VSTSSrcIdField, m_sourceId); m_imWorkItem.InitialView.Add(CommonConstants.VSTSSrcDbField, m_cqParams.uniqueInstId); // use vstshelper to migrate the data wiHelper = (VSTSWorkItemHelper)m_MySchemaMap.vstsHelper; wiHelper.IsWIMigrated(checkList); // get attachments in the imworkitem ProcessAttachments(); // history processing will use same imWorkItem for first history info // and create other history indexes int migratedHistory = 0; if (wiHelper.IsCurrentWorkItemValid()) { migratedHistory = wiHelper.GetCurrentWorkItemHistoryCount(); if (migratedHistory > 0) { // We are going for incremental migration. And as we stuff first history item of a CQBug // into InitialView itself, actual no. of migrated history is one more than the value of // the "Migration Status" field. So increment by one. ++migratedHistory; } } ArrayList historyItems = ProcessHistory(m_imWorkItem.InitialView, migratedHistory); Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Dumping initial view for {0}", m_sourceId); foreach (object key in m_imWorkItem.InitialView.Keys) { Logger.Write(LogSource.CQ, TraceLevel.Verbose, "{0} - {1}", key, m_imWorkItem.InitialView[key]); } bool initialViewStatus = true; try { if (!partiallyMigrated) { // if some history items or links are left to be migrated.. leave the bug as opened.. if (historyItems.Count > 0 || m_referencedEntities.Count > 0) { Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Creating initial view of {0} .. {1} Histories, {2} Links pending", SourceId, historyItems.Count, m_referencedEntities.Count); // create the record and keep it open for history editing initialViewStatus = wiHelper.CreateInitialViewOfWorkItem(m_sourceId, m_imWorkItem, false); } else { Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Creating initial view of {0}", SourceId); // create all the entries in the record and set the status to done initialViewStatus = wiHelper.CreateInitialViewOfWorkItem(m_sourceId, m_imWorkItem, true); } } } catch (Exception ex) { // creation of work item failed string errMsg = UtilityMethods.Format(CQResource.CQ_WI_CREATION_FAILED, SourceId, ex.Message); CQConverter.ReportWorkItemFailure(errMsg, SourceId, m_MySchemaMap.entity, m_MySchemaMap.WIT, m_cqParams.exitOnError); if (m_cqParams.exitOnError == true) { throw new ConverterException(errMsg); } else { // continue with another work item // need to skip this work item.. m_WITId = -1; CompactMe(); return(false); } } finally { } // get back currituck id and store in this m_WITId = wiHelper.WorkItemId; // store the handle of work item to restore the state of work item helper back to // working work item which may get changed because of processing links recursively object workItem = wiHelper.GetCurrentWorkItem(); // before processing history, clean out attachments.. only if its already migrated if (wiHelper.GetCurrentWorkItemAttachmentsCount() == m_imWorkItem.Attachments.Count) { m_imWorkItem.Attachments.Clear(); } // add all the links now so that they go as part of history bool refRecordStatus = true; foreach (LinkRecord linkRec in m_referencedEntities) { if (AddReferenceRecord(linkRec) == false) { refRecordStatus = false; // once false always false } } // process duplicate records if (ProcessDuplicates(m_cqParams) == false) { refRecordStatus = false; } bool writeHistoryPassed = true; wiHelper.SetCurrentWorkItem(workItem); if (historyItems.Count > 0 || m_imWorkItem.Links.Count > 0 || m_imWorkItem.Attachments.Count > 0) { m_imWorkItem.HistoryItems = historyItems; try { writeHistoryPassed = wiHelper.WriteHistoryItems(m_sourceId, m_imWorkItem, refRecordStatus && initialViewStatus); if (!writeHistoryPassed) { // Bug#59861: In the case of the partially migrated bug, // converter says all bugs migrated successfully in // summary, but in error section it says one bug failed // due to attachment size issue. This issue has already // been written to the report. Just need to update the // statistics info. PostMigrationReport.WriteIssue(m_MySchemaMap.entity, m_MySchemaMap.WIT, Stats.MigrationStatus.Failed, ReportIssueType.Info, null, m_sourceId, IssueGroup.Wi, null); } // set the bug migration status to done only if there were no // problems with initial view and any of the references if ((!writeHistoryPassed || !refRecordStatus || !initialViewStatus) && m_cqParams.exitOnError) { // stop processing more records CompactMe(); return(false); } } catch (Exception ex) { // creation of history failed string errMsg = UtilityMethods.Format(CQResource.CQ_WI_MIG_FAILED, SourceId, ex.Message); CQConverter.ReportWorkItemFailure(errMsg, SourceId, m_MySchemaMap.entity, m_MySchemaMap.WIT, m_cqParams.exitOnError); if (m_cqParams.exitOnError == true) { throw new ConverterException(errMsg); } else { // continue with another work item.. reporting this failure CompactMe(); return(false); } } // end of catch finally { } } // end of history items processing // add to pass count ConverterMain.MigrationReport.Statistics.NumberOfItems++; // add to per work item type section if (writeHistoryPassed) { PostMigrationReport.WriteIssue(m_MySchemaMap.entity, m_MySchemaMap.WIT, Stats.MigrationStatus.Passed, ReportIssueType.Info, null, m_sourceId, IssueGroup.Wi, null); } //compact current object CompactMe(); return(true); } // end of Populate()
private void MapLinkTypes(string[] refEntities, string entityToMigrate, OAdEntityDef entityDef) { List <string> refEntityList = new List <string>(refEntities); object[] fieldDefNameObjs = CQWrapper.GetFieldDefNames(entityDef) as object[]; // add the field reference[list] links foreach (object fieldDefNameObj in fieldDefNameObjs) { string fieldDefName = fieldDefNameObj as string; int fieldDefType = CQWrapper.GetFieldDefType(entityDef, fieldDefName); if (fieldDefType == CQConstants.FIELD_REFERENCE) { OAdEntityDef childRecordEntityDef = CQWrapper.GetFieldReferenceEntityDef(entityDef, fieldDefName); string childRecordEntityDefName = CQWrapper.GetEntityDefName(childRecordEntityDef); if (refEntityList.Contains(childRecordEntityDefName)) { var linkTypeMapping = new LinkingLinkTypeMapping(); linkTypeMapping.LeftMigrationSourceUniqueId = "[Please add Left Migration Source Migration Id]"; linkTypeMapping.RightMigrationSourceUniqueId = "[Please add Right Migration Source Migration Id]"; linkTypeMapping.LeftLinkType = string.Format("ClearQuestAdapter.LinkType.ReferenceFieldRecordLink.{0}.{1}", entityToMigrate, childRecordEntityDef); linkTypeMapping.RightLinkType = "[Please add Right link type reference name]"; if (!m_linkTypeMaps.ContainsKey(linkTypeMapping.LeftLinkType)) { m_linkTypeMaps.Add(linkTypeMapping.LeftLinkType, linkTypeMapping); } } } else if (fieldDefType == CQConstants.FIELD_REFERENCE_LIST) { OAdEntityDef childRecordEntityDef = CQWrapper.GetFieldReferenceEntityDef(entityDef, fieldDefName); string childRecordEntityDefName = CQWrapper.GetEntityDefName(childRecordEntityDef); if (refEntityList.Contains(childRecordEntityDefName)) { var linkTypeMapping = new LinkingLinkTypeMapping(); linkTypeMapping.LeftMigrationSourceUniqueId = "[Please add Left Migration Source Migration Id]"; linkTypeMapping.RightMigrationSourceUniqueId = "[Please add Right Migration Source Migration Id]"; linkTypeMapping.LeftLinkType = string.Format("ClearQuestAdapter.LinkType.ReferenceListFieldRecordLink.{0}.{1}", entityToMigrate, childRecordEntityDef); linkTypeMapping.RightLinkType = "[Please add Right link type reference name]"; if (!m_linkTypeMaps.ContainsKey(linkTypeMapping.LeftLinkType)) { m_linkTypeMaps.Add(linkTypeMapping.LeftLinkType, linkTypeMapping); } } } } // add the duplicate links var duplinkTypeMapping = new LinkingLinkTypeMapping(); duplinkTypeMapping.LeftMigrationSourceUniqueId = "[Please add Left Migration Source Migration Id]"; duplinkTypeMapping.RightMigrationSourceUniqueId = "[Please add Right Migration Source Migration Id]"; duplinkTypeMapping.LeftLinkType = "ClearQuestAdapter.LinkType.Duplicate"; duplinkTypeMapping.RightLinkType = "[Please add Right link type reference name]"; if (!m_linkTypeMaps.ContainsKey(duplinkTypeMapping.LeftLinkType)) { m_linkTypeMaps.Add(duplinkTypeMapping.LeftLinkType, duplinkTypeMapping); } }
} // end of FindAllowedEntitiesToMigrate /// <summary> /// Validate all the Entity Types on Clear Quest /// Followed by the respective Field Mappings /// </summary> /// <returns>true if successful, false in case of some error</returns> private void ValidateSchemaMapOnCQ() { Session cqSession = m_cqConnection.GetUserSession(); object[] cqEntities = (object[])CQWrapper.GetSubmitEntityDefNames(cqSession); Display.NewLine(); foreach (SchemaMapping schMap in m_convParams.SchemaMaps) { string infoMsg = UtilityMethods.Format(CQResource.SchemaValidation, schMap.WITDFile); Logger.Write(LogSource.CQ, TraceLevel.Verbose, infoMsg); bool entityFoundInCQ = false; foreach (object obj in cqEntities) { if (schMap.entity.Equals(obj.ToString(), StringComparison.OrdinalIgnoreCase)) { entityFoundInCQ = true; break; } } if (!entityFoundInCQ) { try { Display.StartProgressDisplay(infoMsg); string errMsg = UtilityMethods.Format(CQResource.CQ_ENTITY_NOT_EXIST, CurConResource.Analysis, schMap.entity, Path.GetFileName(m_convParams.SchemaMapFile)); PostMigrationReport.WriteIssue(null, null, RepStatus.Failed, ReportIssueType.Critical, String.Empty, schMap.entity, IssueGroup.Witd, errMsg); throw new ConverterException(errMsg); } finally { Display.StopProgressDisplay(); } } else { OAdEntityDef currEntityDef = CQWrapper.GetEntityDef(cqSession, schMap.entity); // can validate the fields at CQ also, for this entity string fieldMapFile = schMap.fieldMapFile; if (fieldMapFile != null) { UtilityMethods.ValidateFile(fieldMapFile, schMap.schemaMapFile); FieldMaps fldMaps = WITFieldMappings.CreateFromFile(fieldMapFile); ValidateFieldMapOnCQ(currEntityDef, fldMaps.FieldMap, fieldMapFile); // add the predefined/internal field maps FieldMapsFieldMap[] internalFldMaps = GetInternalFieldMaps(fldMaps); fldMaps.FieldMap.CopyTo(internalFldMaps, 0); fldMaps.FieldMap = internalFldMaps; // add the loaded field map in Schema Field Map for future use m_schemaFieldMap.Add(schMap.entity, fldMaps); } } } // end of foreach SchemaMappings } // end of ValidateSchemaMapOnCQ
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); } } }
/// <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); }
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); }