/// <summary> /// Delete company response details data in Microsoft Azure Table storage. /// </summary> /// <param name="entity">Holds company response detail entity data.</param> /// <returns>A task that represents company response entity data is deleted.</returns> public async Task <bool> DeleteEntityAsync(CompanyResponseEntity entity) { await this.EnsureInitializedAsync(); TableOperation deleteOperation = TableOperation.Delete(entity); var result = await this.ResponsesCloudTable.ExecuteAsync(deleteOperation); return(result.HttpStatusCode == (int)HttpStatusCode.NoContent); }
/// <summary> /// Show reject adaptive card on new response request card. /// </summary> /// <param name="userRequestDetails">User request details object.</param> /// <param name="localizer">The current cultures' string localizer.</param> /// <returns>An reject card to show on new response request card.</returns> private static AdaptiveCard GetRejectCard(CompanyResponseEntity userRequestDetails, IStringLocalizer <Strings> localizer) { return(new AdaptiveCard(new AdaptiveSchemaVersion(1, 2)) { Body = new List <AdaptiveElement> { new AdaptiveContainer { Items = new List <AdaptiveElement> { new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Text = localizer.GetString("RejectToggleCardRemarkTitle"), Wrap = true, }, new AdaptiveTextInput { Id = "approvalremark", Placeholder = localizer.GetString("RejectToggleCardRemarkPlaceholder"), IsMultiline = true, MaxLength = RejectToggleCardCommentFieldMaxLimit, }, }, }, }, }, }, }, }, Actions = new List <AdaptiveAction> { new AdaptiveSubmitAction { Title = localizer.GetString("RejectToggleCardSubmitButtonTitle"), Data = new AdaptiveSubmitActionData { AdaptiveCardActions = new CardAction { Type = ActionTypes.MessageBack, Text = Constants.RejectCommand, }, ResponseId = userRequestDetails.ResponseId, ApprovalStatus = RejectedRequestStatus, }, }, }, }); }
/// <summary> /// Stores or update company response details data in Microsoft Azure Table storage. /// </summary> /// <param name="entity">Holds company response detail entity data.</param> /// <returns>A task that represents a company response data that is saved or updated.</returns> private async Task <TableResult> StoreOrUpdateEntityAsync(CompanyResponseEntity entity) { try { await this.EnsureInitializedAsync(); TableOperation addOrUpdateOperation = TableOperation.InsertOrReplace(entity); return(await this.ResponsesCloudTable.ExecuteAsync(addOrUpdateOperation)); } catch (Exception) { throw; } }
public async Task <IActionResult> DeleteAsync([FromBody] CompanyResponseEntity companyResponseEntity) { try { if (string.IsNullOrEmpty(companyResponseEntity?.UserId) || string.IsNullOrEmpty(companyResponseEntity.ResponseId)) { this.logger.LogError("Error while deleting company response details data in Microsoft Azure Table storage."); return(this.GetErrorResponse(StatusCodes.Status400BadRequest, "Error while deleting company response details data in Microsoft Azure Table storage.")); } this.RecordEvent(RecordCompanyHTTPDeleteCall, companyResponseEntity.UserId); return(this.Ok(await this.companyResponseStorageProvider.DeleteEntityAsync(companyResponseEntity))); } catch (Exception ex) { this.logger.LogError(ex, "Error while making call to company response service."); throw; } }
public async Task <IActionResult> PostAsync([FromBody] CompanyResponseEntity companyResponseEntity) { try { if (string.IsNullOrEmpty(companyResponseEntity?.UserId)) { this.logger.LogError("Error while creating company response details data in Microsoft Azure Table storage."); return(this.GetErrorResponse(StatusCodes.Status400BadRequest, "Error while creating company response details data in Microsoft Azure Table storage.")); } var claims = this.GetUserClaims(); this.RecordEvent(RecordCompanyHTTPPostCall, claims.FromId); return(this.Ok(await this.companyResponseStorageProvider.UpsertConverationStateAsync(companyResponseEntity))); } catch (Exception ex) { this.logger.LogError(ex, "Error while making call to company response service."); throw; } }
/// <summary> /// Store user suggestion to Microsoft Azure Table storage. /// </summary> /// <param name="activity">Represents activity for current turn of bot.</param> /// <param name="userSuggestionDetails">New suggestion detail.</param> /// <returns>Represent a task queued for operation.</returns> public async Task <CompanyResponseEntity> AddNewSuggestionAsync(IInvokeActivity activity, AddUserResponseRequestDetail userSuggestionDetails) { userSuggestionDetails = userSuggestionDetails ?? throw new ArgumentNullException(nameof(userSuggestionDetails)); activity = activity ?? throw new ArgumentNullException(nameof(activity)); var userResponse = new CompanyResponseEntity() { QuestionLabel = userSuggestionDetails.Label, QuestionText = userSuggestionDetails.Question, ResponseText = userSuggestionDetails.Response, ResponseId = Guid.NewGuid().ToString(), UserId = activity.From.AadObjectId, LastUpdatedDate = DateTime.UtcNow, CreatedDate = DateTime.UtcNow, ApprovalStatus = PendingRequestStatus, CreatedBy = activity.From.Name, ApprovedOrRejectedDate = DateTime.UtcNow, UserPrincipalName = userSuggestionDetails.UPN, }; await this.companyResponseStorageProvider.UpsertConverationStateAsync(userResponse); return(userResponse); }
/// <summary> /// Get new response request card to create new response. /// </summary> /// <param name="userRequestDetails">User request details object.</param> /// <param name="localizer">The current cultures' string localizer.</param> /// <param name="emptyApproveField">Show error message if SME has missed to fill any field while approving the response.</param> /// <returns>An new response request card attachment.</returns> public static Attachment GetNewResponseRequestCard(CompanyResponseEntity userRequestDetails, IStringLocalizer <Strings> localizer, bool emptyApproveField = false) { if (userRequestDetails == null) { throw new ArgumentNullException(nameof(userRequestDetails)); } AdaptiveCard card = new AdaptiveCard(new AdaptiveSchemaVersion(1, 2)) { Body = new List <AdaptiveElement> { new AdaptiveContainer { Items = new List <AdaptiveElement> { new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Text = localizer.GetString("NotificationRequestCardContentText"), Wrap = true, }, new AdaptiveTextBlock { Text = string.Format(CultureInfo.InvariantCulture, localizer.GetString("NotificationCardRequestText"), userRequestDetails.CreatedBy), Wrap = true, }, }, }, }, }, }, }, new AdaptiveContainer { Items = new List <AdaptiveElement> { new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Text = localizer.GetString("NotificationCardLabelText"), Wrap = true, }, new AdaptiveTextBlock { Text = userRequestDetails.QuestionLabel, Wrap = true, }, new AdaptiveTextBlock { Text = localizer.GetString("NotificationCardQuestion"), Wrap = true, }, new AdaptiveTextBlock { Text = userRequestDetails.QuestionText, Wrap = true, }, new AdaptiveTextBlock { Text = localizer.GetString("NotificationCardResponse"), Wrap = true, }, new AdaptiveTextBlock { Text = userRequestDetails.ResponseText, Wrap = true, }, }, Style = AdaptiveContainerStyle.Emphasis, }, }, }, }, }, new AdaptiveContainer { Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Text = localizer.GetString("ErrorMessageOnApprove"), Wrap = true, IsVisible = emptyApproveField, Color = AdaptiveTextColor.Attention, }, }, }, }, Actions = new List <AdaptiveAction> { new AdaptiveShowCardAction { Title = localizer.GetString("ApproveButtonTitle"), Card = GetApproveCard(userRequestDetails, localizer: localizer), }, new AdaptiveShowCardAction { Title = localizer.GetString("RejectButtonTitle"), Card = GetRejectCard(userRequestDetails, localizer: localizer), }, new AdaptiveOpenUrlAction { Title = string.Format(CultureInfo.InvariantCulture, localizer.GetString("ChatTextButton"), userRequestDetails.CreatedBy), UrlString = $"https://teams.microsoft.com/l/chat/0/0?users={Uri.EscapeDataString(userRequestDetails.UserPrincipalName)}", }, }, }; var adaptiveCardAttachment = new Attachment() { ContentType = AdaptiveCard.ContentType, Content = card, }; return(adaptiveCardAttachment); }
/// <summary> /// Show approve adaptive card on new response request card. /// </summary> /// <param name="userRequestDetails">User request details object.</param> /// <param name="localizer">The current cultures' string localizer.</param> /// <returns>An approve card to show on new response request card.</returns> private static AdaptiveCard GetApproveCard(CompanyResponseEntity userRequestDetails, IStringLocalizer <Strings> localizer) { return(new AdaptiveCard(new AdaptiveSchemaVersion(1, 2)) { Body = new List <AdaptiveElement> { new AdaptiveContainer { Items = new List <AdaptiveElement> { new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Text = localizer.GetString("ApproveToggleCardCategoryTitle"), Wrap = true, }, new AdaptiveTextInput { Id = "updatedquestioncategory", Value = userRequestDetails.QuestionLabel, Placeholder = localizer.GetString("ApproveToggleCardLabelPlaceholder"), MaxLength = ApproveToggleCardCategoryFieldMaxLimit, }, new AdaptiveTextBlock { Text = localizer.GetString("ApproveToggleCardQuestionsTitle"), Wrap = true, }, new AdaptiveTextInput { Id = "updatedquestiontext", Value = userRequestDetails.QuestionText, Placeholder = localizer.GetString("ApproveToggleCardQuestionsPlaceholder"), MaxLength = ApproveToggleCardQuestionFieldMaxLimit, }, new AdaptiveTextBlock { Text = localizer.GetString("ApproveToggleCardQuestionsPlaceholder"), Wrap = true, HorizontalAlignment = AdaptiveHorizontalAlignment.Right, }, new AdaptiveTextBlock { Text = localizer.GetString("ApproveToggleCardResponseTitle"), Wrap = true, }, new AdaptiveTextInput { Id = "updatedresponsetext", Value = userRequestDetails.ResponseText, Placeholder = localizer.GetString("ApproveToggleCardResponsePlaceholder"), MaxLength = ApproveToggleCardResponseFieldMaxLimit, IsMultiline = true, }, }, }, }, }, }, }, }, Actions = new List <AdaptiveAction> { new AdaptiveSubmitAction { Title = localizer.GetString("ApproveToggleCardSubmitButtonTitle"), Data = new AdaptiveSubmitActionData { AdaptiveCardActions = new CardAction { Type = ActionTypes.MessageBack, Text = Constants.ApproveCommand, }, ResponseId = userRequestDetails.ResponseId, ApprovalStatus = ApprovedRequestStatus, }, }, }, }); }
/// <summary> /// Get refreshed card for rejected request. /// </summary> /// <param name="userRequestDetails">User request details object.</param> /// <param name="rejectedBy">User name who approved the request.</param> /// <param name="localizer">The current cultures' string localizer.</param> /// <returns>An attachment.</returns> public static Attachment GetRefreshedCardForRejectedRequest(CompanyResponseEntity userRequestDetails, string rejectedBy, IStringLocalizer <Strings> localizer) { if (userRequestDetails == null) { throw new ArgumentNullException(nameof(userRequestDetails)); } bool showRemarkField = !string.IsNullOrEmpty(userRequestDetails.ApprovalRemark); var formattedDateTime = userRequestDetails.ApprovedOrRejectedDate.ToString(Constants.Rfc3339DateTimeFormat, CultureInfo.InvariantCulture); string dateString = string.Format(CultureInfo.InvariantCulture, localizer.GetString("DateFormat"), "{{DATE(" + formattedDateTime + ", COMPACT)}}", "{{TIME(" + formattedDateTime + ")}}"); AdaptiveCard card = new AdaptiveCard(new AdaptiveSchemaVersion(1, 2)) { Body = new List <AdaptiveElement> { new AdaptiveContainer { Items = new List <AdaptiveElement> { new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Text = localizer.GetString("NotificationCardRequestReject"), Wrap = true, }, new AdaptiveTextBlock { Text = string.Format(CultureInfo.InvariantCulture, localizer.GetString("RefreshedNotificationCardRequestText"), userRequestDetails.CreatedBy), Wrap = true, }, }, }, }, }, }, }, new AdaptiveContainer { Items = new List <AdaptiveElement> { new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Text = localizer.GetString("NotificationCardLabelText"), Wrap = true, }, new AdaptiveTextBlock { Text = userRequestDetails.QuestionLabel, Wrap = true, }, new AdaptiveTextBlock { Text = localizer.GetString("NotificationCardQuestion"), Wrap = true, }, new AdaptiveTextBlock { Text = userRequestDetails.QuestionText, Wrap = true, }, new AdaptiveTextBlock { Text = localizer.GetString("NotificationCardResponse"), Wrap = true, }, new AdaptiveTextBlock { Text = userRequestDetails.ResponseText, Wrap = true, }, }, Style = AdaptiveContainerStyle.Emphasis, }, }, }, }, }, new AdaptiveContainer { Items = new List <AdaptiveElement> { new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Text = localizer.GetString("NotificationCardRemark"), Wrap = true, IsVisible = showRemarkField, }, new AdaptiveTextBlock { Text = userRequestDetails.ApprovalRemark, Wrap = true, IsVisible = showRemarkField, }, new AdaptiveTextBlock { Text = string.Format(CultureInfo.InvariantCulture, localizer.GetString("RejectedAdminCardLabelText"), dateString, rejectedBy), Wrap = true, }, }, }, }, }, }, }, }, }; var adaptiveCardAttachment = new Attachment() { ContentType = AdaptiveCard.ContentType, Content = card, }; return(adaptiveCardAttachment); }
/// <summary> /// Stores or update company response data in Microsoft Azure Table storage. /// </summary> /// <param name="companyResponseEntity">Holds company response detail entity data.</param> /// <returns>A task that represents company response entity data is saved or updated.</returns> public async Task <bool> UpsertConverationStateAsync(CompanyResponseEntity companyResponseEntity) { var result = await this.StoreOrUpdateEntityAsync(companyResponseEntity); return(result.HttpStatusCode == (int)HttpStatusCode.NoContent); }
/// <summary> /// Handle message activity in channel. /// </summary> /// <param name="message">A message in a conversation.</param> /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param> /// <param name="cancellationToken">Propagates notification that operations should be canceled.</param> /// <returns>A task that represents the work queued to execute.</returns> private async Task OnMessageActivityInChannelAsync( IMessageActivity message, ITurnContext <IMessageActivity> turnContext, CancellationToken cancellationToken) { try { if (message.Value == null) { await turnContext.SendActivityAsync(this.localizer.GetString("ErrorWhenMessageInChannel")); return; } IMessageActivity userNotification = null; CompanyResponseEntity companyResponseEntity = null; var cardPostedData = ((JObject)message.Value).ToObject <AdaptiveSubmitActionData>(); var text = cardPostedData.AdaptiveCardActions.Text; var activity = turnContext.Activity; switch (text) { case Constants.ApproveCommand: if (string.IsNullOrEmpty(cardPostedData.UpdatedQuestionCategory) || string.IsNullOrEmpty(cardPostedData.UpdatedQuestionText) || string.IsNullOrEmpty(cardPostedData.UpdatedResponseText)) { companyResponseEntity = this.companyResponseStorageProvider.GetCompanyResponseEntityAsync(cardPostedData.ResponseId).GetAwaiter().GetResult(); var attachment = AdminCard.GetNewResponseRequestCard(companyResponseEntity, localizer: this.localizer, emptyApproveField: true); await AdaptiveCardHelper.RefreshCardAsync(turnContext, companyResponseEntity.ActivityId, attachment); return; } companyResponseEntity = this.companyStorageHelper.AddApprovedData(cardPostedData, activity.From.Name, activity.From.AadObjectId); var approveRequestResult = this.companyResponseStorageProvider.UpsertConverationStateAsync(companyResponseEntity).GetAwaiter().GetResult(); if (approveRequestResult) { // Refresh the approved card in channel. var attachment = AdminCard.GetRefreshedCardForApprovedRequest(companyResponseEntity, activity.From.Name, localizer: this.localizer); await AdaptiveCardHelper.RefreshCardAsync(turnContext, companyResponseEntity.ActivityId, attachment); // Get user notification attachment and send it to user for approved request. userNotification = MessageFactory.Attachment(UserCard.GetNotificationCardForApprovedRequest(companyResponseEntity, localizer: this.localizer)); var result = await this.conversationStorageProvider.GetConversationEntityAsync(companyResponseEntity.UserId); if (result != null) { await AdaptiveCardHelper.SendNotificationCardAsync(turnContext, userNotification, result.ConversationId, cancellationToken); // Tracking for number of requests approved. this.RecordEvent(ApprovedRequestEventName, turnContext); } else { this.logger.LogInformation("Unable to send notification card for approved request because conversation id is null."); } } else { this.logger.LogInformation("Unable to approve the request."); } break; case Constants.RejectCommand: companyResponseEntity = this.companyStorageHelper.AddRejectedData(cardPostedData, activity.From.Name, activity.From.AadObjectId); var rejectRequestResult = this.companyResponseStorageProvider.UpsertConverationStateAsync(companyResponseEntity).GetAwaiter().GetResult(); if (rejectRequestResult) { // Get user notification rejected card attachment. var attachment = AdminCard.GetRefreshedCardForRejectedRequest(companyResponseEntity, activity.From.Name, localizer: this.localizer); await AdaptiveCardHelper.RefreshCardAsync(turnContext, companyResponseEntity.ActivityId, attachment); // Send end user notification for approved request. userNotification = MessageFactory.Attachment(UserCard.GetNotificationCardForRejectedRequest(companyResponseEntity, localizer: this.localizer)); var result = await this.conversationStorageProvider.GetConversationEntityAsync(companyResponseEntity.UserId); if (result != null) { await AdaptiveCardHelper.SendNotificationCardAsync(turnContext, userNotification, result.ConversationId, cancellationToken); // Tracking for number of requests rejected. this.RecordEvent(RejectedRequestEventName, turnContext); } else { this.logger.LogInformation("Unable to send notification card for rejected request because conversation id is null."); } } else { this.logger.LogInformation("Unable to reject the request."); } return; default: this.logger.LogInformation("Unrecognized input in channel"); break; } } catch (Exception ex) { this.logger.LogError(ex, $"Error processing message: {ex.Message}", SeverityLevel.Error); throw; } }
/// <summary> /// Get notification card for approved request. /// </summary> /// <param name="userRequestDetails">User request details object.</param> /// <param name="localizer">The current cultures' string localizer.</param> /// <returns>An attachment card for approved request.</returns> public static Attachment GetNotificationCardForApprovedRequest(CompanyResponseEntity userRequestDetails, IStringLocalizer <Strings> localizer) { if (userRequestDetails == null) { throw new ArgumentNullException(nameof(userRequestDetails)); } var formattedDateTime = userRequestDetails.ApprovedOrRejectedDate.ToString(Constants.Rfc3339DateTimeFormat, CultureInfo.InvariantCulture); string dateString = string.Format(CultureInfo.InvariantCulture, localizer.GetString("DateFormat"), "{{DATE(" + formattedDateTime + ", COMPACT)}}", "{{TIME(" + formattedDateTime + ")}}"); AdaptiveCard card = new AdaptiveCard(new AdaptiveSchemaVersion(1, 2)) { Body = new List <AdaptiveElement> { new AdaptiveContainer { Items = new List <AdaptiveElement> { new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Text = localizer.GetString("NotificationCardApprovedOrRejectedTitle"), Wrap = true, }, new AdaptiveTextBlock { Text = string.Format(CultureInfo.InvariantCulture, localizer.GetString("NotificationCardStatusText"), localizer.GetString("ApprovedRequestStatusText")), Wrap = true, Color = AdaptiveTextColor.Good, }, }, }, }, }, }, }, new AdaptiveContainer { Items = new List <AdaptiveElement> { new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Text = localizer.GetString("NotificationCardLabelText"), Wrap = true, }, new AdaptiveTextBlock { Text = userRequestDetails.QuestionLabel, Wrap = true, }, new AdaptiveTextBlock { Text = localizer.GetString("NotificationCardQuestion"), Wrap = true, }, new AdaptiveTextBlock { Text = userRequestDetails.QuestionText, Wrap = true, }, new AdaptiveTextBlock { Text = localizer.GetString("NotificationCardResponse"), Wrap = true, }, new AdaptiveTextBlock { Text = userRequestDetails.ResponseText, Wrap = true, }, }, Style = AdaptiveContainerStyle.Emphasis, }, }, }, }, }, new AdaptiveContainer { Items = new List <AdaptiveElement> { new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Text = string.Format(CultureInfo.InvariantCulture, localizer.GetString("ApprovedCardLabelText"), dateString), Wrap = true, }, }, }, }, }, }, }, }, }; var adaptiveCardAttachment = new Attachment() { ContentType = AdaptiveCard.ContentType, Content = card, }; return(adaptiveCardAttachment); }