void LoadDescriptionFromCommits()
        {
            SourceBranch = gitService.GetBranch(activeLocalRepo);

            var uniqueCommits = this.WhenAnyValue(
                x => x.SourceBranch,
                x => x.TargetBranch)
                                .Where(x => x.Item1 != null && x.Item2 != null)
                                .Select(branches =>
            {
                var baseBranch    = branches.Item1.Name;
                var compareBranch = branches.Item2.Name;

                // We only need to get max two commits for what we're trying to achieve here.
                // If there's no commits we want to block creation of the PR, if there's one commits
                // we wan't to use its commit message as the PR title/body and finally if there's more
                // than one we'll use the branch name for the title.
                return(service.GetMessagesForUniqueCommits(activeLocalRepo, baseBranch, compareBranch, maxCommits: 2)
                       .Catch <IReadOnlyList <CommitMessage>, Exception>(ex =>
                {
                    log.Warning(ex, "Could not load unique commits");
                    return Observable.Empty <IReadOnlyList <CommitMessage> >();
                }));
            })
                                .Switch()
                                .ObserveOn(RxApp.MainThreadScheduler)
                                .Replay(1)
                                .RefCount();

            Observable.CombineLatest(
                this.WhenAnyValue(x => x.SourceBranch),
                uniqueCommits,
                service.GetPullRequestTemplate(activeLocalRepo).DefaultIfEmpty(string.Empty),
                (compare, commits, template) => new { compare, commits, template })
            .Subscribe(x =>
            {
                var prTitle       = string.Empty;
                var prDescription = string.Empty;

                if (x.commits.Count == 1)
                {
                    prTitle       = x.commits[0].Summary;
                    prDescription = x.commits[0].Details;
                }
                else
                {
                    prTitle = x.compare.Name.Humanize();
                }

                if (!string.IsNullOrWhiteSpace(x.template))
                {
                    if (!string.IsNullOrEmpty(prDescription))
                    {
                        prDescription += "\n\n";
                    }
                    prDescription += x.template;
                }

                PRTitle     = prTitle;
                Description = prDescription;
            });
        }
        public PullRequestCreationViewModel(IRepositoryHost repositoryHost, ISimpleRepositoryModel activeRepo,
                                            IPullRequestService service, INotificationService notifications)
        {
            Extensions.Guard.ArgumentNotNull(repositoryHost, nameof(repositoryHost));
            Extensions.Guard.ArgumentNotNull(activeRepo, nameof(activeRepo));
            Extensions.Guard.ArgumentNotNull(service, nameof(service));
            Extensions.Guard.ArgumentNotNull(notifications, nameof(notifications));

            this.repositoryHost = repositoryHost;

            var obs = repositoryHost.ApiClient.GetRepository(activeRepo.Owner, activeRepo.Name)
                      .Select(r => new RepositoryModel(r))
                      .PublishLast();

            disposables.Add(obs.Connect());
            githubObs = obs;

            githubRepository = githubObs.ToProperty(this, x => x.GitHubRepository);

            this.WhenAnyValue(x => x.GitHubRepository)
            .WhereNotNull()
            .Subscribe(r =>
            {
                TargetBranch = r.IsFork ? r.Parent.DefaultBranch : r.DefaultBranch;
            });

            SourceBranch = activeRepo.CurrentBranch;
            service.GetPullRequestTemplate(activeRepo)
            .Subscribe(x => Description = x ?? String.Empty, () => Description = Description ?? String.Empty);

            this.WhenAnyValue(x => x.Branches)
            .WhereNotNull()
            .Where(_ => TargetBranch != null)
            .Subscribe(x =>
            {
                if (!x.Any(t => t.Equals(TargetBranch)))
                {
                    TargetBranch = GitHubRepository.IsFork ? GitHubRepository.Parent.DefaultBranch : GitHubRepository.DefaultBranch;
                }
            });

            SetupValidators();

            var whenAnyValidationResultChanges = this.WhenAny(
                x => x.TitleValidator.ValidationResult,
                x => x.BranchValidator.ValidationResult,
                x => x.IsBusy,
                (x, y, z) => (x.Value?.IsValid ?? false) && (y.Value?.IsValid ?? false) && !z.Value);

            this.WhenAny(x => x.BranchValidator.ValidationResult, x => x.GetValue())
            .WhereNotNull()
            .Where(x => !x.IsValid && x.DisplayValidationError)
            .Subscribe(x => notifications.ShowError(BranchValidator.ValidationResult.Message));

            createPullRequest = ReactiveCommand.CreateAsyncObservable(whenAnyValidationResultChanges,
                                                                      _ => service
                                                                      .CreatePullRequest(repositoryHost, activeRepo, TargetBranch.Repository, SourceBranch, TargetBranch, PRTitle, Description ?? String.Empty)
                                                                      .Catch <IPullRequestModel, Exception>(ex =>
            {
                log.Error(ex);

                //TODO:Will need a uniform solution to HTTP exception message handling
                var apiException = ex as ApiValidationException;
                var error        = apiException?.ApiError?.Errors?.FirstOrDefault();
                notifications.ShowError(error?.Message ?? ex.Message);
                return(Observable.Empty <IPullRequestModel>());
            }))

                                .OnExecuteCompleted(pr =>
            {
                notifications.ShowMessage(String.Format(CultureInfo.CurrentCulture, Resources.PRCreatedUpstream, TargetBranch.Id,
                                                        TargetBranch.Repository.CloneUrl.ToRepositoryUrl().Append("pull/" + pr.Number)));
            });

            isExecuting = CreatePullRequest.IsExecuting.ToProperty(this, x => x.IsExecuting);

            this.WhenAnyValue(x => x.Initialized, x => x.GitHubRepository, x => x.Description, x => x.IsExecuting)
            .Select(x => !(x.Item1 && x.Item2 != null && x.Item3 != null && !x.Item4))
            .Subscribe(x => IsBusy = x);
        }
Example #3
0
        public async Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection)
        {
            modelService = await modelServiceFactory.CreateAsync(connection);

            activeLocalRepo = repository;
            SourceBranch    = repository.CurrentBranch;

            var obs = modelService.ApiClient.GetRepository(repository.Owner, repository.Name)
                      .Select(r => new RemoteRepositoryModel(r))
                      .PublishLast();

            disposables.Add(obs.Connect());
            var githubObs = obs;

            githubRepository = githubObs.ToProperty(this, x => x.GitHubRepository);

            this.WhenAnyValue(x => x.GitHubRepository)
            .WhereNotNull()
            .Subscribe(r =>
            {
                TargetBranch = r.IsFork ? r.Parent.DefaultBranch : r.DefaultBranch;
            });

            githubObs.SelectMany(r =>
            {
                var b = Observable.Empty <IBranch>();
                if (r.IsFork)
                {
                    b = modelService.GetBranches(r.Parent).Select(x =>
                    {
                        return(x);
                    });
                }
                return(b.Concat(modelService.GetBranches(r)));
            })
            .ToList()
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(x =>
            {
                Branches    = x.ToList();
                Initialized = true;
            });

            SourceBranch = activeLocalRepo.CurrentBranch;

            var uniqueCommits = this.WhenAnyValue(
                x => x.SourceBranch,
                x => x.TargetBranch)
                                .Where(x => x.Item1 != null && x.Item2 != null)
                                .Select(branches =>
            {
                var baseBranch    = branches.Item1.Name;
                var compareBranch = branches.Item2.Name;

                // We only need to get max two commits for what we're trying to achieve here.
                // If there's no commits we want to block creation of the PR, if there's one commits
                // we wan't to use its commit message as the PR title/body and finally if there's more
                // than one we'll use the branch name for the title.
                return(service.GetMessagesForUniqueCommits(activeLocalRepo, baseBranch, compareBranch, maxCommits: 2)
                       .Catch <IReadOnlyList <CommitMessage>, Exception>(ex =>
                {
                    log.Warning(ex, "Could not load unique commits");
                    return Observable.Empty <IReadOnlyList <CommitMessage> >();
                }));
            })
                                .Switch()
                                .ObserveOn(RxApp.MainThreadScheduler)
                                .Replay(1)
                                .RefCount();

            Observable.CombineLatest(
                this.WhenAnyValue(x => x.SourceBranch),
                uniqueCommits,
                service.GetPullRequestTemplate(repository).DefaultIfEmpty(string.Empty),
                (compare, commits, template) => new { compare, commits, template })
            .Subscribe(x =>
            {
                var prTitle       = string.Empty;
                var prDescription = string.Empty;

                if (x.commits.Count == 1)
                {
                    prTitle       = x.commits[0].Summary;
                    prDescription = x.commits[0].Details;
                }
                else
                {
                    prTitle = x.compare.Name.Humanize();
                }

                if (!string.IsNullOrWhiteSpace(x.template))
                {
                    if (!string.IsNullOrEmpty(prDescription))
                    {
                        prDescription += "\n\n";
                    }
                    prDescription += x.template;
                }

                PRTitle     = prTitle;
                Description = prDescription;
            });

            Initialized = true;
        }
        public PullRequestCreationViewModel(
            IConnection connection,
            IModelServiceFactory modelServiceFactory,
            ILocalRepositoryModel activeRepo,
            IPullRequestService service,
            INotificationService notifications)
        {
            Guard.ArgumentNotNull(connection, nameof(connection));
            Guard.ArgumentNotNull(modelServiceFactory, nameof(modelServiceFactory));
            Guard.ArgumentNotNull(activeRepo, nameof(activeRepo));
            Guard.ArgumentNotNull(service, nameof(service));
            Guard.ArgumentNotNull(notifications, nameof(notifications));

            activeLocalRepo = activeRepo;
            modelService    = modelServiceFactory.CreateBlocking(connection);

            var obs = modelService.ApiClient.GetRepository(activeRepo.Owner, activeRepo.Name)
                      .Select(r => new RemoteRepositoryModel(r))
                      .PublishLast();

            disposables.Add(obs.Connect());
            githubObs = obs;

            githubRepository = githubObs.ToProperty(this, x => x.GitHubRepository);

            this.WhenAnyValue(x => x.GitHubRepository)
            .WhereNotNull()
            .Subscribe(r =>
            {
                TargetBranch = r.IsFork ? r.Parent.DefaultBranch : r.DefaultBranch;
            });

            SourceBranch = activeRepo.CurrentBranch;

            this.WhenAnyValue(x => x.Branches)
            .WhereNotNull()
            .Where(_ => TargetBranch != null)
            .Subscribe(x =>
            {
                if (!x.Any(t => t.Equals(TargetBranch)))
                {
                    TargetBranch = GitHubRepository.IsFork ? GitHubRepository.Parent.DefaultBranch : GitHubRepository.DefaultBranch;
                }
            });

            SetupValidators();

            var uniqueCommits = this.WhenAnyValue(
                x => x.SourceBranch,
                x => x.TargetBranch)
                                .Where(x => x.Item1 != null && x.Item2 != null)
                                .Select(branches =>
            {
                var baseBranch    = branches.Item1.Name;
                var compareBranch = branches.Item2.Name;

                // We only need to get max two commits for what we're trying to achieve here.
                // If there's no commits we want to block creation of the PR, if there's one commits
                // we wan't to use its commit message as the PR title/body and finally if there's more
                // than one we'll use the branch name for the title.
                return(service.GetMessagesForUniqueCommits(activeRepo, baseBranch, compareBranch, maxCommits: 2)
                       .Catch <IReadOnlyList <CommitMessage>, Exception>(ex =>
                {
                    log.Warning(ex, "Could not load unique commits");
                    return Observable.Empty <IReadOnlyList <CommitMessage> >();
                }));
            })
                                .Switch()
                                .ObserveOn(RxApp.MainThreadScheduler)
                                .Replay(1)
                                .RefCount();

            var whenAnyValidationResultChanges = this.WhenAny(
                x => x.TitleValidator.ValidationResult,
                x => x.BranchValidator.ValidationResult,
                x => x.IsBusy,
                (x, y, z) => (x.Value?.IsValid ?? false) && (y.Value?.IsValid ?? false) && !z.Value);

            this.WhenAny(x => x.BranchValidator.ValidationResult, x => x.GetValue())
            .WhereNotNull()
            .Where(x => !x.IsValid && x.DisplayValidationError)
            .Subscribe(x => notifications.ShowError(BranchValidator.ValidationResult.Message));

            CreatePullRequest = ReactiveCommand.CreateAsyncObservable(whenAnyValidationResultChanges,
                                                                      _ => service
                                                                      .CreatePullRequest(modelService, activeRepo, TargetBranch.Repository, SourceBranch, TargetBranch, PRTitle, Description ?? String.Empty)
                                                                      .Catch <IPullRequestModel, Exception>(ex =>
            {
                log.Error(ex, "Error creating pull request");

                //TODO:Will need a uniform solution to HTTP exception message handling
                var apiException = ex as ApiValidationException;
                var error        = apiException?.ApiError?.Errors?.FirstOrDefault();
                notifications.ShowError(error?.Message ?? ex.Message);
                return(Observable.Empty <IPullRequestModel>());
            }))
                                .OnExecuteCompleted(pr =>
            {
                notifications.ShowMessage(String.Format(CultureInfo.CurrentCulture, Resources.PRCreatedUpstream, SourceBranch.DisplayName, TargetBranch.Repository.Owner + "/" + TargetBranch.Repository.Name + "#" + pr.Number,
                                                        TargetBranch.Repository.CloneUrl.ToRepositoryUrl().Append("pull/" + pr.Number)));
            });

            isExecuting = CreatePullRequest.IsExecuting.ToProperty(this, x => x.IsExecuting);

            this.WhenAnyValue(x => x.Initialized, x => x.GitHubRepository, x => x.Description, x => x.IsExecuting)
            .Select(x => !(x.Item1 && x.Item2 != null && x.Item3 != null && !x.Item4))
            .Subscribe(x => IsBusy = x);

            Observable.CombineLatest(
                this.WhenAnyValue(x => x.SourceBranch),
                uniqueCommits,
                service.GetPullRequestTemplate(activeRepo).DefaultIfEmpty(string.Empty),
                (compare, commits, template) => new { compare, commits, template })
            .Subscribe(x =>
            {
                var prTitle       = string.Empty;
                var prDescription = string.Empty;

                if (x.commits.Count == 1)
                {
                    prTitle       = x.commits[0].Summary;
                    prDescription = x.commits[0].Details;
                }
                else
                {
                    prTitle = x.compare.Name.Humanize();
                }

                if (!string.IsNullOrWhiteSpace(x.template))
                {
                    if (!string.IsNullOrEmpty(prDescription))
                    {
                        prDescription += "\n\n";
                    }
                    prDescription += x.template;
                }

                PRTitle     = prTitle;
                Description = prDescription;
            });
        }
        public PullRequestCreationViewModel(IRepositoryHost repositoryHost, ILocalRepositoryModel activeRepo,
            IPullRequestService service, INotificationService notifications)
        {
            Extensions.Guard.ArgumentNotNull(repositoryHost, nameof(repositoryHost));
            Extensions.Guard.ArgumentNotNull(activeRepo, nameof(activeRepo));
            Extensions.Guard.ArgumentNotNull(service, nameof(service));
            Extensions.Guard.ArgumentNotNull(notifications, nameof(notifications));

            this.repositoryHost = repositoryHost;

            var obs = repositoryHost.ApiClient.GetRepository(activeRepo.Owner, activeRepo.Name)
                .Select(r => new RemoteRepositoryModel(r))
                .PublishLast();
            disposables.Add(obs.Connect());
            githubObs = obs;

            githubRepository = githubObs.ToProperty(this, x => x.GitHubRepository);

            this.WhenAnyValue(x => x.GitHubRepository)
                .WhereNotNull()
                .Subscribe(r =>
            {
                TargetBranch = r.IsFork ? r.Parent.DefaultBranch : r.DefaultBranch;
            });

            SourceBranch = activeRepo.CurrentBranch;
            service.GetPullRequestTemplate(activeRepo)
                .Subscribe(x => Description = x ?? String.Empty, () => Description = Description ?? String.Empty);

            this.WhenAnyValue(x => x.Branches)
                .WhereNotNull()
                .Where(_ => TargetBranch != null)
                .Subscribe(x =>
                {
                    if (!x.Any(t => t.Equals(TargetBranch)))
                        TargetBranch = GitHubRepository.IsFork ? GitHubRepository.Parent.DefaultBranch : GitHubRepository.DefaultBranch;
                });

            SetupValidators();

            var whenAnyValidationResultChanges = this.WhenAny(
                x => x.TitleValidator.ValidationResult,
                x => x.BranchValidator.ValidationResult,
                x => x.IsBusy,
                (x, y, z) => (x.Value?.IsValid ?? false) && (y.Value?.IsValid ?? false) && !z.Value);

            this.WhenAny(x => x.BranchValidator.ValidationResult, x => x.GetValue())
                .WhereNotNull()
                .Where(x => !x.IsValid && x.DisplayValidationError)
                .Subscribe(x => notifications.ShowError(BranchValidator.ValidationResult.Message));

            createPullRequest = ReactiveCommand.CreateAsyncObservable(whenAnyValidationResultChanges,
                _ => service
                    .CreatePullRequest(repositoryHost, activeRepo, TargetBranch.Repository, SourceBranch, TargetBranch, PRTitle, Description ?? String.Empty)
                    .Catch<IPullRequestModel, Exception>(ex =>
                    {
                        log.Error(ex);

                        //TODO:Will need a uniform solution to HTTP exception message handling
                        var apiException = ex as ApiValidationException;
                        var error = apiException?.ApiError?.Errors?.FirstOrDefault();
                        notifications.ShowError(error?.Message ?? ex.Message);
                        return Observable.Empty<IPullRequestModel>();
                    }))
            .OnExecuteCompleted(pr =>
            {
                notifications.ShowMessage(String.Format(CultureInfo.CurrentCulture, Resources.PRCreatedUpstream, SourceBranch.DisplayName, TargetBranch.Repository.Owner + "/" + TargetBranch.Repository.Name + "#" + pr.Number,
                    TargetBranch.Repository.CloneUrl.ToRepositoryUrl().Append("pull/" + pr.Number)));
            });

            isExecuting = CreatePullRequest.IsExecuting.ToProperty(this, x => x.IsExecuting);

            this.WhenAnyValue(x => x.Initialized, x => x.GitHubRepository, x => x.Description, x => x.IsExecuting)
                .Select(x => !(x.Item1 && x.Item2 != null && x.Item3 != null && !x.Item4))
                .Subscribe(x => IsBusy = x);
        }