// //////////////////////////////////////////////////////////// // Pull Request Reviews // //////////////////////////////////////////////////////////// private async Task <HttpResponseMessage> ProxyPullRequestReview(HttpRequestMessage request, CancellationToken cancellationToken, string owner, string repo, int number) { var response = await ProxyRequest(request, cancellationToken); if (response.IsSuccessStatusCode) { var user = RequestContext.Principal as ShipHubPrincipal; try { await response.Content.LoadIntoBufferAsync(); var review = await response.Content.ReadAsAsync <Review>(GitHubSerialization.MediaTypeFormatters, cancellationToken); using (var context = new dm.ShipHubContext()) { var updater = new DataUpdater(context, _mapper); var repoName = $"{owner}/{repo}"; var ids = await context.Issues .AsNoTracking() .Where(x => x.Repository.FullName == repoName) .Where(x => x.Number == number) .Select(x => new { IssueId = x.Id, RepositoryId = x.RepositoryId }) .SingleAsync(); await updater.UpdateReviews(ids.RepositoryId, ids.IssueId, response.Headers.Date ?? DateTimeOffset.UtcNow, new[] { review }, user.UserId); await updater.Changes.Submit(_queueClient); } } catch (Exception e) { // swallow db exceptions, since if we're here github has created the resource. // we'll probably get it fixed in our db sooner or later, but for now we need to give the client its data. e.Report($"request: {request.RequestUri} response: {response} user: {user.DebugIdentifier}", user.DebugIdentifier); } } return(response); }
public async Task <HttpResponseMessage> DeleteRequestedReviewer(HttpRequestMessage request, CancellationToken cancellationToken, string owner, string repo, int issueNumber) { // https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request var response = await ProxyRequest(request, cancellationToken); if (response.StatusCode == HttpStatusCode.OK) { var user = RequestContext.Principal as ShipHubPrincipal; try { await response.Content.LoadIntoBufferAsync(); var data = await response.Content.ReadAsAsync <JToken>(GitHubSerialization.MediaTypeFormatters, cancellationToken); var removed = data.Value <IEnumerable <string> >("reviewers"); using (var context = new dm.ShipHubContext()) { var changes = await context.DeleteReviewers($"{owner}/{repo}", issueNumber, removed); await changes.Submit(_queueClient); } } catch (Exception e) { e.Report($"request: {request.RequestUri} response: {response} user: {user.DebugIdentifier}", user.DebugIdentifier); } } return(response); }
public async Task <HttpResponseMessage> IssueMilestoneDelete(HttpRequestMessage request, CancellationToken cancellationToken, string owner, string repo, long number) { var response = await ProxyRequest(request, cancellationToken); if (response.StatusCode == HttpStatusCode.NoContent) { var user = RequestContext.Principal as ShipHubPrincipal; try { using (var context = new dm.ShipHubContext()) { // Eww var repoFullName = $"{owner}/{repo}"; var milestoneId = await context.Milestones.AsNoTracking() .Where(x => x.Repository.FullName == repoFullName) .Where(x => x.Number == number) .Select(x => (long?)x.Id) .SingleOrDefaultAsync(); if (milestoneId != null) { var changes = await context.DeleteMilestone(milestoneId.Value); await changes.Submit(_queueClient); } } } catch (Exception e) { e.Report($"request: {request.RequestUri} response: {response} user: {user.DebugIdentifier}", user.DebugIdentifier); } } return(response); }
// //////////////////////////////////////////////////////////// // Issue Labels // //////////////////////////////////////////////////////////// private async Task <HttpResponseMessage> ProxyLabel(HttpRequestMessage request, CancellationToken cancellationToken, string owner, string repo) { var response = await ProxyRequest(request, cancellationToken); if (response.IsSuccessStatusCode) { var user = RequestContext.Principal as ShipHubPrincipal; try { await response.Content.LoadIntoBufferAsync(); var label = await response.Content.ReadAsAsync <Label>(GitHubSerialization.MediaTypeFormatters, cancellationToken); using (var context = new dm.ShipHubContext()) { var updater = new DataUpdater(context, _mapper); var repoName = $"{owner}/{repo}"; var repoId = await context.Repositories .AsNoTracking() .Where(x => x.FullName == repoName) .Select(x => x.Id) .SingleAsync(); await updater.UpdateLabels(repoId, new[] { label }); await updater.Changes.Submit(_queueClient); } } catch (Exception e) { // swallow db exceptions, since if we're here github has created the resource. // we'll probably get it fixed in our db sooner or later, but for now we need to give the client its data. e.Report($"request: {request.RequestUri} response: {response} user: {user.DebugIdentifier}", user.DebugIdentifier); } } return(response); }
public async Task <IHttpActionResult> Logout() { // User wants to log out. using (var context = new d.ShipHubContext()) { var hookDetails = await context.GetLogoutWebhooks(ShipHubUser.UserId); var github = await _grainFactory.GetGrain <IGitHubActor>(ShipHubUser.UserId); var tasks = new List <Task>(); // Delete all repo hooks where they're the only user tasks.AddRange(hookDetails.RepositoryHooks.Select(x => github.DeleteRepositoryWebhook(x.Name, x.HookId, RequestPriority.Interactive))); // Delete all org hooks where they're the only user tasks.AddRange(hookDetails.OrganizationHooks.Select(x => github.DeleteOrganizationWebhook(x.Name, x.HookId, RequestPriority.Interactive))); // Wait and log errors. var userInfo = $"{ShipHubUser.Login} ({ShipHubUser.UserId})"; try { // Ensure requests complete before we revoke our access below await Task.WhenAll(tasks); foreach (var task in tasks) { task.LogFailure(userInfo); } } catch { // They're logging out. We had our chance. } var tokens = await context.Tokens .Where(x => x.UserId == ShipHubUser.UserId) .Select(x => x.Token) .ToArrayAsync(); // Try all the tokens. foreach (var token in tokens) { RevokeGrant(token).LogFailure(userInfo); } // Invalidate their token with ShipHub await context.RevokeAccessTokens(ShipHubUser.UserId); } return(Ok()); }
private async Task <(g.Account UserInfo, IHttpActionResult ErrorResult)> LoginCommon(string token, CancellationToken cancellationToken) { // This would really be a great place for F# discriminated unions. // Return the user info or an error. var userResponse = await GitHubUser(token, cancellationToken); if (!userResponse.IsOk) { return(UserInfo : null, ErrorResult : Error("Unable to determine account from token.", HttpStatusCode.InternalServerError, userResponse.Error)); } var userInfo = userResponse.Result; if (userInfo.Type != g.GitHubAccountType.User) { return(UserInfo : null, ErrorResult : Error("Token must be for a user.", HttpStatusCode.BadRequest)); } // Check scopes (currently a duplicate check here, I know). var scopesOk = _validScopesCollection.Any(x => x.IsSubsetOf(userResponse.Scopes)); if (!scopesOk) { return( UserInfo : null, ErrorResult : Error("Insufficient scopes granted.", HttpStatusCode.Unauthorized, new { Granted = userResponse.Scopes, }) ); } using (var context = new d.ShipHubContext()) { // Create account using stored procedure // This ensures it exists (simpler logic) and also won't collide with sync. await context.BulkUpdateAccounts(userResponse.Date, new[] { _mapper.Map <AccountTableType>(userInfo) }); // Save user access details await context.SetUserAccessToken(userInfo.Id, string.Join(",", userResponse.Scopes), userResponse.RateLimit); } return(UserInfo : userInfo, ErrorResult : null); }
public async Task <HttpResponseMessage> PullRequestReviewCommentDelete(HttpRequestMessage request, CancellationToken cancellationToken, long commentId) { // https://developer.github.com/v3/pulls/comments/#delete-a-comment var response = await ProxyRequest(request, cancellationToken); if (response.StatusCode == HttpStatusCode.NoContent) { var user = RequestContext.Principal as ShipHubPrincipal; try { using (var context = new dm.ShipHubContext()) { var changes = await context.DeletePullRequestComment(commentId, null); await changes.Submit(_queueClient); } } catch (Exception e) { e.Report($"request: {request.RequestUri} response: {response} user: {user.DebugIdentifier}", user.DebugIdentifier); } } return(response); }
public async Task <HttpResponseMessage> PullRequestReviewDelete(HttpRequestMessage request, CancellationToken cancellationToken, long reviewId) { // https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review var response = await ProxyRequest(request, cancellationToken); // WTF DELETE Review returns 200 OK // https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review if (response.IsSuccessStatusCode) { var user = RequestContext.Principal as ShipHubPrincipal; try { using (var context = new dm.ShipHubContext()) { var changes = await context.DeleteReview(reviewId); await changes.Submit(_queueClient); } } catch (Exception e) { e.Report($"request: {request.RequestUri} response: {response} user: {user.DebugIdentifier}", user.DebugIdentifier); } } return(response); }
public async Task <HttpResponseMessage> PullRequestReviewCommentEdit(HttpRequestMessage request, CancellationToken cancellationToken, long commentId) { // https://developer.github.com/v3/pulls/comments/#edit-a-comment var response = await ProxyRequest(request, cancellationToken); if (response.IsSuccessStatusCode) { var user = RequestContext.Principal as ShipHubPrincipal; try { await response.Content.LoadIntoBufferAsync(); var comment = await response.Content.ReadAsAsync <PullRequestComment>(GitHubSerialization.MediaTypeFormatters, cancellationToken); using (var context = new dm.ShipHubContext()) { var updater = new DataUpdater(context, _mapper); var ids = await context.PullRequestComments .AsNoTracking() .Where(x => x.Id == commentId) .Select(x => new { IssueId = x.IssueId, RepositoryId = x.RepositoryId }) .SingleOrDefaultAsync(); if (ids != null) { await updater.UpdatePullRequestComments(ids.RepositoryId, ids.IssueId, response.Headers.Date ?? DateTimeOffset.UtcNow, new[] { comment }); await updater.Changes.Submit(_queueClient); } } } catch (Exception e) { // swallow db exceptions, since if we're here github has created the resource. // we'll probably get it fixed in our db sooner or later, but for now we need to give the client its data. e.Report($"request: {request.RequestUri} response: {response} user: {user.DebugIdentifier}", user.DebugIdentifier); } } return(response); }
public async Task <IHttpActionResult> Login([FromBody] LoginRequest request, CancellationToken cancellationToken) { if ((request?.AccessToken).IsNullOrWhiteSpace()) { return(BadRequest($"{nameof(request.AccessToken)} is required.")); } if (request.ClientName.IsNullOrWhiteSpace()) { return(BadRequest($"{nameof(request.ClientName)} is required.")); } d.User user = null; g.Account userInfo; IHttpActionResult errorResult = null; // Newer clients will have already authenticated via lambda_legacy // They don't send the authorization header though, so let's check // if we recognize this token. using (var context = new d.ShipHubContext()) { user = await context.Tokens .AsNoTracking() .Where(x => x.Token == request.AccessToken) .Select(x => x.User) .SingleOrDefaultAsync(); } if (user != null) { // We know this user already. userInfo = new g.Account() { Id = user.Id, Login = user.Login, Name = user.Name, Type = g.GitHubAccountType.User, }; } else // End goal is to make everything below obsolete. { var login = await LoginCommon(request.AccessToken, cancellationToken); userInfo = login.UserInfo; errorResult = login.ErrorResult; } if (errorResult != null) { // Abort early on error return(errorResult); } // Save settings if sent if (request.SyncSettings != null) { using (var context = new d.ShipHubContext()) { await context.SetAccountSettings(userInfo.Id, request.SyncSettings); } } // Start sync var userGrain = await _grainFactory.GetGrain <IUserActor>(userInfo.Id); userGrain.Sync().LogFailure($"{userInfo.Login} ({userInfo.Id})"); return(Ok(userInfo)); }