Esempio n. 1
0
        public async Task <ActionResult> CreateOrUpdateAsync(string offerName, string planName, string meterName, [FromBody] CustomMeterDimension customMeterDimension)
        {
            AADAuthHelper.VerifyUserAccess(this.HttpContext, _logger, true);

            if (customMeterDimension == null)
            {
                throw new LunaBadRequestUserException(LoggingUtils.ComposePayloadNotProvidedErrorMessage(nameof(customMeterDimension)), UserErrorCode.PayloadNotProvided);
            }

            if (!planName.Equals(customMeterDimension.PlanName))
            {
                throw new LunaBadRequestUserException(LoggingUtils.ComposeNameMismatchErrorMessage(nameof(offerName)), UserErrorCode.NameMismatch);
            }

            if (!meterName.Equals(customMeterDimension.MeterName))
            {
                throw new LunaBadRequestUserException(LoggingUtils.ComposeNameMismatchErrorMessage(nameof(meterName)), UserErrorCode.NameMismatch);
            }

            if (await _customMeterDimensionService.ExistsAsync(offerName, planName, meterName))
            {
                await _customMeterDimensionService.UpdateAsync(offerName, planName, meterName, customMeterDimension);

                return(Ok(customMeterDimension));
            }
            else
            {
                await _customMeterDimensionService.CreateAsync(offerName, planName, meterName, customMeterDimension);

                return(CreatedAtRoute(nameof(GetAsync) + nameof(CustomMeterDimension),
                                      new { offerName = offerName, planName = planName, meterName = meterName },
                                      customMeterDimension));;
            }
        }
Esempio n. 2
0
        private async Task ReportBatchMeterEventsInternal(Offer offer, CustomMeter meter, DateTime effectiveStartTime)
        {
            _logger.LogInformation($"Query and report meter event {meter.MeterName} starting {effectiveStartTime}.");

            DateTime effectiveEndTime = effectiveStartTime.AddHours(1);

            var telemetryDataConnector = await _telemetryDataConnectorService.GetAsync(meter.TelemetryDataConnectorName);

            ITelemetryDataConnector connector = _telemetryConnectionManager.CreateTelemetryDataConnector(
                telemetryDataConnector.Type,
                telemetryDataConnector.Configuration);

            IEnumerable <Usage> meterEvents = await connector.GetMeterEventsByHour(effectiveStartTime, meter.TelemetryQuery);

            // Get the billable meter events
            List <Usage> billableMeterEvents = new List <Usage>();

            foreach (var meterEvent in meterEvents)
            {
                // Send the meter event only if:
                // 1. Subscription exists
                // 2. Subscription is in a plan using the current custom meter
                // 3. Meter usage is enabled
                // 4. The meter usage is not reported
                // 5. There's no error or error happened after the effective start time (we don't skip error).
                Guid subscriptionId;
                if (!Guid.TryParse(meterEvent.ResourceId, out subscriptionId))
                {
                    _logger.LogWarning($"ResourceId {meterEvent.ResourceId} is not a valid subscription. The data type should be GUID.");
                    continue;
                }

                if (!await _subscriptionService.ExistsAsync(subscriptionId))
                {
                    _logger.LogWarning($"The subscription {subscriptionId} doesn't exist. Will not report the meter event {meterEvent.Dimension}.");
                    continue;
                }

                if (!await _subscriptionCustomMeterUsageService.ExistsAsync(subscriptionId, meter.MeterName))
                {
                    _logger.LogWarning($"The subscription usage record with {subscriptionId} and meter name {meter.MeterName} doesn't exist. Will not report the meter event {meterEvent.Dimension}.");
                    continue;
                }

                var subscription = await _subscriptionService.GetAsync(subscriptionId);

                var meterUsage = await _subscriptionCustomMeterUsageService.GetAsync(subscriptionId, meter.MeterName);

                if (await _customMeterDimensionService.ExistsAsync(offer.OfferName, subscription.PlanName, meter.MeterName) &&
                    meterUsage.IsEnabled &&
                    meterUsage.LastUpdatedTime < effectiveEndTime &&
                    (meterUsage.LastErrorReportedTime == DateTime.MinValue || meterUsage.LastErrorReportedTime >= effectiveStartTime))
                {
                    meterEvent.Dimension = meter.MeterName;
                    meterEvent.PlanId    = subscription.PlanName;
                    billableMeterEvents.Add(meterEvent);
                }
            }

            CustomMeteringRequestResult requestResult = new CustomMeteringBatchSuccessResult();

            if (billableMeterEvents.Count > 0)
            {
                requestResult = await _customMeteringClient.RecordBatchUsageAsync(
                    Guid.NewGuid(),
                    Guid.NewGuid(),
                    billableMeterEvents,
                    default);
            }
            else
            {
                // Create an empty result
                ((CustomMeteringBatchSuccessResult)requestResult).Success = true;
                ((CustomMeteringBatchSuccessResult)requestResult).Result  = new List <CustomMeteringSuccessResult>();
            }

            if (requestResult.Success)
            {
                CustomMeteringBatchSuccessResult batchResult = (CustomMeteringBatchSuccessResult)requestResult;

                foreach (var result in batchResult.Result)
                {
                    var subscriptionId         = Guid.Parse(result.ResourceId);
                    var subscriptionMeterUsage = await _subscriptionCustomMeterUsageService.GetAsync(subscriptionId, meter.MeterName);

                    if (result.Status.Equals(nameof(CustomMeterEventStatus.Accepted), StringComparison.InvariantCultureIgnoreCase) ||
                        result.Status.Equals(nameof(CustomMeterEventStatus.Duplicate), StringComparison.InvariantCultureIgnoreCase))
                    {
                        _logger.LogWarning($"Meter event {result.Dimension} for subscription {result.ResourceId} at {result.EffectiveStartTime} reported at {DateTime.Now}, with status {result.Status}.");
                        subscriptionMeterUsage.LastUpdatedTime       = effectiveEndTime;
                        subscriptionMeterUsage.LastErrorReportedTime = DateTime.MinValue;

                        // Always record the reported meter event to Azure table if it is duplicate.
                        var tableEntity = new CustomMeteringAzureTableEntity(
                            result.Status.Equals(nameof(CustomMeterEventStatus.Accepted), StringComparison.InvariantCultureIgnoreCase) ?
                            result : result.Error.AdditionalInfo.AcceptedMessage);

                        await _storageUtility.InsertTableEntity(REPORTED_METER_EVENT_TABLE_NAME, tableEntity);

                        if (effectiveEndTime > subscriptionMeterUsage.UnsubscribedTime)
                        {
                            _logger.LogInformation($"Disabled meter usage for meter {meter.MeterName} subscription {subscriptionId} at {effectiveEndTime}.");
                            subscriptionMeterUsage.IsEnabled    = false;
                            subscriptionMeterUsage.DisabledTime = effectiveEndTime;
                        }
                    }
                    else if (result.Status.Equals(nameof(CustomMeterEventStatus.Expired), StringComparison.InvariantCultureIgnoreCase))
                    {
                        // If the meter event is expired, record a warning and move on
                        _logger.LogWarning($"Meter event {result.Dimension} for subscription {result.ResourceId} at {result.EffectiveStartTime} expired at {DateTime.Now}.");
                        subscriptionMeterUsage.LastUpdatedTime       = effectiveEndTime;
                        subscriptionMeterUsage.LastErrorReportedTime = DateTime.MinValue;

                        await _storageUtility.InsertTableEntity(EXPIRED_METER_EVENT_TABLE_NAME,
                                                                new CustomMeteringAzureTableEntity(result));

                        if (effectiveEndTime > subscriptionMeterUsage.UnsubscribedTime)
                        {
                            _logger.LogInformation($"Disabled meter usage for meter {meter.MeterName} subscription {subscriptionId} at {effectiveEndTime}.");
                            subscriptionMeterUsage.IsEnabled    = false;
                            subscriptionMeterUsage.DisabledTime = effectiveEndTime;
                        }
                    }
                    else
                    {
                        _logger.LogError($"Meter event {result.Dimension} for subscription {result.ResourceId} at {result.EffectiveStartTime} failed to report at {DateTime.Now}.");
                        subscriptionMeterUsage.LastErrorReportedTime = effectiveEndTime;
                        string errorMessage = ComposeErrorMessage(result.Error);
                        subscriptionMeterUsage.LastError = $"Meter event failed with error {result.Status}. Details: {errorMessage}.";

                        await _storageUtility.InsertTableEntity(FAILED_METER_EVENT_TABLE_NAME,
                                                                new FailedCustomMeteringAzureTableEntity(result, errorMessage));

                        // If the meter event request failed with ResourceNotFound error, it could be caused by the time different between
                        // end user cancel the subscription and SaaS service send the cancel request by calling the notification webhook
                        if (effectiveEndTime > subscriptionMeterUsage.UnsubscribedTime && result.Status.Equals("ResourceNotFound"))
                        {
                            _logger.LogInformation($"Disabled meter usage for meter {meter.MeterName} subscription {subscriptionId} at {effectiveEndTime}.");
                            subscriptionMeterUsage.IsEnabled    = false;
                            subscriptionMeterUsage.DisabledTime = effectiveEndTime;
                        }
                    }
                    await _subscriptionCustomMeterUsageService.UpdateAsync(subscriptionId, meter.MeterName, subscriptionMeterUsage);
                }

                await _subscriptionCustomMeterUsageService.UpdateLastUpdatedTimeForUnreportedSubscriptions(offer.OfferName, meter.MeterName, effectiveEndTime);

                _logger.LogInformation($"Completed reporting custom meter events {meter.MeterName} starting {effectiveStartTime}.");
            }
            else
            {
                _logger.LogWarning($"Failed to send the batch meter event. Response code: {requestResult.Code}");
            }
        }