/// <summary>
        /// Transition to next state and update the subscription in database.
        /// </summary>
        /// <param name="subscription">The subscription</param>
        /// <param name="targetState">The target state</param>
        /// <param name="callerName">The caller name</param>
        /// <returns></returns>
        private async Task <Subscription> TransitToNextState(Subscription subscription,
                                                             ProvisioningState targetState,
                                                             [CallerMemberName] string callerName = "")
        {
            MethodInfo method = typeof(ProvisioningService).GetMethod(callerName);

            OutputStatesAttribute attribute = (OutputStatesAttribute)Attribute.GetCustomAttribute(method, typeof(OutputStatesAttribute));

            if (!attribute.InputStates.Contains(targetState.ToString()))
            {
                throw new LunaProvisioningException(
                          $"Cannot transit to {targetState.ToString()} state from method {callerName}.",
                          false);
            }

            //reset retry count
            subscription.RetryCount         = 0;
            subscription.ProvisioningStatus = targetState.ToString();
            subscription.LastUpdatedTime    = DateTime.UtcNow;
            _context.Subscriptions.Update(subscription);
            await _context._SaveChangesAsync();

            return(subscription);
        }
        /// <summary>
        /// Handle the provisioning exceptions
        /// </summary>
        /// <param name="subscription">The subscription</param>
        /// <param name="ex">The exception</param>
        /// <param name="callerName">The caller name</param>
        /// <returns>The updated subscription</returns>
        private async Task <Subscription> HandleExceptions(Subscription subscription, Exception ex,
                                                           [CallerMemberName] string callerName = "")
        {
            _logger.LogError(ex, ex.Message);

            // Transit to error state if:
            // 1. It is not a LunaException
            // 2. It is not retry-able
            // 3. The retry count exceeded the threshold
            if (ex.GetType() != typeof(LunaServerException) || !((LunaServerException)ex).IsRetryable || subscription.RetryCount >= _maxRetry)
            {
                ProvisioningState errorState;
                switch (subscription.ProvisioningStatus)
                {
                case nameof(ProvisioningState.NotificationPending):
                    errorState = ProvisioningState.NotificationFailed;
                    break;

                case nameof(ProvisioningState.ArmTemplatePending):
                case nameof(ProvisioningState.ArmTemplateRunning):
                    errorState = ProvisioningState.ArmTemplateFailed;
                    break;

                case nameof(ProvisioningState.ProvisioningPending):
                case nameof(ProvisioningState.DeployResourceGroupRunning):
                    errorState = ProvisioningState.DeployResourceGroupFailed;
                    break;

                case nameof(ProvisioningState.WebhookPending):
                    errorState = ProvisioningState.WebhookFailed;
                    break;

                default:
                    errorState = ProvisioningState.NotSpecified;
                    break;
                }

                // Transit to error state and reset retry count
                subscription.ProvisioningStatus = errorState.ToString();
                subscription.RetryCount         = 0;
            }
            else
            {
                // Failback to an earlier state if it is specified in the LunaProvisioningException.
                // Otherwise, stay in the same state and retry

                if (ex.GetType() == typeof(LunaProvisioningException) &&
                    ((LunaProvisioningException)ex).FailbackState != ProvisioningState.NotSpecified)
                {
                    var failbackState = ((LunaProvisioningException)ex).FailbackState;
                    subscription.ProvisioningStatus = failbackState.ToString();
                }
            }

            MethodInfo method = typeof(ProvisioningService).GetMethod(callerName);

            OutputStatesAttribute attribute = (OutputStatesAttribute)Attribute.GetCustomAttribute(method, typeof(OutputStatesAttribute));

            if (!attribute.InputStates.Contains(subscription.ProvisioningStatus.ToString()))
            {
                _logger.LogError(ex, $"Can not transit to ${subscription.ProvisioningStatus.ToString()} state from method {callerName}.");
                subscription.ProvisioningStatus = nameof(ProvisioningState.NotSpecified);
            }

            subscription.LastUpdatedTime = DateTime.UtcNow;
            subscription.LastException   = ex.Message;
            _context.Subscriptions.Update(subscription);
            await _context._SaveChangesAsync();

            return(subscription);
        }