/// <summary> /// Validates a personal goal data received from client application is valid. /// </summary> /// <param name="personalGoalData">Details of a personal goal to be saved/updated in storage</param> /// <returns> Returns response representing whether data received is valid or not</returns> private ObjectResult ValidatePersonalGoal(PersonalGoalDetail personalGoalData) { if (!Guid.TryParse(personalGoalData.PersonalGoalId, out _)) { this.logger.LogError(StatusCodes.Status400BadRequest, $"Personal goal id:{personalGoalData.PersonalGoalId} is not a valid GUID."); return(this.BadRequest($"Personal goal id:{personalGoalData.PersonalGoalId} is not a valid GUID.")); } else if (personalGoalData.UserAadObjectId != this.UserObjectId) { this.logger.LogError(StatusCodes.Status403Forbidden, $"User {personalGoalData.UserAadObjectId} is forbidden to perform this operation."); return(this.StatusCode(StatusCodes.Status403Forbidden, $"User {personalGoalData.UserAadObjectId} is forbidden to perform this operation.")); } else if (personalGoalData.CreatedBy != this.HttpContext.User.Identity.Name) { this.logger.LogError(StatusCodes.Status403Forbidden, $"User {personalGoalData.CreatedBy} is forbidden to perform this operation."); return(this.StatusCode(StatusCodes.Status403Forbidden, $"User {personalGoalData.CreatedBy} is forbidden to perform this operation.")); } else if (DateTime.Parse(personalGoalData.StartDate, CultureInfo.InvariantCulture) >= DateTime.Parse(personalGoalData.EndDate, CultureInfo.InvariantCulture)) { this.logger.LogError(StatusCodes.Status400BadRequest, $"Personal goal start date is greater than end date."); return(this.BadRequest("Personal goal start date is greater than end date.")); } else if (DateTime.Parse(personalGoalData.EndDate, CultureInfo.InvariantCulture).ToUniversalTime() < DateTime.Now.ToUniversalTime()) { this.logger.LogError(StatusCodes.Status400BadRequest, $"Personal goal end date is smaller than current date."); return(this.BadRequest("Personal goal end date is smaller than current date.")); } this.logger.LogInformation(StatusCodes.Status200OK, $"Personal goal detail received is valid."); return(this.Ok("Personal goal detail received is valid.")); }
/// <summary> /// Method to send goal reminder card to personal bot. /// </summary> /// <param name="personalGoalDetail">Holds personal goal detail entity data sent from background service.</param> /// <param name="isReminderBeforeThreeDays">Determines reminder to be sent prior 3 days to end date.</param> /// <returns>A Task represents goal reminder card is sent to the bot, installed in the personal scope.</returns> public async Task SendGoalReminderToPersonalBotAsync(PersonalGoalDetail personalGoalDetail, bool isReminderBeforeThreeDays = false) { personalGoalDetail = personalGoalDetail ?? throw new ArgumentNullException(nameof(personalGoalDetail)); var conversationReference = new ConversationReference() { ChannelId = Constants.TeamsBotFrameworkChannelId, Bot = new ChannelAccount() { Id = $"28:{this.microsoftAppCredentials.MicrosoftAppId}" }, ServiceUrl = personalGoalDetail.ServiceUrl, Conversation = new ConversationAccount() { ConversationType = Constants.PersonalConversationType, Id = personalGoalDetail.ConversationId, TenantId = this.options.Value.TenantId }, }; try { await this.retryPolicy.ExecuteAsync(async() => { await((BotFrameworkAdapter)this.adapter).ContinueConversationAsync( this.microsoftAppCredentials.MicrosoftAppId, conversationReference, async(turnContext, cancellationToken) => { string reminderType = string.Empty; AdaptiveTextColor reminderTypeColor = AdaptiveTextColor.Accent; if (isReminderBeforeThreeDays) { reminderType = this.localizer.GetString("PersonalGoalEndingAfterThreeDays"); reminderTypeColor = AdaptiveTextColor.Attention; } else { reminderType = this.GetReminderTypeString(personalGoalDetail.ReminderFrequency); } var goalReminderAttachment = MessageFactory.Attachment(GoalReminderCard.GetGoalReminderCard(this.localizer, this.options.Value.ManifestId, this.options.Value.GoalsTabEntityId, reminderType, reminderTypeColor)); this.logger.LogInformation($"Sending goal reminder card to Personal bot. Conversation id: {personalGoalDetail.ConversationId}"); await turnContext.SendActivityAsync(goalReminderAttachment, cancellationToken); }, CancellationToken.None); }); } catch (Exception ex) { this.logger.LogError(ex, $"Error while sending goal reminder card to personal bot: {ex.Message} at {nameof(this.SendGoalReminderToPersonalBotAsync)}"); throw; } }
public async Task <IActionResult> UpdatePersonalGoalDetailAsync(PersonalGoalDetail personalGoalData) { try { #pragma warning disable CA1062 // Patch details are validated by model validations for null check and is responded with bad request status var validationErrorResponse = this.ValidatePersonalGoal(personalGoalData); #pragma warning restore CA1062 // Patch details are validated by model validations for null check and is responded with bad request status if (validationErrorResponse.StatusCode != StatusCodes.Status200OK) { return(validationErrorResponse); } var existingGoalDetail = await this.personalGoalStorageProvider.GetPersonalGoalDetailByGoalIdAsync(personalGoalData.PersonalGoalId, this.UserObjectId); if (existingGoalDetail == null) { this.logger.LogError(StatusCodes.Status404NotFound, $"The personal goal user trying to update does not exist. Personal goal id: {personalGoalData.PersonalGoalId} "); return(this.NotFound("The personal goal user trying to update does not exist.")); } this.logger.LogInformation("Initiated call to personal goal storage provider."); var result = await this.personalGoalStorageProvider.CreateOrUpdatePersonalGoalDetailAsync(personalGoalData); this.logger.LogInformation("PATCH call for saving personal goal detail in storage is successful"); if (!result) { this.logger.LogError($"Could not save or update goal data received with personal goal id: {personalGoalData.PersonalGoalId}."); return(this.StatusCode(StatusCodes.Status500InternalServerError, "Could not save or update goal data received.")); } // Update list card of personal bot. Enqueue task to task wrapper and it will be executed by goal background service. this.backgroundTaskWrapper.Enqueue(this.cardHelper.UpdatePersonalGoalListCardAsync(personalGoalData)); return(this.Ok(result)); } #pragma warning disable CA1031 // Catching all generic exceptions in order to log exception details in logger catch (Exception ex) #pragma warning restore CA1031 // Catching all generic exceptions in order to log exception details in logger { this.logger.LogError(ex, "Error while saving personal goal detail"); throw; } }
/// <summary> /// Stores or update personal goal detail in storage. /// </summary> /// <param name="personalGoalEntity">Holds personal goal data.</param> /// <returns>A boolean that represents personal goal detail entity data is saved or updated.</returns> public async Task <bool> CreateOrUpdatePersonalGoalDetailAsync(PersonalGoalDetail personalGoalEntity) { personalGoalEntity = personalGoalEntity ?? throw new ArgumentNullException(nameof(personalGoalEntity)); try { await this.EnsureInitializedAsync(); TableOperation operation = TableOperation.InsertOrReplace(personalGoalEntity); var result = await this.CloudTable.ExecuteAsync(operation); return(result.HttpStatusCode == (int)HttpStatusCode.NoContent); } catch (Exception ex) { this.logger.LogError(ex, $"An error occurred in {nameof(this.CreateOrUpdatePersonalGoalDetailAsync)} while saving personal goal data."); throw; } }
/// <summary> /// Gets the add note submit card. /// </summary> /// <param name="personalGoalNoteDetail">Instance containing personal goal note related details.</param> /// <param name="personalGoalDetail">Instance containing personal goal related details.</param> /// <param name="localizer">The current cultures' string localizer.</param> /// <returns>Attachment having add note submit card.</returns> public static Attachment GetsAddNoteSubmitCard(PersonalGoalNoteDetail personalGoalNoteDetail, PersonalGoalDetail personalGoalDetail, IStringLocalizer <Strings> localizer) { personalGoalDetail = personalGoalDetail ?? throw new ArgumentNullException(nameof(personalGoalDetail)); personalGoalNoteDetail = personalGoalNoteDetail ?? throw new ArgumentNullException(nameof(personalGoalNoteDetail)); var isSourceEmpty = string.IsNullOrEmpty(personalGoalNoteDetail?.SourceName) ? true : false; AdaptiveCard addCardSubmitCard = new AdaptiveCard(Constants.AdaptiveCardVersion) { Body = new List <AdaptiveElement>() { new AdaptiveTextBlock { Text = localizer.GetString("AddNoteHeading"), Weight = AdaptiveTextWeight.Bolder, Size = AdaptiveTextSize.Large, Wrap = true, }, new AdaptiveColumnSet { Spacing = AdaptiveSpacing.Padding, Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Width = "1", Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Size = AdaptiveTextSize.Medium, Wrap = true, Text = localizer.GetString("AddNoteGoalNameSubheading"), }, }, }, new AdaptiveColumn { Width = "4", Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Size = AdaptiveTextSize.Medium, Wrap = true, Text = personalGoalDetail.GoalName, Weight = AdaptiveTextWeight.Bolder, }, }, }, }, }, new AdaptiveColumnSet { Spacing = AdaptiveSpacing.Padding, Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Width = "1", Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Size = AdaptiveTextSize.Medium, Wrap = true, Text = localizer.GetString("AddNoteNoteSubheading"), Weight = AdaptiveTextWeight.Bolder, }, }, }, new AdaptiveColumn { Width = "4", Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Size = AdaptiveTextSize.Medium, Wrap = true, Text = personalGoalNoteDetail.PersonalGoalNoteDescription, }, }, }, }, }, }, }; if (!isSourceEmpty) { addCardSubmitCard.Body.Add(new AdaptiveColumnSet() { Spacing = AdaptiveSpacing.None, Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Width = "1", Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Size = AdaptiveTextSize.Medium, Wrap = true, Text = localizer.GetString("AddNoteSourceSubheading"), Weight = AdaptiveTextWeight.Bolder, }, }, }, new AdaptiveColumn { Width = "4", Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Size = AdaptiveTextSize.Medium, Wrap = true, Text = personalGoalNoteDetail.SourceName, }, }, }, }, }); } addCardSubmitCard.Body.Add(new AdaptiveColumnSet() { Spacing = AdaptiveSpacing.None, Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Width = "1", Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Size = AdaptiveTextSize.Medium, Wrap = true, Text = localizer.GetString("AddNoteDateSubheading"), Weight = AdaptiveTextWeight.Bolder, }, }, }, new AdaptiveColumn { Width = "4", Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Size = AdaptiveTextSize.Medium, Wrap = true, Text = CardHelper.FormatDateStringToAdaptiveCardDateFormat(DateTime.Now.ToString(CultureInfo.CurrentCulture)), }, }, }, }, }); addCardSubmitCard.Actions = new List <AdaptiveAction> { new AdaptiveSubmitAction { Title = localizer.GetString("AddNoteEditButtonText"), Data = new AdaptiveSubmitActionData { MsTeams = new TaskModuleAction( localizer.GetString("AddNoteEditButtonText"), new { data = JsonConvert.SerializeObject(new AdaptiveSubmitActionData { AdaptiveActionType = Constants.EditNoteCommand, PersonalGoalNoteId = personalGoalNoteDetail.PersonalGoalNoteId, PersonalGoalId = personalGoalNoteDetail.PersonalGoalId, GoalNoteId = personalGoalNoteDetail.AdaptiveCardActivityId, }), }), }, }, }; return(new Attachment { ContentType = AdaptiveCard.ContentType, Content = addCardSubmitCard, }); }