示例#1
0
        public async Task Preprocess(IMigrationContext migrationContext, IBatchMigrationContext batchContext, IList <WorkItem> sourceWorkItems, IList <WorkItem> targetWorkItems)
        {
            var linkedWorkItemArtifactUrls = new HashSet <string>();

            foreach (WorkItem sourceWorkItem in sourceWorkItems)
            {
                var relations = GetWorkItemLinkRelations(migrationContext, sourceWorkItem.Relations);
                var linkedIds = relations.Select(r => ClientHelpers.GetWorkItemIdFromApiEndpoint(r.Url));
                var uris      = linkedIds.Where(id => !migrationContext.SourceToTargetIds.ContainsKey(id)).Select(id => ClientHelpers.GetWorkItemApiEndpoint(migrationContext.Config.SourceConnection.Account, id));
                linkedWorkItemArtifactUrls.AddRange(uris);
            }

            await linkedWorkItemArtifactUrls.Batch(Constants.BatchSize).ForEachAsync(migrationContext.Config.Parallelism, async(workItemArtifactUris, batchId) =>
            {
                Logger.LogTrace(LogDestination.File, $"Finding linked work items on target for batch {batchId}");
                var results = await ClientHelpers.QueryArtifactUriToGetIdsFromUris(migrationContext.TargetClient.WorkItemTrackingHttpClient, workItemArtifactUris);
                foreach (var result in results.ArtifactUrisQueryResult)
                {
                    if (result.Value != null)
                    {
                        if (result.Value.Count() == 1)
                        {
                            var sourceId = ClientHelpers.GetWorkItemIdFromApiEndpoint(result.Key);
                            var targetId = result.Value.First().Id;

                            migrationContext.SourceToTargetIds[sourceId] = targetId;
                        }
                    }
                }

                Logger.LogTrace(LogDestination.File, $"Finished finding linked work items on target for batch {batchId}");
            });
        }
        protected override void UpdateWorkItemMigrationStatus(IBatchMigrationContext batchContext, int sourceId, WorkItem targetWorkItem)
        {
            WorkItemMigrationState state = batchContext.WorkItemMigrationState.First(w => w.SourceId == sourceId);

            state.MigrationCompleted |= WorkItemMigrationState.MigrationCompletionStatus.Phase1;
            state.TargetId            = targetWorkItem.Id.Value;
        }
 private async Task ProcessInlineImages(IBatchMigrationContext batchContext)
 {
     foreach (WorkItem sourceWorkItem in batchContext.SourceWorkItems)
     {
         await ProcessInlineImagesForWorkItem(batchContext, sourceWorkItem);
     }
 }
示例#4
0
        private async Task ProcessInlineImagesForHtmlField(IBatchMigrationContext batchContext, int sourceWorkItemId, KeyValuePair <string, object> field)
        {
            // we are assuming that this will always be a string, so log if it is not
            if (!(field.Value is string))
            {
                Logger.LogWarning(LogDestination.File, $"Unexpected value for html field {field.Key} for source work item {sourceWorkItemId}");
            }

            string           fieldHtmlContent = field.Value as string;
            HashSet <string> inlineImageUrls  = MigrationHelpers.GetInlineImageUrlsFromField(fieldHtmlContent, this.context.SourceClient.Connection.Uri.AbsoluteUri);

            foreach (string inlineImageUrl in inlineImageUrls)
            {
                // There are scenarios where someone copy/pastes image from one work item to another and the
                // it ends up using the same inline attachment link.
                if (!batchContext.SourceInlineImageUrlToTargetInlineImageGuid.ContainsKey(inlineImageUrl))
                {
                    string targetGuid = await UploadInlineImageAttachmentFromSourceWorkItemToTarget(batchContext, inlineImageUrl, sourceWorkItemId, context.Config.MaxAttachmentSize);

                    if (!String.IsNullOrEmpty(targetGuid))
                    {
                        batchContext.SourceInlineImageUrlToTargetInlineImageGuid.Add(inlineImageUrl, targetGuid);
                    }
                }
            }
        }
示例#5
0
        public async Task <int> ProcessAreaPaths(IBatchMigrationContext batchContext)
        {
            int modificationCount = 0;

            Logger.LogInformation(LogDestination.All, $"Identified {context.SourceAreaAndIterationTree.AreaPathListLookup.Count} area paths in source project.");

            foreach (var areaPath in context.SourceAreaAndIterationTree.AreaPathList)
            {
                string areaPathInTarget = AreaAndIterationPathTree.ReplaceLeadingProjectName(areaPath,
                                                                                             context.Config.SourceConnection.Project, context.Config.TargetConnection.Project);

                // If the area path is not found in the work items we're currently processing then just ignore it.
                if (!batchContext.SourceWorkItems.Any(w => w.Fields.ContainsKey("System.AreaPath") && w.Fields["System.AreaPath"].ToString().ToLower().Equals(areaPath.ToLower())))
                {
                    continue;
                }
                else if (context.TargetAreaAndIterationTree.AreaPathList.Any(a => a.ToLower() == areaPathInTarget.ToLower()))
                {
                    Logger.LogInformation(LogDestination.All, $"[Exists] {areaPathInTarget}.");
                }
                else
                {
                    modificationCount += 1;
                    await CreateAreaPath(areaPathInTarget);

                    Logger.LogSuccess(LogDestination.All, $"[Created] {areaPathInTarget}.");
                }
            }

            Logger.LogInformation(LogDestination.All, $"Area paths synchronized.");
            return(modificationCount);
        }
        protected override void BatchCompleted(IMigrationContext migrationContext, IBatchMigrationContext batchContext)
        {
            var successfullyMigrated = batchContext.WorkItemMigrationState.Where(m => m.MigrationCompleted == WorkItemMigrationState.MigrationCompletionStatus.Phase1).Select(m => new KeyValuePair <int, int>(m.SourceId, m.TargetId.Value));

            // add successfully migrated workitems to main cache
            if (successfullyMigrated.Any())
            {
                migrationContext.SourceToTargetIds.AddRange(successfullyMigrated);
            }
        }
        public async Task Process(IBatchMigrationContext batchContext)
        {
            object           identityObject      = null;
            string           identityValue       = null;
            HashSet <string> identitiesToProcess = new HashSet <string>(StringComparer.CurrentCultureIgnoreCase);

            foreach (var sourceWorkItem in batchContext.SourceWorkItems)
            {
                foreach (var field in context.IdentityFields)
                {
                    if (sourceWorkItem.Fields.TryGetValueIgnoringCase(field, out identityObject))
                    {
                        identityValue = (string)identityObject;
                        if (!string.IsNullOrEmpty(identityValue) &&
                            identityValue.Contains("<") && identityValue.Contains(">") && (identityValue.Contains("@")))
                        {
                            // parse out email address from the combo string
                            identityValue = identityValue.Substring(identityValue.LastIndexOf("<") + 1, identityValue.LastIndexOf(">") - identityValue.LastIndexOf("<") - 1);

                            if (!identitiesToProcess.Contains(identityValue) &&
                                !this.context.ValidatedIdentities.Contains(identityValue) &&
                                !this.context.InvalidIdentities.Contains(identityValue))
                            {
                                Logger.LogTrace(LogDestination.File, $"Found identity {identityValue} in batch {batchContext.BatchId} which has not yet been validated for the target account");
                                identitiesToProcess.Add(identityValue);
                            }
                        }
                    }
                }
            }

            Logger.LogTrace(LogDestination.File, $"Adding {identitiesToProcess.Count} identities to the account for batch {batchContext.BatchId}");
            foreach (var identity in identitiesToProcess)
            {
                try
                {
                    await RetryHelper.RetryAsync(async() =>
                    {
                        return(await graphClient.CreateUserAsync(new GraphUserPrincipalNameCreationContext()
                        {
                            PrincipalName = identity
                        }, Groups));
                    }, 5);

                    context.ValidatedIdentities.Add(identity);
                }
                catch (Exception ex)
                {
                    Logger.LogWarning(LogDestination.File, ex, $"Unable to add identity {identity} to the target account for batch {batchContext.BatchId}");
                    context.InvalidIdentities.Add(identity);
                }
            }

            Logger.LogTrace(LogDestination.File, $"Completed adding {identitiesToProcess.Count} identities to the account for batch {batchContext.BatchId}");
        }
 private async Task ProcessInlineImagesForWorkItem(IBatchMigrationContext batchContext, WorkItem sourceWorkItem)
 {
     if (sourceWorkItem.Fields != null)
     {
         foreach (KeyValuePair <string, object> field in sourceWorkItem.Fields)
         {
             if (this.context.HtmlFieldReferenceNames.Contains(field.Key)) // field is of FieldType.Html
             {
                 await ProcessInlineImagesForHtmlField(batchContext, sourceWorkItem.Id.Value, field);
             }
         }
     }
 }
 public override void PrepareBatchContext(IBatchMigrationContext batchContext, IList <WorkItemMigrationState> workItemsAndStateToMigrate)
 {
     foreach (var sourceWorkItem in batchContext.SourceWorkItems)
     {
         var workItemMigrationState = workItemsAndStateToMigrate.Where(w => w.SourceId == sourceWorkItem.Id.Value).FirstOrDefault();
         if (workItemMigrationState != null && workItemMigrationState.TargetId.HasValue)
         {
             batchContext.TargetIdToSourceWorkItemMapping.Add(workItemMigrationState.TargetId.Value, sourceWorkItem);
         }
         else
         {
             Logger.LogWarning(LogDestination.File, $"Expected source work item {sourceWorkItem.Id} to map to a target work item");
         }
     }
 }
        private async Task ProcessInlineImagesForHtmlField(IBatchMigrationContext batchContext, int sourceWorkItemId, KeyValuePair <string, object> field)
        {
            string           fieldHtmlContent = (string)field.Value; // we are assuming that this will always be convertable to string
            HashSet <string> inlineImageUrls  = MigrationHelpers.GetInlineImageUrlsFromField(fieldHtmlContent, this.context.SourceClient.Connection.Uri.AbsoluteUri);

            foreach (string inlineImageUrl in inlineImageUrls)
            {
                // There are scenarios where someone copy/pastes image from one work item to another and the
                // it ends up using the same inline attachment link.
                if (!batchContext.SourceInlineImageUrlToTargetInlineImageGuid.ContainsKey(inlineImageUrl))
                {
                    string targetGuid = await UploadInlineImageAttachmentFromSourceWorkItemToTarget(batchContext, inlineImageUrl, sourceWorkItemId, context.Config.MaxAttachmentSize);

                    if (!String.IsNullOrEmpty(targetGuid))
                    {
                        batchContext.SourceInlineImageUrlToTargetInlineImageGuid.Add(inlineImageUrl, targetGuid);
                    }
                }
            }
        }
示例#11
0
        public async Task Process(IBatchMigrationContext batchContext)
        {
            int modificationCount = 0;
            await Task.WhenAll(
                Migrator.ReadSourceNodes(context, context.Config.SourceConnection.Project),
                Migrator.ReadTargetNodes(context, context.Config.TargetConnection.Project));

            if (context.Config.MoveAreaPaths)
            {
                modificationCount += await ProcessAreaPaths(batchContext);
            }

            if (context.Config.MoveIterations)
            {
                modificationCount += await ProcessIterationPaths(batchContext);
            }

            if (modificationCount > 0)
            {
                await Migrator.ReadTargetNodes(context, context.Config.TargetConnection.Project);
            }
        }
示例#12
0
 public abstract int GetWorkItemsToProcessCount(IBatchMigrationContext batchContext);
 public async Task Process(IBatchMigrationContext batchContext)
 {
     await ProcessInlineImages(batchContext);
 }
示例#14
0
 public abstract BaseWitBatchRequestGenerator GetWitBatchRequestGenerator(IMigrationContext context, IBatchMigrationContext batchContext);
示例#15
0
        public static void HandleCriticalError(WitBatchResponse witBatchResponse, IEnumerable <int> sourceIds, IBatchMigrationContext batchContext)
        {
            CriticalExceptionResponse exceptionResponse = witBatchResponse.ParseBody <CriticalExceptionResponse>();
            string exceptionMessage = exceptionResponse.Value.Message;

            Logger.LogError(LogDestination.All, $"Exception during batch {batchContext.BatchId}: {exceptionMessage}");
            MarkBatchAsFailed(batchContext, sourceIds, FailureReason.CriticalError);
        }
示例#16
0
        public async Task <IEnumerable <JsonPatchOperation> > Process(IMigrationContext migrationContext, IBatchMigrationContext batchContext, WorkItem sourceWorkItem, WorkItem targetWorkItem)
        {
            IList <JsonPatchOperation> jsonPatchOperations     = new List <JsonPatchOperation>();
            JsonPatchOperation         addPostMoveTagOperation = AddPostMoveTag(migrationContext, sourceWorkItem);

            jsonPatchOperations.Add(addPostMoveTagOperation);
            return(jsonPatchOperations);
        }
示例#17
0
        private async Task <IList <(int SourceId, WitBatchRequest WitBatchRequest)> > GenerateWitBatchRequestsForPhase2Batch(IBatchMigrationContext batchContext, int batchId, IList <WorkItemMigrationState> workItemMigrationState, IList <WorkItem> sourceWorkItems, IList <WorkItem> targetWorkItems)
        {
            IList <(int SourceId, WitBatchRequest WitBatchRequest)> result = new List <(int SourceId, WitBatchRequest WitBatchRequest)>();
            IEnumerable <IPhase2Processor> phase2Processors = ClientHelpers.GetProcessorInstances <IPhase2Processor>(context.Config);

            foreach (IPhase2Processor processor in phase2Processors)
            {
                Logger.LogInformation(LogDestination.File, $"Starting preprocessing of phase 2 step {processor.Name} for batch {batchId}");
                await processor.Preprocess(context, batchContext, sourceWorkItems, targetWorkItems);

                Logger.LogInformation(LogDestination.File, $"Completed preprocessing of phase 2 step {processor.Name} for batch {batchId}");
            }

            foreach (var sourceToTarget in batchContext.SourceWorkItemIdToTargetWorkItemIdMapping)
            {
                int sourceId = sourceToTarget.Key;
                int targetId = sourceToTarget.Value;

                WorkItem sourceWorkItem = sourceWorkItems.First(a => a.Id == sourceId);
                WorkItem targetWorkItem = targetWorkItems.First(a => a.Id == targetId);

                IList <JsonPatchOperation> jsonPatchOperations = new List <JsonPatchOperation>();

                WorkItemMigrationState state = workItemMigrationState.First(a => a.SourceId == sourceId);
                state.RevAndPhaseStatus = GetRevAndPhaseStatus(targetWorkItem, sourceId);
                ISet <string> enabledPhaseStatuses = System.Linq.Enumerable.ToHashSet(phase2Processors.Where(a => a.IsEnabled(context.Config)).Select(b => b.Name));
                enabledPhaseStatuses.Remove(Constants.RelationPhaseClearAllRelations);

                foreach (IPhase2Processor processor in phase2Processors)
                {
                    IEnumerable <JsonPatchOperation> processorJsonPatchOperations = await processor.Process(context, batchContext, sourceWorkItem, targetWorkItem);

                    jsonPatchOperations.AddRange(processorJsonPatchOperations);
                }

                jsonPatchOperations.Add(GetAddHyperlinkWithCommentOperation(targetWorkItems, state, sourceId, targetId, sourceWorkItem, enabledPhaseStatuses));

                if (this.context.Config.IncludeWebLink)
                {
                    var link = (ReferenceLink)sourceWorkItem.Links.Links["html"];
                    var addWebLinkOperation = MigrationHelpers.GetHyperlinkAddOperation(link.Href);
                    jsonPatchOperations.Add(addWebLinkOperation);
                }

                if (jsonPatchOperations.Any())
                {
                    WitBatchRequest witBatchRequest = GenerateWitBatchRequestFromJsonPatchOperations(jsonPatchOperations, targetId);
                    result.Add((sourceId, witBatchRequest));
                }
            }

            return(result);
        }
示例#18
0
 public async Task Preprocess(IMigrationContext migrationContext, IBatchMigrationContext batchContext, IList <WorkItem> sourceWorkItems, IList <WorkItem> targetWorkItems)
 {
 }
 protected override void BatchCompleted(IMigrationContext migrationContext, IBatchMigrationContext batchContext)
 {
     // no-op
 }
 public override BaseWitBatchRequestGenerator GetWitBatchRequestGenerator(IMigrationContext context, IBatchMigrationContext batchContext)
 {
     return(new UpdateWitBatchRequestGenerator(context, batchContext));
 }
 public override int GetWorkItemsToProcessCount(IBatchMigrationContext batchContext)
 {
     return(batchContext.TargetIdToSourceWorkItemMapping.Count);
 }
示例#22
0
        public async Task <IEnumerable <JsonPatchOperation> > Process(IMigrationContext migrationContext, IBatchMigrationContext batchContext, WorkItem sourceWorkItem, WorkItem targetWorkItem)
        {
            IList <JsonPatchOperation> jsonPatchOperations = new List <JsonPatchOperation>();

            if (sourceWorkItem.Relations == null)
            {
                return(jsonPatchOperations);
            }

            IList <WorkItemRelation> sourceWorkItemLinkRelations = GetWorkItemLinkRelations(migrationContext, sourceWorkItem.Relations);

            if (sourceWorkItemLinkRelations.Any())
            {
                foreach (WorkItemRelation sourceWorkItemLinkRelation in sourceWorkItemLinkRelations)
                {
                    int linkedSourceId   = ClientHelpers.GetWorkItemIdFromApiEndpoint(sourceWorkItemLinkRelation.Url);
                    int targetWorkItemId = targetWorkItem.Id.Value;
                    int linkedTargetId;

                    if (!migrationContext.SourceToTargetIds.TryGetValue(linkedSourceId, out linkedTargetId))
                    {
                        continue;
                    }

                    string       comment         = MigrationHelpers.GetCommentFromAttributes(sourceWorkItemLinkRelation);
                    WorkItemLink newWorkItemLink = new WorkItemLink(linkedTargetId, sourceWorkItemLinkRelation.Rel, false, false, comment, 0);

                    JsonPatchOperation workItemLinkAddOperation = MigrationHelpers.GetWorkItemLinkAddOperation(migrationContext, newWorkItemLink);
                    jsonPatchOperations.Add(workItemLinkAddOperation);
                }
            }

            return(jsonPatchOperations);
        }
        /// <summary>
        /// Uploads inline image attachment from source to target and returns target inline image AttachmentReference Guid.
        /// </summary>
        /// <param name="inlineImageUrl"></param>
        /// <returns></returns>
        private async Task <string> UploadInlineImageAttachmentFromSourceWorkItemToTarget(IBatchMigrationContext batchContext, string inlineImageUrl, int sourceWorkItemId, int maxAttachmentSize)
        {
            Guid   sourceGuid = MigrationHelpers.GetAttachmentUrlGuid(inlineImageUrl);
            string targetGuid = null;

            if (Guid.Empty.Equals(sourceGuid))
            {
                Logger.LogWarning(LogDestination.File, $"Unexpected format for inline image url {inlineImageUrl} for source work item {sourceWorkItemId}");
                ClientHelpers.AddFailureReasonToWorkItemMigrationState(sourceWorkItemId, FailureReason.InlineImageUrlFormatError, batchContext.WorkItemMigrationState);

                // just return the null guid since there is nothing we can do with an invalid url
                return(null);
            }

            Stream stream = null;

            try
            {
                Logger.LogTrace(LogDestination.File, $"Reading inline image {inlineImageUrl} for source work item {sourceWorkItemId} from the source account");
                stream = await WorkItemTrackingHelpers.GetAttachmentAsync(this.context.SourceClient.WorkItemTrackingHttpClient, sourceGuid);

                Logger.LogTrace(LogDestination.File, $"Completed reading inline image {inlineImageUrl} for source work item {sourceWorkItemId} from the source account");
            }
            catch (Exception e)
            {
                Logger.LogError(LogDestination.File, e, $"Unable to download inline image {inlineImageUrl} for source work item {sourceWorkItemId} from the source account");
                ClientHelpers.AddFailureReasonToWorkItemMigrationState(sourceWorkItemId, FailureReason.InlineImageDownloadError, batchContext.WorkItemMigrationState);

                // just return the null guid since there is nothing we can do if we couldn't download the inline image
                return(null);
            }

            if (stream != null)
            {
                using (MemoryStream memstream = new MemoryStream())
                {
                    bool copiedStream = false;
                    using (stream)
                    {
                        try
                        {
                            Logger.LogTrace(LogDestination.File, $"Downloading inline image {inlineImageUrl} for source work item {sourceWorkItemId} from the source account");
                            await ClientHelpers.CopyStreamAsync(stream, memstream);

                            Logger.LogTrace(LogDestination.File, $"Completed downloading inline image {inlineImageUrl} for source work item {sourceWorkItemId} from the source account");
                            copiedStream = true;
                        }
                        catch (Exception e)
                        {
                            Logger.LogError(LogDestination.File, e, $"Unable to download inline image {inlineImageUrl} for source work item {sourceWorkItemId} from the source account");
                            ClientHelpers.AddFailureReasonToWorkItemMigrationState(sourceWorkItemId, FailureReason.InlineImageDownloadError, batchContext.WorkItemMigrationState);
                        }
                    }

                    if (memstream.Length > maxAttachmentSize)
                    {
                        Logger.LogWarning(LogDestination.File, $"Inline image attachment of source work item with id {sourceWorkItemId} and url {inlineImageUrl} exceeded the maximum attachment size of {maxAttachmentSize} bytes." +
                                          $" Skipping creating the inline image attachment in target account.");
                        return(null);
                    }

                    if (copiedStream)
                    {
                        memstream.Position = 0;
                        //upload the attachment to target
                        try
                        {
                            Logger.LogTrace(LogDestination.File, $"Uploading inline image {inlineImageUrl} for source work item {sourceWorkItemId} from the source account");
                            var aRef = await WorkItemTrackingHelpers.CreateAttachmentChunkedAsync(this.context.TargetClient.WorkItemTrackingHttpClient, this.context.TargetClient.Connection, memstream, this.context.Config.AttachmentUploadChunkSize);

                            targetGuid = aRef.Id.ToString();
                            Logger.LogTrace(LogDestination.File, $"Completed uploading inline image {inlineImageUrl} for source work item {sourceWorkItemId} from the source account");
                        }
                        catch (Exception e)
                        {
                            Logger.LogError(LogDestination.File, e, $"Unable to upload inline image for source work item {sourceWorkItemId} in the target account");
                            ClientHelpers.AddFailureReasonToWorkItemMigrationState(sourceWorkItemId, FailureReason.InlineImageUploadError, batchContext.WorkItemMigrationState);
                        }
                    }
                }
            }

            return(targetGuid);
        }
        public async Task <IEnumerable <JsonPatchOperation> > Process(IMigrationContext migrationContext, IBatchMigrationContext batchContext, WorkItem sourceWorkItem, WorkItem targetWorkItem)
        {
            IList <JsonPatchOperation> jsonPatchOperations = new List <JsonPatchOperation>();

            if (sourceWorkItem.Relations == null)
            {
                return(jsonPatchOperations);
            }

            IEnumerable <WorkItemRelation> sourceAttachmentWorkItemRelations = GetAttachmentWorkItemRelations(sourceWorkItem.Relations);

            if (sourceAttachmentWorkItemRelations.Any())
            {
                foreach (WorkItemRelation sourceAttachmentWorkItemRelation in sourceAttachmentWorkItemRelations)
                {
                    WorkItemRelation targetAttachmentRelation = GetAttachmentIfExistsOnTarget(targetWorkItem, sourceAttachmentWorkItemRelation);

                    if (targetAttachmentRelation != null) // is on target
                    {
                        JsonPatchOperation attachmentAddOperation = MigrationHelpers.GetRelationAddOperation(targetAttachmentRelation);
                        jsonPatchOperations.Add(attachmentAddOperation);
                    }
                    else // is not on target
                    {
                        AttachmentLink attachmentLink = await UploadAttachmentFromSourceRelation(migrationContext, batchContext, sourceWorkItem, sourceAttachmentWorkItemRelation, migrationContext.Config.MaxAttachmentSize);

                        if (attachmentLink != null)
                        {
                            WorkItemRelation newAttachmentWorkItemRelation = new WorkItemRelation();
                            newAttachmentWorkItemRelation.Rel        = sourceAttachmentWorkItemRelation.Rel;
                            newAttachmentWorkItemRelation.Url        = attachmentLink.AttachmentReference.Url;
                            newAttachmentWorkItemRelation.Attributes = new Dictionary <string, object>();
                            newAttachmentWorkItemRelation.Attributes[Constants.RelationAttributeName]         = attachmentLink.FileName;
                            newAttachmentWorkItemRelation.Attributes[Constants.RelationAttributeResourceSize] = attachmentLink.ResourceSize;
                            newAttachmentWorkItemRelation.Attributes[Constants.RelationAttributeComment]      = attachmentLink.Comment;

                            JsonPatchOperation attachmentAddOperation = MigrationHelpers.GetRelationAddOperation(newAttachmentWorkItemRelation);
                            jsonPatchOperations.Add(attachmentAddOperation);
                        }
                    }
                }
            }

            return(jsonPatchOperations);
        }
        public async Task <IEnumerable <JsonPatchOperation> > Process(IMigrationContext migrationContext, IBatchMigrationContext batchContext, WorkItem sourceWorkItem, WorkItem targetWorkItem)
        {
            IList <JsonPatchOperation>     jsonPatchOperations           = new List <JsonPatchOperation>();
            IEnumerable <WorkItemRelation> sourceGitCommitLinksRelations = GetGitLinksRelationsFromWorkItem(sourceWorkItem, Constants.RelationArtifactLink, migrationContext.Config.SourceConnection.Account);

            if (sourceGitCommitLinksRelations.Any())
            {
                foreach (WorkItemRelation sourceGitCommitLinkRelation in sourceGitCommitLinksRelations)
                {
                    string           adjustedUrl = ConvertGitCommitLinkToHyperLink(sourceWorkItem.Id.Value, sourceGitCommitLinkRelation.Url, migrationContext.Config.SourceConnection.Account);
                    WorkItemRelation targetGitCommitHyperlinkRelation = GetGitCommitHyperlinkIfExistsOnTarget(targetWorkItem, adjustedUrl);

                    if (targetGitCommitHyperlinkRelation != null) // is on target
                    {
                        JsonPatchOperation gitCommitHyperlinkAddOperation = MigrationHelpers.GetRelationAddOperation(targetGitCommitHyperlinkRelation);
                        jsonPatchOperations.Add(gitCommitHyperlinkAddOperation);
                    }
                    else // is not on target
                    {
                        string comment = string.Empty;
                        if (sourceGitCommitLinkRelation.Attributes.ContainsKey(Constants.RelationAttributeComment))
                        {
                            comment = $"{sourceGitCommitLinkRelation.Attributes[Constants.RelationAttributeComment]}";
                        }

                        string adjustedComment = $"{Constants.RelationAttributeGitCommitCommentValue}{comment}";

                        WorkItemRelation newGitCommitLinkRelation = new WorkItemRelation();
                        newGitCommitLinkRelation.Rel        = Constants.Hyperlink;
                        newGitCommitLinkRelation.Url        = adjustedUrl;
                        newGitCommitLinkRelation.Attributes = new Dictionary <string, object>();
                        newGitCommitLinkRelation.Attributes[Constants.RelationAttributeComment] = adjustedComment;

                        JsonPatchOperation gitCommitHyperlinkAddOperation = MigrationHelpers.GetRelationAddOperation(newGitCommitLinkRelation);
                        jsonPatchOperations.Add(gitCommitHyperlinkAddOperation);
                    }
                }
            }

            return(jsonPatchOperations);
        }
示例#26
0
        public async Task Process(IBatchMigrationContext batchContext)
        {
            object           identityObject      = null;
            string           identityValue       = null;
            HashSet <string> identitiesToProcess = new HashSet <string>(StringComparer.CurrentCultureIgnoreCase);

            foreach (var sourceWorkItem in batchContext.SourceWorkItems)
            {
                foreach (var field in context.IdentityFields)
                {
                    if (sourceWorkItem.Fields.TryGetValueIgnoringCase(field, out identityObject))
                    {
                        identityValue = (string)identityObject;
                        if (!string.IsNullOrEmpty(identityValue) &&
                            identityValue.Contains("<") && identityValue.Contains(">") && (identityValue.Contains("@")))
                        {
                            // parse out email address from the combo string
                            identityValue = identityValue.Substring(identityValue.LastIndexOf("<") + 1, identityValue.LastIndexOf(">") - identityValue.LastIndexOf("<") - 1);

                            if (!identitiesToProcess.Contains(identityValue) &&
                                !this.context.ValidatedIdentities.Contains(identityValue) &&
                                !this.context.InvalidIdentities.Contains(identityValue))
                            {
                                Logger.LogTrace(LogDestination.File, $"Found identity {identityValue} in batch {batchContext.BatchId} which has not yet been validated for the target account");
                                identitiesToProcess.Add(identityValue);
                            }
                        }
                    }
                }
            }

            Logger.LogTrace(LogDestination.File, $"Adding {identitiesToProcess.Count} identities to the account for batch {batchContext.BatchId}");
            foreach (var identity in identitiesToProcess)
            {
                try
                {
                    var createUserResult = await RetryHelper.RetryAsync(async() =>
                    {
                        return(await graphClient.CreateUserAsync(new GraphUserPrincipalNameCreationContext()
                        {
                            PrincipalName = identity
                        }));
                    }, 5);

                    // using identity from createUserResult since the identity could be in a mangled format that ReadIdentities does not support
                    var identities = await RetryHelper.RetryAsync(async() =>
                    {
                        return(await identityHttpClient.ReadIdentitiesAsync(IdentitySearchFilter.MailAddress, createUserResult.MailAddress));
                    }, 5);

                    if (identities.Count == 0)
                    {
                        Logger.LogWarning(LogDestination.File, $"Unable to add identity {identity} to the target account for batch {batchContext.BatchId}");
                        context.InvalidIdentities.Add(identity);
                    }
                    else
                    {
                        var assignResult = await RetryHelper.RetryAsync(async() =>
                        {
                            return(await licensingHttpClient.AssignAvailableEntitlementAsync(identities[0].Id, dontNotifyUser: true));
                        }, 5);

                        context.ValidatedIdentities.Add(identity);
                    }
                }
                catch (Exception ex)
                {
                    Logger.LogWarning(LogDestination.File, ex, $"Unable to add identity {identity} to the target account for batch {batchContext.BatchId}");
                    context.InvalidIdentities.Add(identity);
                }
            }

            Logger.LogTrace(LogDestination.File, $"Completed adding {identitiesToProcess.Count} identities to the account for batch {batchContext.BatchId}");
        }
示例#27
0
        public async Task <IEnumerable <JsonPatchOperation> > Process(IMigrationContext migrationContext, IBatchMigrationContext batchContext, WorkItem sourceWorkItem, WorkItem targetWorkItem)
        {
            var jsonPatchOperations = new List <JsonPatchOperation>();
            var attachments         = await UploadAttachmentsToTarget(migrationContext, sourceWorkItem);

            foreach (var attachment in attachments)
            {
                JsonPatchOperation revisionHistoryAttachmentAddOperation = MigrationHelpers.GetRevisionHistoryAttachmentAddOperation(attachment, sourceWorkItem.Id.Value);
                jsonPatchOperations.Add(revisionHistoryAttachmentAddOperation);
            }

            return(jsonPatchOperations);
        }
示例#28
0
        public async Task <IEnumerable <JsonPatchOperation> > Process(IMigrationContext migrationContext, IBatchMigrationContext batchContext, WorkItem sourceWorkItem, WorkItem targetWorkItem)
        {
            IList <JsonPatchOperation>     jsonPatchOperations = new List <JsonPatchOperation>();
            IEnumerable <WorkItemRelation> sourceRemoteLinks   = sourceWorkItem.Relations?.Where(r => RelationHelpers.IsRemoteLinkType(migrationContext, r.Rel));

            if (sourceRemoteLinks != null && sourceRemoteLinks.Any())
            {
                foreach (WorkItemRelation sourceRemoteLink in sourceRemoteLinks)
                {
                    string           url = ConvertRemoteLinkToHyperlink(sourceRemoteLink.Url);
                    WorkItemRelation targetRemoteLinkHyperlinkRelation = GetHyperlinkIfExistsOnTarget(targetWorkItem, url);

                    if (targetRemoteLinkHyperlinkRelation != null) // is on target
                    {
                        JsonPatchOperation remoteLinkHyperlinkAddOperation = MigrationHelpers.GetRelationAddOperation(targetRemoteLinkHyperlinkRelation);
                        jsonPatchOperations.Add(remoteLinkHyperlinkAddOperation);
                    }
                    else // is not on target
                    {
                        string comment = string.Empty;
                        if (sourceRemoteLink.Attributes.ContainsKey(Constants.RelationAttributeComment))
                        {
                            comment = $"{sourceRemoteLink.Attributes[Constants.RelationAttributeComment]}";
                        }

                        WorkItemRelation newRemoteLinkHyperlinkRelation = new WorkItemRelation();
                        newRemoteLinkHyperlinkRelation.Rel        = Constants.Hyperlink;
                        newRemoteLinkHyperlinkRelation.Url        = url;
                        newRemoteLinkHyperlinkRelation.Attributes = new Dictionary <string, object>();
                        newRemoteLinkHyperlinkRelation.Attributes[Constants.RelationAttributeComment] = comment;

                        JsonPatchOperation remoteLinkHyperlinkAddOperation = MigrationHelpers.GetRelationAddOperation(newRemoteLinkHyperlinkRelation);
                        jsonPatchOperations.Add(remoteLinkHyperlinkAddOperation);
                    }
                }
            }

            return(jsonPatchOperations);
        }
示例#29
0
 /// <summary>
 /// Populates batchContext.WorkItems
 /// </summary>
 /// <param name="migrationContext"></param>
 /// <param name="workItemIds"></param>
 /// <param name="batchContext"></param>
 /// <param name="expand"></param>
 /// <returns></returns>
 public static async Task ReadSourceWorkItems(IMigrationContext migrationContext, IEnumerable <int> workItemIds, IBatchMigrationContext batchContext, WorkItemExpand?expand = WorkItemExpand.All)
 {
     batchContext.SourceWorkItems = await WorkItemTrackingHelpers.GetWorkItemsAsync(migrationContext.SourceClient.WorkItemTrackingHttpClient, workItemIds, expand : expand);
 }
        private async Task <AttachmentLink> UploadAttachmentFromSourceRelation(IMigrationContext migrationContext, IBatchMigrationContext batchContext, WorkItem sourceWorkItem, WorkItemRelation sourceRelation, int maxAttachmentSize)
        {
            //Attachments are of type Rel = "AttachedFile"
            if (sourceRelation.Rel == Constants.AttachedFile)
            {
                string filename     = null;
                string comment      = null;
                long   resourceSize = 0;
                //get the file name and comment
                if (sourceRelation.Attributes.ContainsKey(Constants.RelationAttributeName))
                {
                    filename = sourceRelation.Attributes[Constants.RelationAttributeName].ToString();
                }
                if (sourceRelation.Attributes.ContainsKey(Constants.RelationAttributeComment))
                {
                    comment = sourceRelation.Attributes[Constants.RelationAttributeComment].ToString();
                }
                //get the guid from the url
                Guid attachmentId;
                if (Guid.TryParse(sourceRelation.Url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Last(), out attachmentId))
                {
                    Stream stream = null;
                    try
                    {
                        Logger.LogTrace(LogDestination.File, $"Reading attachment {filename} for source work item {sourceWorkItem.Id} from the source account");
                        stream = await WorkItemTrackingHelpers.GetAttachmentAsync(migrationContext.SourceClient.WorkItemTrackingHttpClient, attachmentId);

                        Logger.LogTrace(LogDestination.File, $"Completed reading attachment {filename} for source work item {sourceWorkItem.Id} from the source account");
                    }
                    catch (Exception e)
                    {
                        Logger.LogError(LogDestination.File, e, $"Unable to download attachment {filename} for source work item {sourceWorkItem.Id} from the source account");
                        ClientHelpers.AddFailureReasonToWorkItemMigrationState(sourceWorkItem.Id.Value, FailureReason.AttachmentDownloadError, batchContext.WorkItemMigrationState);
                        return(null);
                    }

                    AttachmentReference aRef = null;
                    using (MemoryStream memstream = new MemoryStream())
                    {
                        using (stream)
                        {
                            try
                            {
                                Logger.LogTrace(LogDestination.File, $"Downloading attachment {filename} for source work item {sourceWorkItem.Id} from the source account");
                                await ClientHelpers.CopyStreamAsync(stream, memstream);

                                Logger.LogTrace(LogDestination.File, $"Completed downloading attachment {filename} for source work item {sourceWorkItem.Id} from the source account");
                            }
                            catch (Exception e)
                            {
                                Logger.LogError(LogDestination.File, e, $"Unable to read downloaded attachment {filename} for source work item {sourceWorkItem.Id} from the source account");
                                ClientHelpers.AddFailureReasonToWorkItemMigrationState(sourceWorkItem.Id.Value, FailureReason.AttachmentDownloadError, batchContext.WorkItemMigrationState);
                                return(null);
                            }
                        }

                        resourceSize = memstream.Length;
                        if (resourceSize > maxAttachmentSize)
                        {
                            Logger.LogWarning(LogDestination.File, $"Attachment of source work item with id {sourceWorkItem.Id} and url {sourceRelation.Url} exceeded the maximum attachment size of {maxAttachmentSize} bytes." +
                                              $" Skipping creating the attachment in target account.");
                            return(null);
                        }
                        memstream.Position = 0;
                        //upload the attachment to the target
                        try
                        {
                            Logger.LogTrace(LogDestination.File, $"Uploading attachment {filename} of {resourceSize} bytes for source work item {sourceWorkItem.Id} from the source account");
                            aRef = await WorkItemTrackingHelpers.CreateAttachmentChunkedAsync(migrationContext.TargetClient.WorkItemTrackingHttpClient, migrationContext.TargetClient.Connection, memstream, migrationContext.Config.AttachmentUploadChunkSize);

                            Logger.LogTrace(LogDestination.File, $"Completed uploading attachment {filename} for source work item {sourceWorkItem.Id} from the source account");
                        }
                        catch (Exception e)
                        {
                            Logger.LogError(LogDestination.File, e, $"Unable to upload attachment {filename} for source work item {sourceWorkItem.Id} to the target account");
                            ClientHelpers.AddFailureReasonToWorkItemMigrationState(sourceWorkItem.Id.Value, FailureReason.AttachmentUploadError, batchContext.WorkItemMigrationState);
                        }
                    }
                    if (aRef != null)
                    {
                        return(new AttachmentLink(filename, aRef, resourceSize, comment));
                    }
                }
                else
                {
                    Logger.LogError(LogDestination.File, $"Attachment link is incorrect for {sourceWorkItem.Id} {sourceRelation.Url}. Skipping creating the attachment in target account.");
                    ClientHelpers.AddFailureReasonToWorkItemMigrationState(sourceWorkItem.Id.Value, FailureReason.AttachmentUploadError, batchContext.WorkItemMigrationState);
                }
            }

            return(null);
        }