public ClearQuestAttachmentItem( OAdEntity aHostRecord, OAdAttachmentField aHostField, OAdAttachment aAttachment, ClearQuestConnectionConfig connectionConfiguration) { // gather info to query for the record EntityDefName = CQWrapper.GetEntityDefName(aHostRecord); EntityDispName = CQWrapper.GetEntityDisplayName(aHostRecord); // gather info to query for attachment field FieldName = CQWrapper.GetAttachmentFieldName(aHostField); string name; string comment; string dispName; int fileSize; CQWrapper.GetAttachmentMetadata(aAttachment, out name, out comment, out dispName, out fileSize); Name = name; Comment = comment; DisplayName = dispName; Length = (long)fileSize; // fileSize returned by CQ API is in bytes ConnectionConfiguration = connectionConfiguration; }
private void ComputeDeltaPerRecord( OAdEntity aRecord) { OAdEntityDef aEntityDef = CQWrapper.GetEntityDef(m_userSession, CQWrapper.GetEntityDefName(aRecord)); string recordDispName = CQWrapper.GetEntityDisplayName(aRecord); string recordEntDefName = CQWrapper.GetEntityDefName(aRecord); #region process history bool recordContentIsModified = false; bool maybeNewRecord = false; Dictionary <string, List <ClearQuestRecordItem> > historyDelta = new Dictionary <string, List <ClearQuestRecordItem> >(); Dictionary <string, int> perHistoryFieldLastIndex = new Dictionary <string, int>(); // needed for updating processed delta // find all history fields OAdHistoryFields aHistFields = CQWrapper.GetHistoryFields(aRecord); int historyFldCount = CQWrapper.HistoryFieldsCount(aHistFields); bool containsNewHistory = false; for (int histFldIndex = 0; histFldIndex < historyFldCount; histFldIndex++) { object ob = (object)histFldIndex; OAdHistoryField aHistoryField = CQWrapper.HistoryFieldsItem(aHistFields, ref ob); string historyFieldName = CQWrapper.GetHistoryFieldName(aHistoryField); // find last processed history entry for this history field string lookupItemId = CQDeltaComputationProgressLookupService.CreateHistoryItemId(recordEntDefName, recordDispName, historyFieldName); int startHistIndex = 1 + DeltaComputeProgressService.GetLastProcessedItemVersion(lookupItemId); // find all history in a particular history field int historyCount = CQWrapper.HistoryFieldHistoriesCount(aHistoryField); for (int histIndex = startHistIndex; histIndex < historyCount; histIndex++) { object obHistIndex = (object)histIndex; OAdHistory aHistory = CQWrapper.HistoryFieldHistoriesItem(aHistoryField, ref obHistIndex); CQHistory cqHistory = new CQHistory(aHistory); CQMigrationItem migrationItem = new CQHistoryMigrationItem(recordDispName, historyFieldName, histIndex); if (TranslationService.IsSyncGeneratedItemVersion(ClearQuestRecordItem.GetMigrationRecordId(recordEntDefName, recordDispName), migrationItem.MigrationItemVersion, m_configurationService.SourceId)) { continue; } if (histIndex == 0) { maybeNewRecord = true; } // add unprocessed history fields for processing if (!historyDelta.ContainsKey(historyFieldName)) { historyDelta.Add(historyFieldName, new List <ClearQuestRecordItem>(historyCount)); } historyDelta[aHistoryField.fieldname].Add(new ClearQuestRecordItem(aRecord, aHistory, historyFieldName, histIndex.ToString())); containsNewHistory = true; // based on action type, we decide whether content change is needed int actionType = CQWrapper.GetActionDefType(aEntityDef, cqHistory.Action); switch (actionType) { case CQConstants.ACTION_SUBMIT: break; case CQConstants.ACTION_MODIFY: recordContentIsModified = true; break; case CQConstants.ACTION_CHANGE: break; case CQConstants.ACTION_DUPLICATE: break; case CQConstants.ACTION_UNDUPLICATE: break; case CQConstants.ACTION_IMPORT: break; case CQConstants.ACTION_DELETE: TraceManager.TraceInformation(ClearQuestResource.ClearQuest_Msg_RecordDeleted, recordEntDefName, recordDispName); break; case CQConstants.ACTION_BASE: break; case CQConstants.ACTION_RECORD_SCRIPT_ALIAS: break; } } perHistoryFieldLastIndex.Add(historyFieldName, historyCount - 1); } #endregion #region generate delta for content and history if (maybeNewRecord || recordContentIsModified) { // the first revision, i.e. "Submit", of a CQ record is always hard-coded to be '1' CQMigrationItem contentMigrationAction = new CQMigrationItem(recordDispName, ClearQuestRecordItem.NewRecordVersion); bool isNewRecord = false; if (maybeNewRecord) { isNewRecord = !(DeltaComputeProgressService.IsMigrationItemProcessed(recordDispName, ClearQuestRecordItem.NewRecordVersionValue)); } if (!isNewRecord) { // all subsequent record "MODIFICATIONs" are hard-coded to be "update@<Now.Ticks>" contentMigrationAction.MigrationItemVersion = ClearQuestRecordItem.RecordUpdateVersion + "@" + DateTime.Now.Ticks; } ClearQuestRecordItem recordContentItem = new ClearQuestRecordItem(aRecord, contentMigrationAction.MigrationItemVersion); recordContentItem.CQSession = m_userSession; recordContentItem.Version = contentMigrationAction.MigrationItemVersion; ChangeGroup contentChangeGroup = recordContentItem.CreateChangeGroup( m_changeGroupService, m_migrationContext, isNewRecord && m_isLastRevisionAutoCorrectionEnabled); contentChangeGroup.Save(); if (isNewRecord && !containsNewHistory) { DeltaComputeProgressService.UpdateCache(recordDispName, ClearQuestRecordItem.NewRecordVersionValue); } } var lastHistoryRecordItem = historyDelta[historyDelta.Keys.Last()].Last(); foreach (string histFieldName in historyDelta.Keys) { foreach (ClearQuestRecordItem recordHistItem in historyDelta[histFieldName]) { recordHistItem.CQSession = m_userSession; ChangeGroup changeGroup = recordHistItem.CreateChangeGroup( m_changeGroupService, m_migrationContext, (CQStringComparer.FieldName.Equals(recordHistItem.HistoryFieldName, lastHistoryRecordItem.HistoryFieldName) && recordHistItem.Version.Equals(lastHistoryRecordItem.Version, StringComparison.OrdinalIgnoreCase) && m_isLastRevisionAutoCorrectionEnabled)); changeGroup.Save(); } Debug.Assert(perHistoryFieldLastIndex.ContainsKey(histFieldName), "perHistoryFieldLastIndex.ContainsKey(histFieldName) returns false"); string deltaComputeProcessLookupId = CQDeltaComputationProgressLookupService.CreateHistoryItemId(recordEntDefName, recordDispName, histFieldName); DeltaComputeProgressService.UpdateCache(deltaComputeProcessLookupId, perHistoryFieldLastIndex[histFieldName]); } #endregion #region process attachment OAdAttachmentFields aAttachmentFields = CQWrapper.GetAttachmentFields(aRecord); for (int aAttachmentFieldsIndex = 0; aAttachmentFieldsIndex < CQWrapper.AttachmentsFieldsCount(aAttachmentFields); aAttachmentFieldsIndex++) { object ob = (object)aAttachmentFieldsIndex; OAdAttachmentField aAttachmentField = CQWrapper.AttachmentsFieldsItem(aAttachmentFields, ref ob); string fieldName = CQWrapper.GetAttachmentFieldName(aAttachmentField); ChangeGroup changeGroup = m_changeGroupService.CreateChangeGroupForDeltaTable( string.Format("{0}:{1}:{2}", recordDispName, "Attachments", fieldName)); // process all attachments OAdAttachments attachments = CQWrapper.GetAttachments(aAttachmentField); for (int attachmentIndex = 0; attachmentIndex < CQWrapper.AttachmentsCount(attachments); attachmentIndex++) { object obIndex = (object)attachmentIndex; OAdAttachment aAttachment = CQWrapper.AttachmentsItem(attachments, ref obIndex); ClearQuestAttachmentItem attachmentItem = new ClearQuestAttachmentItem(aRecord, aAttachmentField, aAttachment, UserSessionConnConfig); attachmentItem.CQSession = m_userSession; attachmentItem.CreateChangeAction(changeGroup, lastHistoryRecordItem.Version); } if (changeGroup.Actions.Count > 0) { changeGroup.Save(); } } #endregion }
public void Download(string localPath) { byte[] metadataHash = HashAttachmentMetadata(Name, Comment, DisplayName, Length); if (null == CQSession) { throw new InvalidOperationException("CQSession == NULL"); } // [teyang] TODO: validation on localPath if (File.Exists(localPath)) { File.Delete(localPath); } OAdEntity aRecord = CQWrapper.GetEntity(CQSession, EntityDefName, EntityDispName); OAdAttachmentFields aAllAttFields = CQWrapper.GetAttachmentFields(aRecord); bool attachmentFound = false; for (int attFieldsIndex = 0; attFieldsIndex < CQWrapper.AttachmentsFieldsCount(aAllAttFields); attFieldsIndex++) { object ob = (object)attFieldsIndex; OAdAttachmentField aAttField = CQWrapper.AttachmentsFieldsItem(aAllAttFields, ref ob); string fieldName = CQWrapper.GetAttachmentFieldName(aAttField); if (!CQStringComparer.FieldName.Equals(fieldName, this.FieldName)) { // not the hosting attachment field, try next one continue; } // attachment field is found, now look for the attachment OAdAttachments aAttachments = CQWrapper.GetAttachments(aAttField); OAdAttachment aAttachment = null; for (int attachmentIndex = 0; attachmentIndex < CQWrapper.AttachmentsCount(aAttachments); attachmentIndex++) { object obIndex = (object)attachmentIndex; aAttachment = CQWrapper.AttachmentsItem(aAttachments, ref obIndex); string name; string comment; string dispName; int fileSize; CQWrapper.GetAttachmentMetadata(aAttachment, out name, out comment, out dispName, out fileSize); byte[] hash = HashAttachmentMetadata(name, comment, dispName, (long)fileSize); if (HashProducer.CompareMD5(metadataHash, hash) == 0) { attachmentFound = true; break; } } if (attachmentFound) { Debug.Assert(null != aAttachment, "null == aAttachment"); CQWrapper.LoadAttachment(aAttachment, localPath); } // we've checked the host att field already, no need to proceed break; } if (!attachmentFound) { // [teyang] TODO: typed exception, AttachmentNotFound conflict handling throw new Exception(string.Format("attachment '{0}' is not found", Name)); } }