Exemple #1
0
        public async Task <IActionResult> UpdateSubscription(Guid id, [FromBody] SubscriptionUpdate update)
        {
            Data.Models.Subscription subscription = await _context.Subscriptions.Where(sub => sub.Id == id)
                                                    .FirstOrDefaultAsync();

            if (subscription == null)
            {
                return(NotFound());
            }

            var doUpdate = false;

            if (!string.IsNullOrEmpty(update.SourceRepository))
            {
                subscription.SourceRepository = update.SourceRepository;
                doUpdate = true;
            }

            if (update.Policy != null)
            {
                subscription.PolicyObject = update.Policy.ToDb();
                doUpdate = true;
            }

            if (!string.IsNullOrEmpty(update.ChannelName))
            {
                Channel channel = await _context.Channels.Where(c => c.Name == update.ChannelName)
                                  .FirstOrDefaultAsync();

                if (channel == null)
                {
                    return(BadRequest(
                               new ApiError(
                                   "The request is invalid",
                                   new[] { $"The channel '{update.ChannelName}' could not be found." })));
                }

                subscription.Channel = channel;
                doUpdate             = true;
            }

            if (update.Enabled.HasValue)
            {
                subscription.Enabled = update.Enabled.Value;
                doUpdate             = true;
            }

            if (doUpdate)
            {
                _context.Subscriptions.Update(subscription);
                await _context.SaveChangesAsync();
            }


            return(Ok(new Subscription(subscription)));
        }
Exemple #2
0
        public async Task <IActionResult> Create([FromBody] SubscriptionData subscription)
        {
            Channel channel = await _context.Channels.Where(c => c.Name == subscription.ChannelName)
                              .FirstOrDefaultAsync();

            if (channel == null)
            {
                return(BadRequest(
                           new ApiError(
                               "the request is invalid",
                               new[] { $"The channel '{subscription.ChannelName}' could not be found." })));
            }

            Repository repo = await _context.Repositories.FindAsync(subscription.TargetRepository);

            if (subscription.TargetRepository.Contains("github.com"))
            {
                // If we have no repository information or an invalid installation id
                // then we will fail when trying to update things, so we fail early.
                if (repo == null || repo.InstallationId <= 0)
                {
                    return(BadRequest(
                               new ApiError(
                                   "the request is invalid",
                                   new[]
                    {
                        $"The repository '{subscription.TargetRepository}' does not have an associated github installation. " +
                        "The Maestro github application must be installed by the repository's owner and given access to the repository."
                    })));
                }
            }
            // In the case of a dev.azure.com repository, we don't have an app installation,
            // but we should add an entry in the repositories table, as this is required when
            // adding a new subscription policy.
            // NOTE:
            // There is a good chance here that we will need to also handle <account>.visualstudio.com
            // but leaving it out for now as it would be preferred to use the new format
            else if (subscription.TargetRepository.Contains("dev.azure.com"))
            {
                if (repo == null)
                {
                    _context.Repositories.Add(
                        new Data.Models.Repository
                    {
                        RepositoryName = subscription.TargetRepository,
                        InstallationId = default
                    });
Exemple #3
0
        public async Task <IActionResult> Create([FromBody] SubscriptionData subscription)
        {
            Channel channel = await _context.Channels.Where(c => c.Name == subscription.ChannelName)
                              .FirstOrDefaultAsync();

            if (channel == null)
            {
                return(BadRequest(
                           new ApiError(
                               "the request is invalid",
                               new[] { $"The channel '{subscription.ChannelName}' could not be found." })));
            }

            if (subscription.TargetRepository.Contains("github.com"))
            {
                // If we have no repository information or an invalid installation id
                // then we will fail when trying to update things, so we fail early.
                Repository repo = await _context.Repositories.FindAsync(subscription.TargetRepository);

                if (repo == null || repo.InstallationId <= 0)
                {
                    return(BadRequest(
                               new ApiError(
                                   "the request is invalid",
                                   new[]
                    {
                        $"The repository '{subscription.TargetRepository}' does not have an associated github installation. " +
                        "The Maestro github application must be installed by the repository's owner and given access to the repository."
                    })));
                }
            }

            Data.Models.Subscription subscriptionModel = subscription.ToDb();
            subscriptionModel.Channel = channel;
            await _context.Subscriptions.AddAsync(subscriptionModel);

            await _context.SaveChangesAsync();

            return(CreatedAtRoute(
                       new
            {
                action = "GetSubscription",
                id = subscriptionModel.Id
            },
                       new Subscription(subscriptionModel)));
        }
Exemple #4
0
        public async Task Test(
            bool mergeOnPassedChecks,
            PrStatus prStatus,
            bool existingPrHasChecks,
            bool existingPrPassedChecks)
        {
            const string prCommentId   = "5";
            Channel      channel       = CreateChannel();
            Build        oldBuild      = CreateOldBuild();
            Build        build         = CreateNewBuild();
            var          buildChannels = new[]
            {
                new BuildChannel {
                    Build = oldBuild, Channel = channel
                },
                new BuildChannel {
                    Build = build, Channel = channel
                }
            };
            Subscription subscription = CreateSubscription(mergeOnPassedChecks ? new MergePolicyDefinition
            {
                Name = "AllChecksSuccessful"
            } : null);

            subscription.Channel = channel;
            var repoInstallation =
                new RepoInstallation {
                Repository = subscription.TargetRepository, InstallationId = 1
            };
            Asset asset = build.Assets[0];
            await Context.RepoInstallations.AddAsync(repoInstallation);

            await Context.Subscriptions.AddAsync(subscription);

            await Context.BuildChannels.AddRangeAsync(buildChannels);

            await Context.SaveChangesAsync();

            var actorId    = new ActorId(subscription.Id);
            var existingPr = "https://repo.pr/existing";
            var pr         = "https://repo.pr/new";


            bool shouldMergeExistingPr = prStatus == PrStatus.Open && mergeOnPassedChecks &&
                                         existingPrHasChecks && existingPrPassedChecks;

            void SetupCreatePr()
            {
                Darc.Setup(
                    d => d.CreatePullRequestAsync(
                        subscription.TargetRepository,
                        subscription.TargetBranch,
                        build.Commit,
                        It.IsAny <IList <AssetData> >(),
                        null,
                        It.IsAny <string>(),
                        It.IsAny <string>()))
                .ReturnsAsync(
                    (
                        string repo,
                        string branch,
                        string commit,
                        IList <AssetData> assets,
                        string baseBranch,
                        string title,
                        string description) =>
                {
                    assets.Should().BeEquivalentTo(new AssetData {
                        Name = asset.Name, Version = asset.Version
                    });
                    return(pr);
                });
            }

            void SetupUpdatePr()
            {
                Darc.Setup(
                    r => r.UpdatePullRequestAsync(
                        existingPr,
                        build.Commit,
                        subscription.TargetBranch,
                        It.IsAny <IList <AssetData> >(),
                        It.IsAny <string>(),
                        It.IsAny <string>()))
                .ReturnsAsync(
                    (
                        string url,
                        string commit,
                        string branch,
                        IList <AssetData> assets,
                        string title,
                        string description) =>
                {
                    return(url);
                });
            }

            void SetupExistingPr()
            {
                StateManager.Data[SubscriptionActor.PullRequest] =
                    new InProgressPullRequest {
                    BuildId = oldBuild.Id, Url = existingPr
                };
                Darc.Setup(r => r.GetPullRequestStatusAsync(existingPr)).ReturnsAsync(prStatus);
                if (mergeOnPassedChecks && prStatus == PrStatus.Open)
                {
                    if (existingPrHasChecks)
                    {
                        Darc.Setup(r => r.GetPullRequestChecksAsync(existingPr))
                        .ReturnsAsync(
                            new List <Check>
                        {
                            new Check(
                                existingPrPassedChecks ? CheckState.Success : CheckState.Failure,
                                "check",
                                "https://check.stuff/1")
                        });
                    }
                    else
                    {
                        Darc.Setup(r => r.GetPullRequestChecksAsync(existingPr)).ReturnsAsync(new List <Check>());
                    }
                }

                if (prStatus == PrStatus.Open)
                {
                    Darc.Setup(d => d.CreatePullRequestCommentAsync(It.IsAny <string>(), It.IsAny <string>()))
                    .ReturnsAsync(prCommentId);
                }

                if (shouldMergeExistingPr)
                {
                    Darc.Setup(r => r.MergePullRequestAsync(existingPr, null))
                    .Returns(Task.CompletedTask);
                }
            }

            switch (prStatus)
            {
            case PrStatus.None:
                SetupCreatePr();
                break;

            case PrStatus.Open:
                SetupExistingPr();
                if (shouldMergeExistingPr)
                {
                    SetupCreatePr();
                }
                else
                {
                    SetupUpdatePr();
                }

                break;

            case PrStatus.Merged:
                SetupExistingPr();
                SetupCreatePr();
                break;

            case PrStatus.Closed:
                SetupExistingPr();
                SetupCreatePr();
                break;
            }


            var actor = ActivatorUtilities.CreateInstance <SubscriptionActor>(Scope.ServiceProvider, actorId);
            await actor.UpdateAsync(build.Id);

            if (shouldMergeExistingPr || prStatus == PrStatus.Merged)
            {
                subscription.LastAppliedBuildId.Should().Be(oldBuild.Id);
            }
            else
            {
                subscription.LastAppliedBuildId.Should().Be(null);
            }

            StateManager.Data.Should()
            .BeEquivalentTo(
                new Dictionary <string, object>
            {
                [SubscriptionActor.PullRequest] = new InProgressPullRequest
                {
                    BuildId         = build.Id,
                    Url             = prStatus == PrStatus.Open && !shouldMergeExistingPr ? existingPr : pr,
                    StatusCommentId = prStatus == PrStatus.Open && !shouldMergeExistingPr ? prCommentId : null
                }
            });

            Reminders.Data.Should()
            .BeEquivalentTo(
                new Dictionary <string, MockReminderManager.Reminder>
            {
                [SubscriptionActor.PullRequestCheck] = new MockReminderManager.Reminder(
                    SubscriptionActor.PullRequestCheck,
                    Array.Empty <byte>(),
                    TimeSpan.FromMinutes(5),
                    TimeSpan.FromMinutes(5))
            });
        }
Exemple #5
0
        public async Task SynchronizeInProgressPRAsync(
            PrStatus prStatus,
            bool existingPrHasChecks,
            bool existingPrPassedChecks)
        {
            Channel channel       = CreateChannel();
            Build   oldBuild      = CreateOldBuild();
            Build   build         = CreateNewBuild();
            var     buildChannels = new[]
            {
                new BuildChannel {
                    Build = oldBuild, Channel = channel
                },
                new BuildChannel {
                    Build = build, Channel = channel
                }
            };
            Subscription subscription = CreateSubscription(new MergePolicyDefinition
            {
                Name = "AllChecksSuccessful",
            });

            subscription.Channel = channel;
            var repoInstallation =
                new RepoInstallation {
                Repository = subscription.TargetRepository, InstallationId = 1
            };
            await Context.RepoInstallations.AddAsync(repoInstallation);

            await Context.Subscriptions.AddAsync(subscription);

            await Context.BuildChannels.AddRangeAsync(buildChannels);

            await Context.SaveChangesAsync();

            var existingPr = "https://repo.pr/existing";
            var actorId    = new ActorId(subscription.Id);

            StateManager.Data[SubscriptionActor.PullRequest] =
                new InProgressPullRequest {
                BuildId = oldBuild.Id, Url = existingPr
            };

            Darc.Setup(d => d.GetPullRequestStatusAsync(existingPr)).ReturnsAsync(prStatus);

            if (prStatus == PrStatus.Open)
            {
                if (existingPrHasChecks)
                {
                    Darc.Setup(d => d.GetPullRequestChecksAsync(existingPr))
                    .ReturnsAsync(
                        new List <Check>
                    {
                        new Check(
                            existingPrPassedChecks ? CheckState.Success : CheckState.Failure,
                            "check",
                            "https://check.stuff/1")
                    });
                }
                else
                {
                    Darc.Setup(d => d.GetPullRequestChecksAsync(existingPr)).ReturnsAsync(new List <Check>());
                }

                if (existingPrHasChecks && existingPrPassedChecks)
                {
                    Darc.Setup(d => d.MergePullRequestAsync(existingPr, null))
                    .Returns(Task.CompletedTask);
                }

                Darc.Setup(d => d.CreatePullRequestCommentAsync(It.IsAny <string>(), It.IsAny <string>()))
                .ReturnsAsync("5");
            }

            var actor = ActivatorUtilities.CreateInstance <SubscriptionActor>(Scope.ServiceProvider, actorId);
            await actor.ReceiveReminderAsync(
                SubscriptionActor.PullRequestCheck,
                Array.Empty <byte>(),
                TimeSpan.Zero,
                TimeSpan.FromMinutes(5));

            switch (prStatus)
            {
            case PrStatus.Merged:
                subscription.LastAppliedBuildId.Should().Be(oldBuild.Id);
                goto case PrStatus.Closed;

            case PrStatus.Closed:
                Reminders.Data.Should().BeEmpty();
                StateManager.Data.Should().BeEmpty();
                break;
            }
        }