public async Task <IActionResult> DeleteUserVisit(int user, string visit) { IActionResult response = null; this._logger.LogInformation(LoggingEvents.DELETE_USER_VISIT, "Delete user visit for userId={user} and visitId={visit}", user, visit); var claims = this.ExtractClaimsFromAuthorizationHeaderBearerToken(this.Request.Headers); //NOTE: Right now this always evaluates to true, because the BearerTokenDecryptor is hard coded to //return this claim. if (ClaimsChecker.IsAllowed("DELETE", "*", claims)) { await this.VisitsRepository.DeleteVisit(user, visit); //200 OK is always returned unless there is an internal server error, because this is an //idempotent call. If there is an error, the framework will auto reply with 500 internal server error. response = this.Ok(); } else { this._logger.LogWarning(LoggingEvents.DELETE_USER_VISIT, "Unauthorized attempt to delete user visit for userId={user} and visitId={visit}", user, visit); response = this.Unauthorized(); } return(response); }
public async Task <IActionResult> PostUserVisit(int user, [FromBody] PostVisitRepresentation visit) { IActionResult response = null; this._logger.LogInformation(LoggingEvents.POST_USER_VISIT, "Post user visit for userId={user}", user); var claims = this.ExtractClaimsFromAuthorizationHeaderBearerToken(this.Request.Headers); //NOTE: Right now this always evaluates to true, because the BearerTokenDecryptor is hard coded to //return this claim. if (ClaimsChecker.IsAllowed("POST", "*", claims)) { var claimsUserId = this.ExtractClaimsUserId(claims); if (claimsUserId.HasValue && user == claimsUserId.Value) { //Validate input data if (!string.IsNullOrWhiteSpace(visit.City) && visit.City.Length <= MAXCITYLENGTH && !string.IsNullOrWhiteSpace(visit.State) && visit.State.Length == MAXSTATELENGTH) { //TODO: Add rate limiting check here, so even if a valid user submits valid data, //they can't create hundreds of these per minute, because it's expected to come //from a real end user and not an automated system. If a backend system does call //this API then the rate limiting can be made smarter or relaxed to a very large number. var tasks = new List <Task>(); var stateTask = this.GeographyRepository.GetStateByAbbreviationAsync(visit.State); tasks.Add(stateTask); var cityTask = this.GeographyRepository.GetCityAsync(visit.State, visit.City); tasks.Add(cityTask); var state = stateTask.Result; var city = cityTask.Result; //Do all these tasks at once and wait for all to complete. await Task.WhenAll(tasks); //Persist to the repository var userVisit = new Visit() { Created = DateTime.UtcNow, User = user, CityId = city.CityId, StateId = state.StateId, VisitId = Guid.NewGuid().ToString() }; await this.VisitsRepository.SaveVisit(userVisit); //Convert to representation. var visitRepresentation = new VisitRepresentation() { City = city.Name, Created = userVisit.Created, State = state.Abbreviation, User = user, VisitId = userVisit.VisitId, Links = new VisitRepresentationLinks() { Self = new Link() { Href = string.Format("/user/{0}/visit/{1}", userVisit.User, userVisit.VisitId) } } }; response = this.Ok(visitRepresentation); } else { response = this.BadRequest("City or state too long. Use city name and state abbreviation."); } } else { this._logger.LogWarning(LoggingEvents.POST_USER_VISIT, "Unauthorized attempt to post user visit for userId={user}", user); response = this.Unauthorized(); } } else { this._logger.LogWarning(LoggingEvents.POST_USER_VISIT, "Unauthorized attempt to post user visit for userId={user}", user); response = this.Unauthorized(); } return(response); }