Ejemplo n.º 1
0
 /// <summary>
 /// The main point of this is to set the recipient Id, service URL, and conversation Id for the send parameters.
 /// </summary>
 /// <param name="getSendNotificationParamsResponse">The send notification parameters to be updated.</param>
 /// <param name="incomingTeamDataEntity">The incoming team data entity.</param>
 private void SetParamsForTeamRecipient(
     GetSendNotificationParamsResponse getSendNotificationParamsResponse,
     TeamDataEntity incomingTeamDataEntity)
 {
     // All necessary parameters will always be set in the incoming team data.
     getSendNotificationParamsResponse.RecipientId    = incomingTeamDataEntity.TeamId;
     getSendNotificationParamsResponse.ServiceUrl     = incomingTeamDataEntity.ServiceUrl;
     getSendNotificationParamsResponse.ConversationId = incomingTeamDataEntity.TeamId;
 }
Ejemplo n.º 2
0
        /// <summary>
        /// The service used to fetch or generate the necessary parameters for sending the notification.
        /// </summary>
        /// <param name="messageContent">The message content of the send queue message.</param>
        /// <param name="log">The logger.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public async Task <GetSendNotificationParamsResponse> GetSendNotificationParamsAsync(
            SendQueueMessageContent messageContent,
            ILogger log)
        {
            // Fetch the current sending notification. This is where the data about what is being sent is stored.
            var activeNotificationEntity = await this.sendingNotificationDataRepository.GetAsync(
                NotificationDataTableNames.SendingNotificationsPartition,
                messageContent.NotificationId);

            // Store the content for the notification that is to be sent.
            var getSendNotificationParamsResponse = new GetSendNotificationParamsResponse
            {
                NotificationContent     = activeNotificationEntity.Content,
                ForceCloseAzureFunction = false,
            };

            // Depending on the recipient type and the data in the send queue message,
            // fetch or generate the recipient Id, service URL, and conversation Id to be used for
            // sending the notification.
            switch (messageContent.RecipientData.RecipientType)
            {
            case RecipientDataType.User:
                await this.SetParamsForUserRecipientAsync(
                    getSendNotificationParamsResponse,
                    messageContent.RecipientData.UserData,
                    messageContent,
                    log);

                break;

            case RecipientDataType.Team:
                this.SetParamsForTeamRecipient(
                    getSendNotificationParamsResponse,
                    messageContent.RecipientData.TeamData);
                break;

            default:
                throw new ArgumentException($"Invalid recipient type: {messageContent.RecipientData.RecipientType}");
            }

            return(getSendNotificationParamsResponse);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// The main point of this is to set the recipient Id, service URL, and conversation Id for the send parameters.
        /// The recipient Id and service URL is always set in the incoming user data.
        ///
        /// For the conversation Id there are three possibilities:
        ///     Sending to all users - the conversationId is present in the incoming user data.
        ///     Sending to a team's members - the conversation Id was already stored for that user in the User Data table.
        ///         For this scenario, the incoming user data is stored back into the User Data table
        ///         because it may hold more information that was not present in the User Data table originally,
        ///         such as name
        ///     Sending to a team's members - the conversation Id was not stored for that user in the User Data table.
        /// </summary>
        /// <param name="getSendNotificationParamsResponse">The send notification parameters to be updated.</param>
        /// <param name="incomingUserDataEntity">The incoming user data entity.</param>
        /// <param name="messageContent">The queue message content.</param>
        /// <param name="log">The logger.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        private async Task SetParamsForUserRecipientAsync(
            GetSendNotificationParamsResponse getSendNotificationParamsResponse,
            UserDataEntity incomingUserDataEntity,
            SendQueueMessageContent messageContent,
            ILogger log)
        {
            try
            {
                // The AAD Id and service URL will always be set in the incoming user data.
                getSendNotificationParamsResponse.RecipientId = incomingUserDataEntity.AadId;
                getSendNotificationParamsResponse.ServiceUrl  = incomingUserDataEntity.ServiceUrl;

                /*
                 * The rest of the logic in this method is primarily for fetching/generating the conversation Id.
                 *
                 */

                // Case where the conversation Id is included in the incoming user data.
                if (!string.IsNullOrWhiteSpace(incomingUserDataEntity.ConversationId))
                {
                    getSendNotificationParamsResponse.ConversationId = incomingUserDataEntity.ConversationId;

                    return;
                }

                // Case where the conversation Id is not included in the incoming user data (the conversation Id may or
                // may not be stored in the User Data table).

                // Fetch the data for that user from the User Data table to see if they are present.
                // It is possible that that user's data has not been stored in the User Data table.
                // If this is the case, then the conversation will need to be created for that user.
                var storedUserDataEntity = await this.userDataRepository.GetAsync(
                    UserDataTableNames.UserDataPartition,
                    incomingUserDataEntity.AadId);

                // These blocks are used to determine the conversation Id to be used when sending the notification.
                string conversationId = null;
                if (storedUserDataEntity != null)
                {
                    /*
                     * Case where the user's data was stored in the User Data table so their conversation Id is
                     * known. Update the user's entry in the User Data table, though, with the incoming user data
                     * because the incoming user data may hold more information than what is already stored for
                     * that user, such as their name.
                     */

                    // Set the conversation Id to be used when sending the notification.
                    conversationId = storedUserDataEntity.ConversationId;

                    // Set the conversation Id to ensure it is not removed from the User Data table on the update.
                    incomingUserDataEntity.ConversationId = storedUserDataEntity.ConversationId;

                    incomingUserDataEntity.PartitionKey = UserDataTableNames.UserDataPartition;
                    incomingUserDataEntity.RowKey       = incomingUserDataEntity.AadId;

                    await this.userDataRepository.InsertOrMergeAsync(incomingUserDataEntity);
                }
                else
                {
                    /*
                     * Falling into this block means that the user data and the conversation Id for this user
                     * has not been stored. Because of this, the conversation needs to be created and that
                     * conversation Id needs to be stored for that user for later use.
                     */

                    var createConversationResponse = await this.createUserConversationService.CreateConversationAsync(
                        userDataEntity : incomingUserDataEntity,
                        maxNumberOfAttempts : this.maxNumberOfAttempts,
                        log : log);

                    if (createConversationResponse.ResultType == CreateUserConversationResultType.Succeeded)
                    {
                        // Set the conversation Id to be used when sending the notification.
                        conversationId = createConversationResponse.ConversationId;

                        // Store the newly created conversation Id so the create conversation
                        // request will not need to be made again for the user for future notifications.
                        incomingUserDataEntity.ConversationId = createConversationResponse.ConversationId;

                        incomingUserDataEntity.PartitionKey = UserDataTableNames.UserDataPartition;
                        incomingUserDataEntity.RowKey       = incomingUserDataEntity.AadId;

                        await this.userDataRepository.InsertOrMergeAsync(incomingUserDataEntity);
                    }
                    else if (createConversationResponse.ResultType == CreateUserConversationResultType.Throttled)
                    {
                        // If the request was attempted the maximum number of allowed attempts and received
                        // all throttling responses, then set the overall delay time for the system so all
                        // other calls will be delayed and add the message back to the queue with a delay to be
                        // attempted later.
                        await this.delaySendingNotificationService.DelaySendingNotificationAsync(
                            sendRetryDelayNumberOfSeconds : this.sendRetryDelayNumberOfSeconds,
                            sendQueueMessageContent : messageContent,
                            log : log);

                        // Signal that the Azure Function should be completed to be attempted later.
                        getSendNotificationParamsResponse.ForceCloseAzureFunction = true;
                        return;
                    }
                    else if (createConversationResponse.ResultType == CreateUserConversationResultType.Failed)
                    {
                        // If the create conversation call failed, save the results, do not attempt the
                        // request again, and end the function.
                        await this.manageResultDataService.ProcessResultDataAsync(
                            notificationId : messageContent.NotificationId,
                            recipientId : incomingUserDataEntity.AadId,
                            totalNumberOfSendThrottles : 0,
                            isStatusCodeFromCreateConversation : true,
                            statusCode : createConversationResponse.StatusCode,
                            allSendStatusCodes : string.Empty,
                            errorMessage : createConversationResponse.ErrorMessage,
                            log : log);

                        // Signal that the Azure Function should be completed.
                        getSendNotificationParamsResponse.ForceCloseAzureFunction = true;
                        return;
                    }
                }

                // Set the conversation Id to be used when sending the notification.
                getSendNotificationParamsResponse.ConversationId = conversationId;
            }
            catch (Exception e)
            {
                var errorMessage = $"{e.GetType()}: {e.Message}";
                log.LogError(e, $"ERROR: {errorMessage}");

                throw;
            }
        }