public virtual async Task <IActionResult> RetrySubscriptionActionAsync(Guid id, long timestamp) { DateTime ts = DateTimeOffset.FromUnixTimeSeconds(timestamp).UtcDateTime; Data.Models.Subscription subscription = await _context.Subscriptions.Where(sub => sub.Id == id) .FirstOrDefaultAsync(); if (subscription == null) { return(NotFound()); } SubscriptionUpdateHistoryEntry update = await _context.SubscriptionUpdateHistory .Where(u => u.SubscriptionId == id) .FirstOrDefaultAsync(u => Math.Abs(EF.Functions.DateDiffSecond(u.Timestamp, ts)) < 1); if (update == null) { return(NotFound()); } if (update.Success) { return(StatusCode( (int)HttpStatusCode.NotAcceptable, new ApiError("That action was successful, it cannot be retried."))); } _queue.Post <SubscriptionActorActionWorkItem>( SubscriptionActorActionWorkItem.GetArguments(subscription.Id, update.Method, update.Arguments) ); return(Accepted()); }
protected async Task <IActionResult> TriggerSubscriptionCore(Guid id, int buildId) { Data.Models.Subscription subscription = await _context.Subscriptions.Include(sub => sub.LastAppliedBuild) .Include(sub => sub.Channel) .FirstOrDefaultAsync(sub => sub.Id == id); if (buildId != 0) { Data.Models.Build build = await _context.Builds.Where(b => b.Id == buildId).FirstOrDefaultAsync(); // Non-existent build if (build == null) { return(BadRequest($"Build {buildId} was not found")); } // Build doesn't match source repo if (!(build.GitHubRepository?.Equals(subscription.SourceRepository, StringComparison.InvariantCultureIgnoreCase) == true || build.AzureDevOpsRepository?.Equals(subscription.SourceRepository, StringComparison.InvariantCultureIgnoreCase) == true)) { return(BadRequest($"Build {buildId} does not match source repo")); } } if (subscription == null) { return(NotFound()); } var values = new { SubId = id, BuildId = buildId }; _queue.Post <StartSubscriptionUpdateWorkItem>(JToken.FromObject(values)); return(Accepted(new Subscription(subscription))); }
public async Task <IActionResult> Create([FromBody] SubscriptionData subscription) { Data.Models.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." }))); } var subscriptionModel = new Data.Models.Subscription(subscription) { Channel = channel }; await _context.Subscriptions.AddAsync(subscriptionModel); await _context.SaveChangesAsync(); return(CreatedAtRoute( new { action = "GetSubscription", id = subscriptionModel.Id }, new Subscription(subscriptionModel))); }
public async Task Subscribe(SubscriptionRequest request) { Data.Models.Subscription subscription = await m_context.Subscriptions.SingleOrDefaultAsync(x => x.Theme == request.Theme); if (subscription == null) { subscription = new Data.Models.Subscription { Theme = request.Theme, Creator = request.Creator }; } if (subscription.Id == null || !(await m_context.UserSubscriptions.AnyAsync(x => x.UserId == request.UserId && x.SubscriptionId == subscription.Id))) { await m_context.UserSubscriptions.AddAsync(new UserSubscription { UserId = request.UserId, Subscription = subscription, EmailNotificationNeeded = request.EmailNotificationNeeded, ClientNotificationNeeded = request.ClientNotificationNeeded, IsSubscribed = true }); await m_context.SaveChangesAsync(); } }
public override async Task <IActionResult> UpdateSubscription(Guid id, [FromBody] v2018_07_16.Models.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)) { Data.Models.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))); }
public override async Task <IActionResult> TriggerSubscription(Guid id) { Data.Models.Subscription subscription = await TriggerSubscriptionCore(id); if (subscription == null) { return(NotFound()); } return(Accepted(new Subscription(subscription))); }
public async Task Unsubscribe(UnsubscriptionRequest request) { Data.Models.Subscription subscription = await m_context.Subscriptions.AsNoTracking().SingleOrDefaultAsync(x => x.Theme == request.Theme); UserSubscription userSubscription = await m_context.UserSubscriptions .SingleOrDefaultAsync(x => x.UserId == request.UserId && x.SubscriptionId == subscription.Id); userSubscription.IsSubscribed = false; await m_context.SaveChangesAsync(); }
public async Task <IActionResult> GetSubscription(Guid id) { Data.Models.Subscription subscription = await _context.Subscriptions.Where(sub => sub.Id == id) .FirstOrDefaultAsync(); if (subscription == null) { return(NotFound()); } return(Ok(new Subscription(subscription))); }
public override async Task <IActionResult> GetSubscription(Guid id) { Data.Models.Subscription subscription = await _context.Subscriptions.Include(sub => sub.LastAppliedBuild) .Include(sub => sub.Channel) .FirstOrDefaultAsync(sub => sub.Id == id); if (subscription == null) { return(NotFound()); } return(Ok(new Subscription(subscription))); }
protected async Task <Data.Models.Subscription> TriggerSubscriptionCore(Guid id) { Data.Models.Subscription subscription = await _context.Subscriptions.Include(sub => sub.LastAppliedBuild) .Include(sub => sub.Channel) .FirstOrDefaultAsync(sub => sub.Id == id); if (subscription == null) { return(null); } _queue.Post <StartSubscriptionUpdateWorkItem>(JToken.FromObject(id)); return(subscription); }
public Subscription([NotNull] Data.Models.Subscription other) { if (other == null) { throw new ArgumentNullException(nameof(other)); } Id = other.Id; Channel = other.Channel == null ? null : new Channel(other.Channel); SourceRepository = other.SourceRepository; TargetRepository = other.TargetRepository; TargetBranch = other.TargetBranch; Policy = new SubscriptionPolicy(other.PolicyObject); }
public virtual async Task <IActionResult> GetSubscriptionHistory(Guid id) { Data.Models.Subscription subscription = await _context.Subscriptions.Where(sub => sub.Id == id) .FirstOrDefaultAsync(); if (subscription == null) { return(NotFound()); } IOrderedQueryable <SubscriptionUpdateHistoryEntry> query = _context.SubscriptionUpdateHistory .Where(u => u.SubscriptionId == id) .OrderByDescending(u => u.Timestamp); return(Ok(query)); }
public Subscription([NotNull] Data.Models.Subscription other) { if (other == null) { throw new ArgumentNullException(nameof(other)); } Id = other.Id; Channel = other.Channel == null ? null : new Channel(other.Channel); LastAppliedBuild = other.LastAppliedBuild == null ? null : new Build(other.LastAppliedBuild); SourceRepository = other.SourceRepository; TargetRepository = other.TargetRepository; TargetBranch = other.TargetBranch; Enabled = other.Enabled; Policy = new v2018_07_16.Models.SubscriptionPolicy(other.PolicyObject); }
public override async Task <IActionResult> TriggerSubscription(Guid id) { Data.Models.Subscription subscription = await _context.Subscriptions.Include(sub => sub.LastAppliedBuild) .Include(sub => sub.Channel) .FirstOrDefaultAsync(sub => sub.Id == id); if (subscription == null) { return(NotFound()); } _queue.Post( async() => { await _dependencyUpdater.StartSubscriptionUpdateAsync(id); }); return(Accepted(new Subscription(subscription))); }
public async Task NotifySubsribers(string theme, string text, Logic.Models.Enums.Severity severity) { Data.Models.Subscription subscription = await m_context.Subscriptions .AsNoTracking() .Include(x => x.UserSubscriptions.Where(u => u.IsSubscribed == true)) .SingleOrDefaultAsync(x => x.Theme == theme); if (subscription == null) { throw new System.Exception(); } NotificationMessage message = new NotificationMessage { Title = theme, Text = text, Severity = severity }; UserNotificationBulkRequest notificationRequest = new UserNotificationBulkRequest { UserIds = subscription.UserSubscriptions.Where(x => x.ClientNotificationNeeded == true).Select(x => x.UserId), Message = message }; UserNotificationBulkRequest emailRequest = new UserNotificationBulkRequest { UserIds = subscription.UserSubscriptions.Where(x => x.EmailNotificationNeeded == true).Select(x => x.UserId), Message = message }; if (notificationRequest.UserIds.Count() != 0) { await m_notificationService.AddUserNotifications(notificationRequest); } if (emailRequest.UserIds.Count() != 0) { await m_emailNotificationService.SendEmailNotifications(emailRequest); } }
public override async Task <IActionResult> DeleteSubscription(Guid id) { Data.Models.Subscription subscription = await _context.Subscriptions.FirstOrDefaultAsync(sub => sub.Id == id); if (subscription == null) { return(NotFound()); } Data.Models.SubscriptionUpdate subscriptionUpdate = await _context.SubscriptionUpdates.FirstOrDefaultAsync(u => u.SubscriptionId == subscription.Id); if (subscriptionUpdate != null) { _context.SubscriptionUpdates.Remove(subscriptionUpdate); } _context.Subscriptions.Remove(subscription); await _context.SaveChangesAsync(); return(Ok(new Subscription(subscription))); }
public async Task <IActionResult> UpdateSubscription(Guid id, [FromBody] SubscriptionUpdate update) { Data.Models.Subscription subscription = await _context.Subscriptions.Where(sub => sub.Id == id).Include(sub => sub.Channel) .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 (update.PullRequestFailureNotificationTags != null) { if (!await AllNotificationTagsValid(update.PullRequestFailureNotificationTags)) { return(BadRequest(new ApiError("Invalid value(s) provided in Pull Request Failure Notification Tags; is everyone listed publicly a member of the Microsoft github org?"))); } subscription.PullRequestFailureNotificationTags = update.PullRequestFailureNotificationTags; doUpdate = true; } if (!string.IsNullOrEmpty(update.ChannelName)) { Data.Models.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) { Data.Models.Subscription equivalentSubscription = await FindEquivalentSubscription(subscription); if (equivalentSubscription != null) { return(Conflict( new ApiError( "the request is invalid", new[] { $"The subscription '{equivalentSubscription.Id}' already performs the same update." }))); } _context.Subscriptions.Update(subscription); await _context.SaveChangesAsync(); } return(Ok(new Subscription(subscription))); }
public async Task <IActionResult> Create([FromBody, Required] BuildData build) { Data.Models.Build buildModel = build.ToDb(); buildModel.DateProduced = DateTimeOffset.UtcNow; if (build.Dependencies != null) { // For each Dependency, update the time to Inclusion. // This measure is to be used for telemetry purposes, and has several known corner cases // where the measurement will not be correct: // 1. For any dependencies that were added before this column was added, the TimeToInclusionInMinutes // will be 0. // 2. For new release branches, until new builds of dependencies are added, this will recalculate // the TimeToInclusion, so it will seem inordinately large until new builds are added. This will // be particularly true for dependencies that are infrequently updated. foreach (var dep in build.Dependencies) { // Heuristic to discover if this dependency has been added to the same repository and branch // of the current build. If we find a match in the BuildDependencies table, it means // that this is not a new dependency, and we should use the TimeToInclusionInMinutes // of the previous time this dependency was added. var buildDependency = _context.BuildDependencies.Where(d => d.DependentBuildId == dep.BuildId && d.Build.GitHubRepository == buildModel.GitHubRepository && d.Build.GitHubBranch == buildModel.GitHubBranch && d.Build.AzureDevOpsRepository == buildModel.AzureDevOpsRepository && d.Build.AzureDevOpsBranch == buildModel.AzureDevOpsBranch ).FirstOrDefault(); if (buildDependency != null) { dep.TimeToInclusionInMinutes = buildDependency.TimeToInclusionInMinutes; } else { // If the dependent build is not currently in the BuildDependency table for this repo/branch (ie is a new dependency), // find the dependency in the Builds table and calculate the time to inclusion // We want to use the BuildChannel insert time if it exists. So we need to heuristically: // 1. Find the subscription between these two repositories on the current branch // 2. Find the entry in BuildChannels and get the insert time // In certain corner cases, we may pick the wrong subscription or BuildChannel Data.Models.Build depBuild = await _context.Builds.FindAsync(dep.BuildId); // If we don't find a subscription or a BuildChannel entry, use the dependency's // date produced. DateTimeOffset startTime = depBuild.DateProduced; Data.Models.Subscription subscription = _context.Subscriptions.Where(s => (s.SourceRepository == depBuild.GitHubRepository || s.SourceRepository == depBuild.AzureDevOpsRepository) && (s.TargetRepository == buildModel.GitHubRepository || s.TargetRepository == buildModel.AzureDevOpsRepository) && (s.TargetBranch == buildModel.GitHubBranch || s.TargetBranch == buildModel.AzureDevOpsBranch) ).LastOrDefault(); if (subscription != null) { Data.Models.BuildChannel buildChannel = _context.BuildChannels.Where(bc => bc.BuildId == depBuild.Id && bc.ChannelId == subscription.ChannelId ).LastOrDefault(); if (buildChannel != null) { startTime = buildChannel.DateTimeAdded; } } dep.TimeToInclusionInMinutes = (buildModel.DateProduced - startTime).TotalMinutes; } } await _context.BuildDependencies.AddRangeAsync( build.Dependencies.Select( b => new Data.Models.BuildDependency { Build = buildModel, DependentBuildId = b.BuildId, IsProduct = b.IsProduct, TimeToInclusionInMinutes = b.TimeToInclusionInMinutes, })); } await _context.Builds.AddAsync(buildModel); await _context.SaveChangesAsync(); // Compute the dependency incoherencies of the build. // Since this might be an expensive operation we do it asynchronously. Queue.Post( async() => { await SetBuildIncoherencyInfoAsync(buildModel.Id); }); return(CreatedAtRoute( new { action = "GetBuild", id = buildModel.Id }, new Models.Build(buildModel))); }
private async Task SaveCandidates(SophiaDbContext dbContext, IEnumerable <Recommending.Candidate> candidates, Octokit.PullRequest pullRequest, Data.Models.Subscription subscription) { var dateTime = DateTimeOffset.UtcNow; dbContext.AddRange(candidates.Select(q => new Data.Models.Candidate() { GitHubLogin = q.Contributor.GitHubLogin, PullRequestNumber = pullRequest.Number, Rank = q.Rank, RecommenderType = RecommenderType.Chrev, SubscriptionId = subscription.Id, SuggestionDateTime = dateTime })); await dbContext.SaveChangesAsync(); }
private async Task GetCandidates(EventContext eventContext, SophiaDbContext dbContext, int issueNumber, long repositoryId, Data.Models.Subscription subscription, int topCandidatesLength) { var installationClient = eventContext.InstallationContext.Client; var recommender = new CodeReviewerRecommender(RecommenderType.Chrev, dbContext); var pullRequest = await installationClient.PullRequest.Get(repositoryId, issueNumber); var pullRequestFiles = await installationClient.PullRequest.Files(repositoryId, issueNumber); var candidates = await recommender.Recommend(subscription.Id, pullRequest, pullRequestFiles, topCandidatesLength); await SaveCandidates(dbContext, candidates, pullRequest, subscription); var message = GenerateMessage(candidates, pullRequestFiles); await installationClient.Issue.Comment.Create(repositoryId, issueNumber, message); }
private async Task GetCandidates(EventContext eventContext, SophiaDbContext dbContext, int issueNumber, long repositoryId, Data.Models.Subscription subscription, int topCandidatesLength) { var installationClient = eventContext.InstallationContext.Client; var pullRequest = await installationClient.PullRequest.Get(repositoryId, issueNumber); var pullRequestFiles = await installationClient.PullRequest.Files(repositoryId, issueNumber); var fileOwners = await GetFileOwners(dbContext, subscription.Id, pullRequestFiles); var expertCandidates = await GetExpertCandidates(dbContext, subscription.Id, pullRequest, pullRequestFiles, topCandidatesLength); var learnerCandidates = await GetLearnerCandidates(dbContext, subscription.Id, pullRequest, pullRequestFiles, topCandidatesLength); await SaveCandidates(dbContext, expertCandidates, learnerCandidates, pullRequest, subscription); var message = GenerateMessage(expertCandidates, learnerCandidates, pullRequestFiles, fileOwners); await installationClient.Issue.Comment.Create(repositoryId, issueNumber, message); }