} // end of CreateUserSession /// <summary> /// Creates a Admin Session /// </summary> private void CreateAdminSession() { // create and initialize admin session object cqAdminSession = CQWrapper.CreateAdminSession(); Logger.WritePerf(LogSource.CQ, "Connecting to CQ Admin Session"); CQWrapper.AdminLogon(cqAdminSession, m_user, m_pwd, m_dbSet, m_configFile); Logger.WritePerf(LogSource.CQ, "Connected to CQ Admin Session"); OAdUser cqUser = CQWrapper.GetUser(cqAdminSession, m_user); try { if (!CQWrapper.IsSuperUser(cqUser)) { string errMsg = UtilityMethods.Format(CQResource.CQ_NO_ADMIN_RIGHT, CurConResource.Analysis, m_configFile); Logger.Write(LogSource.CQ, TraceLevel.Error, errMsg); ConverterMain.MigrationReport.WriteIssue(String.Empty, errMsg, string.Empty /* no item */, null, "Config", ReportIssueType.Critical); throw new ConverterException(errMsg); } } catch (System.Runtime.InteropServices.COMException ex) { string errMsg = UtilityMethods.Format(CQResource.CQ_NO_ADMIN_RIGHT, m_configFile); Logger.WriteException(LogSource.CQ, ex); Logger.Write(LogSource.CQ, TraceLevel.Error, errMsg); ConverterMain.MigrationReport.WriteIssue(String.Empty, errMsg, string.Empty /* no item */, null, "Config", ReportIssueType.Critical); throw new ConverterException(errMsg); } } // end of CreateAdminSession
} // end of ProcessStatesAndTransitions /// <summary> /// Process the CQ properties for USER type field /// </summary> /// <param name="cqFieldType">ClearQuest field type</param> /// <param name="witField">WIT Field handle</param> /// <param name="fldMap">Field Map handle (will be populated with Field Map)</param> private void ProcessUserFieldProperties(int cqFieldType, FieldDefinition witField, ref FieldMapsFieldMap fldMap) { // set the type explicitly as string for user type field witField.type = CQConstants.WITFieldTypes[CQConstants.FIELD_SHORT_STRING]; fldMap = new FieldMapsFieldMap(); if (cqFieldType == CQConstants.FIELD_REFERENCE) { // set the VALIDUSER constraint only if its a single reference // for reference list of users, migrate as simple string field bug#399176 witField.VALIDUSER = new ValidUserRule(); fldMap.ValueMaps = new FieldMapsFieldMapValueMaps(); fldMap.ValueMaps.refer = CQConstants.UserMapXMLValue; } else { string warningMsg = UtilityMethods.Format(CQResource.CQ_USER_LIST_CHANGED, witField.OldFieldName); ConverterMain.MigrationReport.WriteIssue(String.Empty, warningMsg, CQWrapper.GetEntityDefName(this.cqEntity), null, IssueGroup.Witd.ToString(), ReportIssueType.Warning); Logger.Write(LogSource.CQ, TraceLevel.Warning, warningMsg); } // check only for requiredness for User field ProcessFieldProperties(witField, cqFieldType, true); fldMap.from = witField.OldFieldName; fldMap.to = witField.name; // new field name.. if changed fldMap.exclude = "false"; }
} // end of CQConnection Ctor /// <summary> /// Creates User session, Admin session and executes the query /// </summary> public void Initialize() { m_pwd = System.Environment.GetEnvironmentVariable("CQUserPwd"); if (m_pwd != null) { m_pwd = m_pwd.Trim(); } if (m_pwd == null) { Console.Write(UtilityMethods.Format(CQResource.CQ_ENTER_PWD, m_user)); m_pwd = Microsoft.TeamFoundation.Converters.Utility.LocalizedPasswordReader.ReadLine(); Display.NewLine(); } CreateUserSession(); CreateAdminSession(); try { m_queryDef = CQWrapper.GetQueryDef(CQWrapper.GetWorkSpace(cqUserSession), m_query); } catch (ConverterException conEx) { string errMsg = UtilityMethods.Format(CQResource.CQ_INVALID_QUERY, CurConResource.Analysis, m_query, m_configFile); Logger.Write(LogSource.CQ, TraceLevel.Error, errMsg); ConverterMain.MigrationReport.WriteIssue(String.Empty, errMsg, string.Empty /* no item */, null, "Config", ReportIssueType.Critical); throw new ConverterException(errMsg, conEx); } }
/// <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); }
/// <summary> /// Creates a User Session /// </summary> private void CreateUserSession() { // create and initialize session object cqUserSession = CQWrapper.CreateSession(); Logger.WritePerf(LogSource.CQ, "Connecting to CQ User Session"); CQWrapper.UserLogon(cqUserSession, m_user, m_pwd, m_userDb, (int)CQConstants.SessionType.PRIVATE, m_dbSet, m_configFile); Logger.WritePerf(LogSource.CQ, "Connected to CQ User Session"); } // end of CreateUserSession
} // 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
/// <summary> /// Generate User Mappings based on the Clearquest users /// Each User mapping generates the same user name in to section also /// </summary> private void GenerateDefaultUserMaps(string userMapFileName) { Logger.WritePerf(LogSource.CQ, "Generating Default User Map"); AdminSession cqAdminSess = m_cqConnection.GetAdminSession(); OAdUsers users = CQWrapper.GetUsers(cqAdminSess); // create a instance of usermap.xml file UserMappings userMaps = new UserMappings(userMapFileName); for (int userindx = 0; userindx < users.Count; userindx++) { object userObj = (object)userindx; OAdUser aUser = CQWrapper.GetUser(users, ref userObj); userMaps.Add(aUser.Name, aUser.Name); } userMaps.Flush(); Logger.WritePerf(LogSource.CQ, "Default User Map Generation Done"); } // end of GenerateDefaultUserMaps()
/// <summary> /// Create the WIT in XML and all the underlying subelements /// </summary> private void CreateWorkItemType() { Logger.EnteredMethod(LogSource.CQ); WorkItemType wit = new WorkItemType(); wit.name = CQWrapper.GetEntityDefName(cqEntityDef); // set the WIT witdSchema.SetWorkItemType(wit); wit.FORM = new Form(); // add Fields to this WIT ProcessFields(wit); // add Workflow to WIT switch (CQWrapper.GetEntityDefType(cqEntityDef)) { case CQConstants.STATE_BASED: case CQConstants.STATE_OR_STATELESS: { Logger.Write(LogSource.CQ, TraceLevel.Info, "Workflow is either State Based or Stateless"); ProcessWorkflow(wit); break; } case CQConstants.STATE_LESS: { // whatever is the type of state we have to generate the workflow information // as that is mandatory as per the XSD file rules.. so a dummy block for Workflow // will be generated Logger.Write(LogSource.CQ, TraceLevel.Info, "Workflow is Stateless. Adding Dummy Workflow"); wit.WORKFLOW = new Workflow(); CreateDummyWorkflow(wit.WORKFLOW); break; } } Logger.ExitingMethod(LogSource.CQ); }
/// <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> /// add States and Transitions in WIT /// </summary> /// <param name="wf">Handle to Workflow item</param> private void ProcessStatesAndTransitions(Workflow wf) { Logger.EnteredMethod(LogSource.CQ, wf); // get all states from CQ object[] cqStates = (object[])CQWrapper.GetStateDefNames(cqEntityDef); if (cqStates.Length > 0) { string[] states = new string[cqStates.Length]; Logger.Write(LogSource.CQ, TraceLevel.Verbose, "No of CQ States: [{0}]", cqStates.Length); int l = 0; foreach (string aState in cqStates) { Logger.Write(LogSource.CQ, TraceLevel.Verbose, "CQ State: [{0}]", aState); State witState = new State(); witState.value = aState; wf.WITDSTATES.Add(witState); // add the state in states array states[l++] = aState; } string startState = null; object[] cqActions = (object[])CQWrapper.GetActionDefNames(cqEntityDef); string submitActionName = null; foreach (string action in cqActions) { Logger.Write(LogSource.CQ, TraceLevel.Verbose, "CQ Action: [{0}]", action); int actType = CQWrapper.GetActionDefType(cqEntityDef, action); if (actType == CQConstants.ACTION_SUBMIT) { submitActionName = action; startState = CQWrapper.GetActionDestStateName(cqEntityDef, submitActionName); Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Found Start State : From [\"\"] to [{0}]", startState); // add a transition from NULL state to START state Transition witTransition = new Transition(); witTransition.from = String.Empty; witTransition.to = startState; // add the default reason as NEW for first transition Reason transReason = new Reason(); transReason.value = submitActionName; witTransition.REASONS.DEFAULTREASON = transReason; wf.WITDTRANSITIONS.Add(witTransition); break; } } // find out all the duplicate and unduplicate actions and duplicate states List <string> unduplicateActions = new List <string>(); List <string> duplicateStates = new List <string>(); object[] allActions = (object[])CQWrapper.GetActionDefNames(cqEntityDef); foreach (string action in allActions) { int actionType = CQWrapper.GetActionDefType(cqEntityDef, action); if (actionType == CQConstants.ACTION_DUPLICATE) { string dupActState = CQWrapper.GetActionDestStateName(cqEntityDef, action); if (!string.IsNullOrEmpty(dupActState)) { duplicateStates.Add(dupActState); } Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Duplicate Action : [{0}]", action); Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Duplicate State : [{0}]", dupActState); } else if (actionType == CQConstants.ACTION_UNDUPLICATE) { unduplicateActions.Add(action); Logger.Write(LogSource.CQ, TraceLevel.Verbose, "UnDuplicate Action : [{0}]", action); } } // prepare the state transition matrix for (int dstState = 0; dstState < states.Length; dstState++) { // visit all the source states which can lead to the current destination state for (int srcState = 0; srcState < states.Length; srcState++) { Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Calling DoesTransitionExist Source State: [{0}], Dest State: [{1}]", states[srcState], states[dstState]); object[] transitions = (object[])CQWrapper.DoesTransitionExist(cqEntityDef, states[srcState], states[dstState]); if (transitions != null && transitions.Length > 0) { Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Does Transition returned [{0}] transitions", transitions.Length); Transition witTransition = new Transition(); witTransition.from = states[srcState]; witTransition.to = states[dstState]; // add the reason as the ACTION defined in CQ Reason transReason = new Reason(); transReason.value = (string)transitions.GetValue(0); witTransition.REASONS.DEFAULTREASON = transReason; wf.WITDTRANSITIONS.Add(witTransition); Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Adding Transitions [{0}] to [{1}] with Reason [{2}]", states[srcState], states[dstState], transReason.value); } else { Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Does Transition returned No transitions"); } } // now for each duplicate action find out all the states that lead to duplicate action // so that back transition could be added for (int dupStateIndex = 0; dupStateIndex < duplicateStates.Count; dupStateIndex++) { string dupState = duplicateStates[dupStateIndex]; // here dststate is being used as the source state and the destination state is Duplicate Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Calling DoesTransitionExist Source State: [{0}], Dest State: [{1}]", states[dstState], dupState); object[] transitions = (object[])CQWrapper.DoesTransitionExist(cqEntityDef, states[dstState], dupState); if (transitions != null && transitions.Length > 0) { Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Does Transition returned [{0}] transitions", transitions.Length); bool found = false; // if this transition is not already there.. foreach (Transition trans in wf.WITDTRANSITIONS) { if (TFStringComparer.WorkItemStateName.Equals(trans.from, dupState) && TFStringComparer.WorkItemStateName.Equals(trans.to, states[dstState])) { found = true; // add all the unduplicate actions name as allowed reasons foreach (string undupActionName in unduplicateActions) { // string undupActionName = (string)duplicateActions[dupStateIndex]; if (trans.REASONS.DEFAULTREASON.Equals(undupActionName)) { // reason already there.. skip this duplicate state and move to next state continue; // skip this reason and add other reasons } // check in other reasons if (trans.REASONS.REASON == null) { // no reasons added yet.. add this trans.REASONS.WITDREASON.Add(undupActionName); } else { // check if this reason already there if (!trans.REASONS.REASON.Contains(undupActionName)) { trans.REASONS.REASON.Add(undupActionName); } } } // move to next state break; } // end of if transition exist } if (!found) { // this back transition is not defined.. create it now // add a unduplicate transition Transition witTransition = new Transition(); witTransition.from = dupState; witTransition.to = states[dstState]; // add the reason as the ACTION defined in CQ bool isFirst = true; foreach (string undupActionName in unduplicateActions) { Reason transReason = new Reason(); transReason.value = undupActionName; if (isFirst) { witTransition.REASONS.DEFAULTREASON = transReason; isFirst = false; } else { witTransition.REASONS.WITDREASON.Add(transReason); } } wf.WITDTRANSITIONS.Add(witTransition); } } else { Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Does Transition returned No transitions"); } } // end of duplicate action handling } // end of preparing state transition matrix for loop } //if (cqStates.Length > 0) else { // if the current entity does not have any states the minimal // state model has top be created Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Creating Dummy Workflow for {0}", schemaXMLFileName); CreateDummyWorkflow(wf); } Logger.ExitingMethod(LogSource.CQ); } // end of ProcessStatesAndTransitions
/// <summary> /// Process the field properties and add it in WITD /// </summary> /// <param name="witField">Handle to Field instance to process the properties</param> /// <param name="cqFieldType">ClearQuest field type</param> /// <param name="isUserField">Is it a user field</param> private void ProcessFieldProperties(FieldDefinition witField, int cqFieldType, bool isUserField) { Logger.EnteredMethod(LogSource.CQ, witField); switch (CQWrapper.GetEntityFieldRequiredness(cqEntity, witField.OldFieldName)) { case CQConstants.MANDATORY: { PlainRule pRule = new PlainRule(); witField.REQUIRED = pRule; break; } case CQConstants.READONLY: { // PlainRule pRule = new PlainRule(); // witField.READONLY = pRule; string roMsg = UtilityMethods.Format(CQResource.CQ_FLD_READONLY_CHANGED, witField.OldFieldName); Logger.Write(LogSource.CQ, TraceLevel.Warning, roMsg); ConverterMain.MigrationReport.WriteIssue(String.Empty, roMsg, CQWrapper.GetEntityDefName(this.cqEntity), null, IssueGroup.Witd.ToString(), ReportIssueType.Warning); break; } case CQConstants.OPTIONAL: // no handling for these behaviors break; case CQConstants.USEHOOK: // no handling for these behaviors string hookMsg = UtilityMethods.Format(CQResource.CQ_FLD_SKIP_HOOK, witField.OldFieldName); ConverterMain.MigrationReport.WriteIssue(String.Empty, hookMsg, CQWrapper.GetEntityDefName(this.cqEntity), null, IssueGroup.Witd.ToString(), ReportIssueType.Warning); Logger.Write(LogSource.CQ, TraceLevel.Warning, hookMsg); break; } if (!isUserField) { int choiceType = CQWrapper.GetFieldChoiceType(cqEntity, witField.OldFieldName); object[] choices = (object[])CQWrapper.GetFieldChoiceList(cqEntity, witField.OldFieldName); ListRule choiceList = null; if (choices != null && choices.Length > 0) { choiceList = new ListRule(); foreach (object ob in choices) { ListItem lItem = new ListItem(); lItem.value = (string)ob; choiceList.WITDItems.Add(lItem); } // decide whether its ALLOWED or SUGGESTED list if (choiceType == CQConstants.CLOSED_CHOICE && cqFieldType != CQConstants.FIELD_MULTILINE_STRING) { witField.ALLOWEDVALUES = choiceList; } else { // since currituck does not allow multi value list, for all such fields // set the List Type to Suggested Values (instead of Allowed Values) witField.SUGGESTEDVALUES = choiceList; } // if there is a field containing any of Suggested/Allowed/Prohibited // values, the fieldtype has to be either string or integer.. // as per the CQ-Currituck field mappings, we map Multiline String // to Plain Text.. // So if it is niether String nor Integer, Move it to String type.. if (witField.type != FieldType.String && witField.type != FieldType.Integer) { Logger.Write(LogSource.CQ, TraceLevel.Warning, "Converting Field {0} of type {1} to String type", witField.OldFieldName, witField.type); witField.type = FieldType.String; } } } Logger.ExitingMethod(LogSource.CQ); } // end of ProcessFieldProperties
/// <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
} // 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()
} // end of ProcessHistory() /// <summary> /// Processes the attachments for current record /// </summary> private void ProcessAttachments() { int noOfAttachments = 0; try { OAdAttachmentFields allAttachments = CQWrapper.GetAttachmentFields(m_CQEntity); StringCollection filesAttached = new StringCollection(); int attachmentsIndex = 0; for (attachmentsIndex = 0; attachmentsIndex < CQWrapper.AttachmentsFieldsCount(allAttachments); attachmentsIndex++) { object ob = (object)attachmentsIndex; OAdAttachmentField attachmentFld = CQWrapper.AttachmentsFieldsItem(allAttachments, ref ob); // process all attachments OAdAttachments attachments = CQWrapper.GetAttachments(attachmentFld); int attachmentIndex; for (attachmentIndex = 0; attachmentIndex < CQWrapper.AttachmentsCount(attachments); attachmentIndex++) { // there are some attachments // create the dir for this bug dbid only if string dirName = Path.Combine(CQConstants.AttachmentsDir, m_dbid.ToString()); if (attachmentIndex == 0) // for the first time { // check if dir with curr dbid exist if (Directory.Exists(dirName)) { // if the dir exists, clean the contents // as it is the system created dir Logger.Write(LogSource.CQ, TraceLevel.Warning, "Removing folder {0} recursively for attachments of entity {1}", m_dbid, m_entityName); try { Directory.Delete(dirName, true); } catch (IOException ioe) { Logger.Write(LogSource.CQ, TraceLevel.Warning, "Failed to delete folder {0} containing attachments for work item {1}", dirName, SourceId); Logger.WriteException(LogSource.CQ, ioe); } catch (UnauthorizedAccessException uae) { Logger.Write(LogSource.CQ, TraceLevel.Warning, "Permission denied for deleting folder {0} containing attachments for work item {1}", dirName, SourceId); Logger.WriteException(LogSource.CQ, uae); } } Logger.Write(LogSource.CQ, TraceLevel.Info, "Creating folder {0} for attachments of entity {1}", m_dbid, m_entityName); UtilityMethods.CreateDirectory(dirName); } // for every attachments attachment create a separate // sub dir as (attchmentsIndex+attchmentIndex) string currAttachmentDir = Path.Combine(dirName, attachmentsIndex.ToString(CultureInfo.InvariantCulture)); currAttachmentDir = Path.Combine(currAttachmentDir, attachmentIndex.ToString(CultureInfo.InvariantCulture)); // there cannot be any dir existing as we already cleaned up Logger.Write(LogSource.CQ, TraceLevel.Info, "Creating folder {0} for attachment {1} of entity {2}", currAttachmentDir, attachmentIndex + 1, m_entityName); UtilityMethods.CreateDirectory(currAttachmentDir); object obIndex = (object)attachmentIndex; OAdAttachment aAttachment = CQWrapper.AttachmentsItem(attachments, ref obIndex); noOfAttachments++; string attachFileName; string attachDescription; CQWrapper.GetAttachmentFileNameAndDescription(aAttachment, out attachFileName, out attachDescription); Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Adding attachment"); Logger.Write(LogSource.CQ, TraceLevel.Verbose, "\tFile Name : {0}", attachFileName); Logger.Write(LogSource.CQ, TraceLevel.Verbose, "\tDescription : {0}", attachDescription); // save the attachment as file on disk string tempfile = Path.Combine(currAttachmentDir, attachFileName); CQWrapper.LoadAttachment(aAttachment, tempfile); m_imWorkItem.Attachments.Add(new InMemoryAttachment(tempfile, attachDescription, false)); filesAttached.Add(attachFileName); #if DEBUG CommonConstants.NoOfAttachments++; CommonConstants.TotalAttachmentSize += CQWrapper.AttachmentFileSize(aAttachment); #endif } // end of processing attachment } // end of processing attachments } finally { } } // end of GetAttachments
} // end of Initialize /// <summary> /// Starts the actual schema migration followed by data migration /// </summary> public void Convert() { Session cqSession = m_cqConnection.GetUserSession(); OAdQuerydef qryDef = m_cqConnection.QueryDefinition; // get the base entity definition to analyze string baseEntityDefName = CQWrapper.GetPrimaryEntityDefName(qryDef); Debug.Assert(baseEntityDefName != null); Logger.WritePerf(LogSource.CQ, "Start Analyze"); // set o/p directory only if it is not specified in the CQConfig file.. // otherwise its already set / created in Initialize section if (String.IsNullOrEmpty(m_convParams.OutputDirectory)) { CQConverterUtil.SetOutputDirectory(baseEntityDefName, m_convParams.ConfigFile); } if (baseEntityDefName != null) { string[] refEntities = CQConverterUtil.GetReferencedEntityDefNames(cqSession, baseEntityDefName, m_convParams.ConfigFile); StringBuilder infoMsg1 = new StringBuilder(UtilityMethods.Format(CQResource.CQ_ENTITY_MIGRATED)); foreach (string str in refEntities) { infoMsg1.Append(str); infoMsg1.Append(", "); // REVIEW - GautamG: String not localized } infoMsg1.Remove(infoMsg1.Length - 2, 1); // remove last comma infoMsg1.Append(Environment.NewLine); Logger.Write(LogSource.CQ, TraceLevel.Info, infoMsg1.ToString()); Display.DisplayMessage(infoMsg1.ToString()); // create the schema map file so that for each WITD xml generation // the entries are added in schema map WITSchemaMappings schemaMap = new WITSchemaMappings(); int reportEntityIndex = 0; ConverterMain.MigrationReport.Summary.SourceAndDestination.WorkItemTypes.WorkItemTypeTypes = new WorkItemTypeTypes[refEntities.Length]; foreach (string entityToMigrate in refEntities) { if (entityToMigrate != null) { // process the given entity definition and generate xml for each entity // one for the base entity and one for each of referenced entities OAdEntityDef entityDef = CQWrapper.GetEntityDef(cqSession, entityToMigrate); string entityDefName = entityDef.GetName(); string schemaXmlFile = entityDefName + ".xml"; // get the file name prepended with the path.. to be generated under base entity name folder schemaXmlFile = CQConverterUtil.GetFileNameWithPath(schemaXmlFile); string fieldMapXmlFile = entityDefName + CQConstants.FieldMapFileSuffix; fieldMapXmlFile = CQConverterUtil.GetFileNameWithPath(fieldMapXmlFile); // add the default map to schema schemaMap.Mappings.AddSchemaMap(entityDefName, entityDefName, schemaXmlFile, fieldMapXmlFile); ConverterMain.MigrationReport.AddOutput(CQResource.Witd, schemaXmlFile); ConverterMain.MigrationReport.AddOutput(CQResource.WitFieldMap, fieldMapXmlFile); WITDXMLGenerator currEntityXML = new WITDXMLGenerator(schemaXmlFile, fieldMapXmlFile, cqSession, entityDef, m_vstsConn); currEntityXML.GenerateSchemaXml(); // add the entity information in migration report WorkItemTypeTypes wiType = new WorkItemTypeTypes(); wiType.From = wiType.To = entityDefName; ConverterMain.MigrationReport.Summary.SourceAndDestination.WorkItemTypes.WorkItemTypeTypes[reportEntityIndex++] = wiType; // add the link type mappings MapLinkTypes(refEntities, entityToMigrate, entityDef); } } // foreach (string entityToMigrate in refEntities) Display.NewLine(); // generated the schemas and the corresponding field maps // finally serialize the schema map file string schemaMapFile = CQConverterUtil.GetFileNameWithPath(CQConstants.SchemaMapFile); string userMapFile = CQConverterUtil.GetFileNameWithPath(CQConstants.UserMapFile); ConverterMain.MigrationReport.AddOutput(CQResource.SchemaMap, schemaMapFile); ConverterMain.MigrationReport.AddOutput(CQResource.UserMap, userMapFile); schemaMap.GenerateSchemaMappings(schemaMapFile, userMapFile); GenerateDefaultUserMaps(userMapFile); ConverterMain.MigrationReport.Statistics.NumberOfItems = refEntities.Length; // generate the link type mapping file GenerateLinkTypeMappings(); } Logger.WritePerf(LogSource.CQ, "End Analyze"); } // end of Convert()
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 ValidateFieldMapOnCQ /// <summary> /// Execute the Query and migrate the data /// </summary> /// <param name="baseEntityName">Base Entity Name</param> private void MigrateData(string baseEntityName, string baseEntityWitName) { Session cqSession = m_cqConnection.GetUserSession(); OAdQuerydef qryDef = m_cqConnection.QueryDefinition; // edit the query and add dbid field // dbid is suppose to be unique within a Entity CQWrapper.BuildField(qryDef, "dbid"); // prepare result set OAdResultset result = CQWrapper.BuildResultSet(cqSession, qryDef); // process records for base entity CQEntity baseEntityRecords = m_cqParams.entityRecords[baseEntityName]; // enable record count before execute so that no of records can be fetched CQWrapper.EnableRecordCount(result); // execute the query CQWrapper.ExecuteResultSet(result); int columnCount = CQWrapper.GetResultSetColumnCount(result); // lookup for dbid column bool dbidExist = false; int dbidColumnIndex = 0; for (int colIter = 1; colIter <= columnCount; colIter++) { if (string.Equals(CQWrapper.GetColumnLabel(result, colIter), "dbid", StringComparison.OrdinalIgnoreCase)) { dbidExist = true; dbidColumnIndex = colIter; break; } } if (!dbidExist) { // neither query contain dbid nor can be edited to include a new column string errMsg = UtilityMethods.Format(CQResource.CQ_NO_DBID_IN_QUERY, m_cqConnection.QueryName, m_convParams.ConfigFile); PostMigrationReport.WriteIssue(null, null, RepStatus.Failed, ReportIssueType.Critical, String.Empty, baseEntityName, IssueGroup.Config, errMsg); Logger.Write(LogSource.CQ, TraceLevel.Error, errMsg); throw new ConverterException(errMsg); } // start the progress thread for updating the progress m_progressThread = new Thread(new ThreadStart(CQConverter.UpdateProgress)); m_progressThread.Name = "Progress"; try { // get the work item helper handle TotalRecords = CQWrapper.GetRecordCount(result); m_progressThread.Start(); while (CQWrapper.ResultSetMoveNext(result) == CQConstants.SUCCESS) { string dbid = (string)CQWrapper.GetColumnValue(result, dbidColumnIndex); // create a CQEntity for that CQEntityRec record = new CQEntityRec(int.Parse(dbid), baseEntityName, m_cqParams); try { // populate and migrate the record and all referenced records RecordsProcessed++; baseEntityRecords.AddRecord(record); if (record.Populate() == false && m_cqParams.exitOnError == true) { return; // stop processing more records } } catch (ConverterException conEx) { // log the error and continue with next item string errMsg = UtilityMethods.Format(CQResource.CQ_WI_READ_FAILED, dbid, conEx.Message); ReportWorkItemFailure(errMsg, dbid, baseEntityName, baseEntityWitName, m_cqParams.exitOnError); if (m_cqParams.exitOnError == true) { // throw the error back .. should not continue with the current record throw; } } } } finally { // terminate the progress thread m_progressThread.Abort(); Thread.Sleep(5000); // allow the display thread to stop } } // end of MigrateData
} // 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
/// <summary> /// Generate the XML Schema based on the data processed /// </summary> private void GenerateXml() { string entityName = CQWrapper.GetEntityDefName(cqEntityDef); string infoMsg = UtilityMethods.Format(CQResource.SchemaCreation, entityName); Logger.Write(LogSource.CQ, TraceLevel.Verbose, infoMsg); Display.StartProgressDisplay(infoMsg); try { witdSchema.GenerateWITDSchema(schemaXMLFileName); witdFieldMap.GenerateFieldMappings(fieldMapXMLFileName); // bug# 21768.. add the xml comment in witd and field map file if (fieldsToComment.Count > 0) { string commentMsg = CQResource.CoreFieldComment; // open both document in Xml DOM XmlDocument witdDoc = new XmlDocument(); XmlReader witdRdr = new XmlTextReader(schemaXMLFileName); witdDoc.Load(witdRdr); witdRdr.Close(); XmlDocument fldMapDoc = new XmlDocument(); XmlReader fldMapRdr = new XmlTextReader(fieldMapXMLFileName); fldMapDoc.Load(fldMapRdr); fldMapRdr.Close(); XmlNamespaceManager nsm = new XmlNamespaceManager(witdDoc.NameTable); nsm.AddNamespace("CQ", Const.WITDTypesNamespace); XmlNode parentFldNode = witdDoc.SelectSingleNode( String.Format("//CQ:{0}/{1}/{2}", Const.TagWitd, Const.TagWit, Const.TagFields), nsm); XmlNode parentMapNode = fldMapDoc.SelectSingleNode(Const.TagFieldMaps); foreach (string fldName in fieldsToComment) { XmlNode fldNode = parentFldNode.SelectSingleNode( String.Format("/descendant::{0}[@{1} = '{2}']", Const.TagField, Const.TagName, fldName)); if (fldNode != null) { XmlComment comment = witdDoc.CreateComment(string.Format(commentMsg, fldName)); parentFldNode.InsertBefore(comment, fldNode); } XmlNode mapNode = parentMapNode.SelectSingleNode( String.Format("/descendant::{0}[@{1} = '{2}']", Const.TagFieldMap, Const.TagTo, fldName)); if (mapNode != null) { XmlComment comment = fldMapDoc.CreateComment(string.Format(commentMsg, fldName)); parentMapNode.InsertBefore(comment, mapNode); } } // write the modified schema file back XmlTextWriter writer = new XmlTextWriter(schemaXMLFileName, null); writer.Formatting = Formatting.Indented; witdDoc.Save(writer); writer.Close(); // write the modified field map file back writer = new XmlTextWriter(fieldMapXMLFileName, null); writer.Formatting = Formatting.Indented; fldMapDoc.Save(writer); writer.Close(); } } finally { Display.StopProgressDisplay(); } } // end of Generatexml
} // end of Populate() /// <summary> /// Processes the history for current record. /// </summary> /// <param name="initialView">First history item structure</param> /// <param name="migratedHistory">No of history items already migrated</param> /// <returns>List of history items except first one</returns> private ArrayList ProcessHistory(Hashtable initialView, int migratedHistory) { int noOfHistory = 0; ArrayList historyItems = new ArrayList(); try { Logger.Write(LogSource.CQ, TraceLevel.Verbose, "Processing History for {0} : {1}", m_entityName, m_sourceId); // record all history items except the first one Hashtable currentHistory = null; OAdHistoryFields cqHistFields = CQWrapper.GetHistoryFields(m_CQEntity); int historyFldCount = CQWrapper.HistoryFieldsCount(cqHistFields); for (int histFldIndex = 0; histFldIndex < historyFldCount; histFldIndex++) { object ob = (object)histFldIndex; OAdHistoryField historyField = CQWrapper.HistoryFieldsItem(cqHistFields, ref ob); int historyCount = CQWrapper.HistoryFieldHistoriesCount(historyField); for (int histIndex = migratedHistory; histIndex < historyCount; histIndex++) { if (histIndex == 0) { // first history.. use the initial view to record history currentHistory = initialView; } else { // create a new instance to record history InMemoryHistoryItem imHistItem = new InMemoryHistoryItem(); historyItems.Add(imHistItem); currentHistory = imHistItem.UpdatedView; } object obHistIndex = (object)histIndex; OAdHistory aHistory = CQWrapper.HistoryFieldHistoriesItem(historyField, ref obHistIndex); string[] parsedString = CQWrapper.HistoryValue(aHistory).Split('\t'); Debug.Assert(parsedString != null && parsedString.Length >= 6); string date = parsedString[1]; string user = parsedString[2]; string action = parsedString[3]; string oldstate = parsedString[4]; string newstate = parsedString[5]; DateTime changedDate = DateTime.Parse(date, CultureInfo.CurrentCulture); Logger.Write(LogSource.CQ, TraceLevel.Verbose, "History Item : [{0}] [{1}] [{2}] [{3}]", changedDate, user, oldstate, newstate); string historyval = UtilityMethods.Format(CQResource.CQ_HISTORY_STRING, changedDate.ToString(CultureInfo.CurrentCulture), user, action, oldstate, newstate); currentHistory.Add("History", historyval); currentHistory.Add("user_name", user); currentHistory.Add("Reason", action); if (!String.Equals("N/A", newstate, StringComparison.Ordinal)) { // add state only if state is valid currentHistory.Add("State", newstate); } noOfHistory++; #if DEBUG CommonConstants.NoOfHistory++; #endif } } } finally { } return(historyItems); } // end of ProcessHistory()