Esempio n. 1
0
        /// <summary>
        /// Builds a list of strings where each string should be added as a comment to the GitHub issue.
        /// </summary>
        public static IEnumerable <string> GetFormattedComments(WorkItemDetails workItemDetails)
        {
            ArgValidate.IsNotNull(workItemDetails, nameof(workItemDetails));

            var stringBuilder = new StringBuilder();

            // Format and return all work item comments.
            foreach (WorkItemComment comment in workItemDetails.Comments.EmptyIfNull())
            {
                string postedBy = comment.PostedBy;
                if (string.IsNullOrEmpty(postedBy))
                {
                    postedBy = CodePlexStrings.UnknownUser;
                }

                string commentHeading = string.Format(Resources.CommentPostedByPersonXOnDateY, postedBy, comment.PostedDate.ToString("d"));

                stringBuilder
                .AppendLine(commentHeading)
                .AppendLine(comment.Message)
                .AppendLine();

                yield return(stringBuilder.ToString());

                stringBuilder.Clear();
            }

            // Format closing comment if any is specified.
            string closingComment = GetFormattedClosingComment(workItemDetails);

            if (!string.IsNullOrEmpty(closingComment))
            {
                yield return(closingComment);
            }
        }
 private async Task AddCommentsAsync(int issueNumber, WorkItemDetails workItemDetails)
 {
     foreach (string comment in TextUtilities.GetFormattedComments(workItemDetails))
     {
         await issues.Comment.Create(owner : repoOwner, name : repo, number : issueNumber, newComment : comment);
     }
 }
Esempio n. 3
0
        private static string GetFormattedClosingComment(WorkItemDetails workItemDetails)
        {
            string closingComment = workItemDetails.WorkItem.ClosedComment;
            string closedBy       = workItemDetails.WorkItem.ClosedBy;
            string reasonClosed   = workItemDetails.WorkItem.ReasonClosed?.Name;

            // TODO: Produce a different comment if closedBy != null.
            if (!string.IsNullOrEmpty(closingComment) &&
                !string.IsNullOrEmpty(closedBy) &&
                !string.IsNullOrEmpty(reasonClosed))
            {
                string closingCommentTitle = string.Format(Resources.IssueClosedByXWithComment, MarkdownFormatter.Italic(closedBy));

                var stringBuilder =
                    new StringBuilder()
                    .AppendLine(MarkdownFormatter.Bold(closingCommentTitle))
                    .AppendLine(closingComment);

                if (!string.IsNullOrEmpty(reasonClosed) && !StringComparer.Equals(reasonClosed, CodePlexStrings.Unassigned))
                {
                    stringBuilder
                    .AppendLine()
                    .AppendLine(MarkdownFormatter.Bold(Resources.ReasonClosed))
                    .AppendLine(reasonClosed);
                }

                return(stringBuilder.ToString());
            }

            return(string.Empty);
        }
Esempio n. 4
0
        /// <summary>
        /// Writes some of the work item information to the console.
        /// </summary>
        public Task WriteWorkItemAsync(WorkItemDetails workItemDetails)
        {
            ArgValidate.IsNotNull(workItemDetails, nameof(workItemDetails));
            ArgValidate.IsNotNull(workItemDetails.WorkItem, nameof(workItemDetails.WorkItem));

            string output = $"{workItemDetails.WorkItem.Id}{Environment.NewLine}{workItemDetails.WorkItem.Summary}{Environment.NewLine}{workItemDetails.WorkItem.Description}{Environment.NewLine}{Environment.NewLine}";

            Console.WriteLine(output);
            return(Task.FromResult(false));
        }
        /// <summary>
        /// Searches for issue in GitHub and updates it.
        /// </summary>
        /// <remarks>
        /// This is only expected to happen if the initial write failed on a previous run.
        /// </remarks>
        public Task UpdateWorkItemAsync(WorkItemDetails workItemDetails)
        {
            ArgValidate.IsNotNull(workItemDetails, nameof(workItemDetails));
            ArgValidate.IsNotNull(workItemDetails.WorkItem, nameof(workItemDetails.WorkItem));

            return(InvokeAsync(
                       async() =>
            {
                Issue issue = await GetCorrespondingIssueAsync(workItemDetails.WorkItem.Id);
                await UpdateIssueAsync(issue, workItemDetails);
                return true;        // This return statement is only here because we're expected to return Task<T>.
            }));
        }
        /// <summary>
        /// Writes <paramref name="workItemDetails"/> as a new issue to GitHub.
        /// </summary>
        public Task WriteWorkItemAsync(WorkItemDetails workItemDetails)
        {
            ArgValidate.IsNotNull(workItemDetails, nameof(workItemDetails));
            ArgValidate.IsNotNull(workItemDetails.WorkItem, nameof(workItemDetails.WorkItem));

            return(InvokeAsync(
                       async() =>
            {
                Issue createdIssue = await CreateNewIssueAsync(workItemDetails.WorkItem, workItemDetails.FileAttachments);
                await SetIssueDetailsAsync(createdIssue, workItemDetails);
                return true;        // This return statement is only here because we're expected to return Task<T>.
            }));
        }
        private async Task UpdateIssueAsync(Issue issue, WorkItemDetails workItemDetails)
        {
            WorkItem workItem = workItemDetails.WorkItem;

            IssueUpdate issueUpdate = issue.ToUpdate();

            issueUpdate.Title = workItem.Summary;
            issueUpdate.Body  = TextUtilities.GetFormattedWorkItemBody(workItem, workItemDetails.FileAttachments);

            SetNewIssueLabels(issueUpdate.Labels, workItem);

            issue = await issues.Update(owner : repoOwner, name : repo, number : issue.Number, issueUpdate : issueUpdate);

            await DeleteAllCommentsAsync(issue.Number);
            await SetIssueDetailsAsync(issue, workItemDetails);
        }
        /// <summary>
        /// Gets the details about an individual work item.
        /// </summary>
        public async Task <WorkItemDetails> GetWorkItemAsync(WorkItemSummary workItem)
        {
            ArgValidate.IsNotNull(workItem, nameof(workItem));

            WorkItemDetails workItemToReturn = null;
            string          detailedUrl      = string.Format(IssuesDetailsUrlTemplate, project, workItem.Id);

            try
            {
                string workItemJson = await httpClient.DownloadStringAsync(detailedUrl);

                workItemToReturn = JsonConvert.DeserializeObject <WorkItemDetails>(workItemJson);
            }
            catch (JsonReaderException ex)
            {
                // If the object coming back from CodePlex cannot be parsed, something went wrong
                // on network so indicate to the calling method this could be retried.
                throw new HttpRequestFailedException(ex.Message, ex);
            }

            return(workItemToReturn);
        }
 private async Task SetIssueDetailsAsync(Issue issue, WorkItemDetails workItemDetails)
 {
     await AddCommentsAsync(issue.Number, workItemDetails);
     await UpdateLabelsAndStateAsync(issue, closeIssue : workItemDetails.WorkItem.IsClosed());
 }
Esempio n. 10
0
 /// <summary>
 /// Writes some of the work item information to the console
 /// </summary>
 /// <remarks>
 /// In this case because the writer is dumping the data to console, there is no way to update the work item so we just dump it to console.
 /// </remarks>
 public Task UpdateWorkItemAsync(WorkItemDetails value) => WriteWorkItemAsync(value);
Esempio n. 11
0
        /// <summary>
        /// Migrates all work items from <paramref name="source"/> to <paramref name="destination"/>.
        /// </summary>
        public static async Task MigrateAsync(
            IWorkItemSource source, IWorkItemDestination destination, MigrationSettings settings, ILogger logger)
        {
            ArgValidate.IsNotNull(source, nameof(source));
            ArgValidate.IsNotNull(destination, nameof(destination));
            ArgValidate.IsNotNull(logger, nameof(logger));
            ArgValidate.IsNotNull(settings, nameof(settings));

            logger.LogMessage(LogLevel.Info, Resources.BeginMigrationMessage);

            try
            {
                // Retrive all work items that have already been migrated.
                logger.LogMessage(LogLevel.Info, Resources.LookupMigratedWorkItemsMessage);
                IReadOnlyList <MigratedWorkItem> migratedWorkItems = await destination.GetMigratedWorkItemsAsync();

                IDictionary <int, MigrationState> migratedWorkItemState = GetWorkItemDictionary(migratedWorkItems);

                // Update the skip list to include any work items the user indicated we should not retrieve.
                AddSkipItemsToMigrateState(settings.WorkItemsToSkip, migratedWorkItemState);

                // Get the list of potential work items to migrate.
                logger.LogMessage(LogLevel.Info, Resources.LookupWorkItemsToMigrate);
                IReadOnlyList <WorkItemSummary> notMigratedWorkItems =
                    await source.GetWorkItemsAsync(id => GetMigrationState(migratedWorkItemState, id) != MigrationState.Migrated);

                // Get the actual list of work items to migrate taking into account the limit specified in migration settings.
                int countToMigrate = settings.MaxItemsToMigrate == -1 ? notMigratedWorkItems.Count : settings.MaxItemsToMigrate;
                logger.LogMessage(LogLevel.Info, Resources.StartingMigrationOfXWorkItems, countToMigrate);

                logger.LogMessage(LogLevel.Warning, Resources.ProgressOfMigrationWillBeSlow);

                IEnumerable <WorkItemSummary> workItemsToMigrate = notMigratedWorkItems.Take(countToMigrate);

                int migratedWorkItemCount = 0;
                // Download the details from CodePlex and push it to the destination.
                foreach (WorkItemSummary workItemSummary in workItemsToMigrate)
                {
                    logger.LogMessage(LogLevel.Trace, Resources.LookupIndividualWorkItem, workItemSummary.Id);
                    WorkItemDetails item = null;
                    await RetryAsync(
                        async() => item = await source.GetWorkItemAsync(workItemSummary), settings.MaxRetryCount, settings.RetryDelay);

                    if (GetMigrationState(migratedWorkItemState, workItemSummary.Id) == MigrationState.PartiallyMigrated)
                    {
                        logger.LogMessage(LogLevel.Trace, Resources.UpdatingWorkItem, workItemSummary.Id);
                        await RetryAsync(() => destination.UpdateWorkItemAsync(item), settings.MaxRetryCount, settings.RetryDelay);
                    }
                    else
                    {
                        logger.LogMessage(LogLevel.Trace, Resources.AddingWorkItem, workItemSummary.Id);
                        await RetryAsync(() => destination.WriteWorkItemAsync(item), settings.MaxRetryCount, settings.RetryDelay);
                    }

                    logger.LogMessage(LogLevel.Info, Resources.SuccessfullyMigratedWorkItemIdXTitleY, workItemSummary.Id, ++migratedWorkItemCount,
                                      countToMigrate, workItemSummary.Title);
                }
            }
            catch (Exception ex)
            {
                logger.LogMessage(LogLevel.Error, Resources.LogExceptionMessage, ex.Message);
                throw;
            }

            logger.LogMessage(LogLevel.Info, Resources.MigrationCompletedSuccessfully);
        }