Ejemplo n.º 1
0
        public async void CallLogicApp(IReadOnlyList <Document> events)
        {
            try
            {
                // Have the HttpClient factory create a new client instance.
                var httpClient = _httpClientFactory.CreateClient("LogicAppClient");

                // Create the payload to send to the Logic App.
                foreach (var e in events)
                {
                    var payload = new LogicAppAlert
                    {
                        data           = JsonConvert.SerializeObject(e),
                        recipientEmail = Environment.GetEnvironmentVariable("RecipientEmail")
                    };

                    var postBody = JsonConvert.SerializeObject(payload);

                    var httpResult = await httpClient.PostAsync(Environment.GetEnvironmentVariable("LogicAppUrl"), new StringContent(postBody, Encoding.UTF8, "application/json"));
                }
            }
            catch (Exception ex)
            {
                log.LogError(ex.Message);
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Adds alert messages to an Azure Storage queue. This allows alerts to be sent as a summary
        /// of events within a given period of time, as defined in the <see cref="Settings"/>.
        /// </summary>
        /// <param name="alert">The alert to queue.</param>
        /// <returns></returns>
        protected async Task QueueTripAlert(LogicAppAlert alert)
        {
            // Use the QueueResolver to retrieve a reference to the Azure Storage queue.
            var alertsQueue = _queueResolver.GetQueue(WellKnown.StorageQueues.AlertQueueName);

            // Create a message and add it to the queue.
            var message = new CloudQueueMessage(JsonConvert.SerializeObject(alert));
            await alertsQueue.AddMessageAsync(message);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Uses the Logic App's HTTP trigger to send the alert.
        /// </summary>
        /// <param name="alert"></param>
        /// <returns></returns>
        protected async Task SendAlertToLogicApp(LogicAppAlert alert)
        {
            // Have the HttpClient factory create a new client instance.
            var httpClient = _httpClientFactory.CreateClient(NamedHttpClients.LogicAppClient);

            var postBody = JsonConvert.SerializeObject(alert);

            // Post the alert to the Logic App.
            await httpClient.PostAsync(Environment.GetEnvironmentVariable("LogicAppUrl"), new StringContent(postBody, Encoding.UTF8, "application/json"));
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Triggers the Logic App through the LogicAppUrl environment variable/app setting
        /// by instantiating a new <see cref="HttpClient"/> instance from the
        /// <see cref="HttpClientFactory"/> and POSTing payload data, including the
        /// <see cref="Trip"/> information, <see cref="averageRefrigerationUnitTemp"/>
        /// aggregate value, and the RecipientEmail environment variable.
        /// </summary>
        /// <param name="averageRefrigerationUnitTemp">Average refrigeration unit temperature
        /// reading in the telemetry batch.</param>
        /// <returns></returns>
        public async Task SendTripAlert(double averageRefrigerationUnitTemp)
        {
            var settings = await GetSettingsFromDatabase();

            // If the user has indicated that they want alerts and they have specified a recipient email address, send the alert as needed.
            if (settings != null && !string.IsNullOrWhiteSpace(settings.recipientEmailAddress) && settings.SendAlerts)
            {
                // Only send or queue the alert if the trip status type is enabled in the alert settings.
                if (settings.sendTripStartedAlerts && _trip.status == WellKnown.Status.Active ||
                    settings.sendTripCompletedAlerts && _trip.status == WellKnown.Status.Completed ||
                    settings.sendTripDelayedAlerts && _trip.status == WellKnown.Status.Delayed)
                {
                    // Create the payload to send to the Logic App.
                    var payload = new LogicAppAlert
                    {
                        consignmentId        = _trip.consignmentId,
                        customer             = _trip.consignment.customer,
                        deliveryDueDate      = _trip.consignment.deliveryDueDate,
                        hasHighValuePackages = _trip.packages.Any(p => p.highValue),
                        id = _trip.id,
                        lastRefrigerationUnitTemperatureReading = averageRefrigerationUnitTemp,
                        location = _trip.location,
                        lowestPackageStorageTemperature = _trip.packages.Min(p => p.storageTemperature),
                        odometerBegin       = _trip.odometerBegin,
                        odometerEnd         = _trip.odometerEnd,
                        plannedTripDistance = _trip.plannedTripDistance,
                        tripStarted         = _trip.tripStarted,
                        tripEnded           = _trip.tripEnded,
                        status             = _trip.status,
                        vin                = _trip.vin,
                        temperatureSetting = _trip.temperatureSetting,
                        recipientEmail     = settings.recipientEmailAddress
                    };

                    if (string.IsNullOrWhiteSpace(settings.sendAlertInterval))
                    {
                        // The send alert interval setting is null or empty, indicating that every alert should be sent individually.
                        payload.isSummary = false;
                        // Send the alert to the Logic App.
                        await SendAlertToLogicApp(payload);
                    }
                    else
                    {
                        // The settings specify that an alert summary should be sent on a schedule. Add the alert to a queue.
                        await QueueTripAlert(payload);
                    }
                }
            }
        }
Ejemplo n.º 5
0
        public async Task TripProcessor([CosmosDBTrigger(
                                             databaseName: WellKnown.COSMOSDB_DB_NAME,
                                             collectionName: WellKnown.COSMOSDB_COLLECTION_NAME_TELEMETRY,
                                             ConnectionStringSetting = WellKnown.COSMOSDB_CONNECTIONSTRING_NAME,
                                             LeaseCollectionName = WellKnown.COSMOSDB_COLLECTION_NAME_LEASES,
                                             LeaseCollectionPrefix = WellKnown.COSMOSDB_LEASE_PREFIX_TRIPS,
                                             LeasesCollectionThroughput = LEASES_COLLECTION_THROUGHPUT,
                                             CreateLeaseCollectionIfNotExists = true,
                                             StartFromBeginning = true)] IReadOnlyList <Document> vehicleEvents,
                                        ILogger log)
        {
            log.LogInformation($"Evaluating {vehicleEvents.Count} events from Cosmos DB to optionally update Trip and Consignment metadata.");

            // Retrieve the Trip records by VIN, compare the odometer reading to the starting odometer reading to calculate miles driven,
            // and update the Trip and Consignment status and send an alert if needed once completed.
            var sendTripAlert     = false;
            var database          = WellKnown.COSMOSDB_DB_NAME;
            var metadataContainer = WellKnown.COSMOSDB_COLLECTION_NAME_METADATA;

            if (vehicleEvents.Count > 0)
            {
                foreach (var group in vehicleEvents.GroupBy(singleEvent => singleEvent.GetPropertyValue <string>(nameof(VehicleEvent.vin))))
                {
                    // TODO 3: We have grouped the events by vehicle VIN. Assign local variables to hold the VIN, get the max odometer value, and average refrigeration unit temperature.
                    // Complete: var vin = ...; var odometerHigh = ...; var averageRefrigerationUnitTemp = ...;

                    // First, retrieve the metadata Cosmos DB container reference:
                    var container = _cosmosClient.GetContainer(database, metadataContainer);

                    // Create a query, defining the partition key so we don't execute a fan-out query (saving RUs), where the entity type is a Trip and the status is not Completed, Canceled, or Inactive.
                    var query = container.GetItemLinqQueryable <Trip>(requestOptions: new QueryRequestOptions {
                        PartitionKey = new Microsoft.Azure.Cosmos.PartitionKey(vin)
                    })
                                .Where(p => p.status != WellKnown.Status.Completed &&
                                       p.status != WellKnown.Status.Canceled &&
                                       p.status != WellKnown.Status.Inactive &&
                                       p.entityType == WellKnown.EntityTypes.Trip)
                                .ToFeedIterator();

                    if (query.HasMoreResults)
                    {
                        // Only retrieve the first result.
                        var trip = (await query.ReadNextAsync()).FirstOrDefault();

                        if (trip != null)
                        {
                            // Retrieve the Consignment record.
                            var document = await container.ReadItemAsync <Consignment>(trip.consignmentId,
                                                                                       new Microsoft.Azure.Cosmos.PartitionKey(trip.consignmentId));

                            var consignment       = document.Resource;
                            var updateTrip        = false;
                            var updateConsignment = false;

                            // Calculate how far along the vehicle is for this trip.
                            var milesDriven = odometerHigh - trip.odometerBegin;
                            if (milesDriven >= trip.plannedTripDistance)
                            {
                                // The trip is completed!
                                trip.status        = WellKnown.Status.Completed;
                                trip.odometerEnd   = odometerHigh;
                                trip.tripEnded     = DateTime.UtcNow;
                                consignment.status = WellKnown.Status.Completed;

                                // Update the trip and consignment records.
                                updateTrip        = true;
                                updateConsignment = true;

                                sendTripAlert = true;
                            }
                            else
                            {
                                if (DateTime.UtcNow >= consignment.deliveryDueDate && trip.status != WellKnown.Status.Delayed)
                                {
                                    // The trip is delayed!
                                    trip.status        = WellKnown.Status.Delayed;
                                    consignment.status = WellKnown.Status.Delayed;

                                    // Update the trip and consignment records.
                                    updateTrip        = true;
                                    updateConsignment = true;

                                    sendTripAlert = true;
                                }
                            }

                            if (trip.tripStarted == null)
                            {
                                // Set the trip start date.
                                trip.tripStarted = DateTime.UtcNow;
                                // Set the trip and consignment status to Active.
                                trip.status        = WellKnown.Status.Active;
                                consignment.status = WellKnown.Status.Active;

                                updateTrip        = true;
                                updateConsignment = true;

                                sendTripAlert = true;
                            }

                            // Update the trip and consignment records.
                            // TODO 4: Complete the code to update the Trip and Consignment records.
                            // Complete: if (updateTrip) { ... } if (updateConsignment) { ... }

                            // Send a trip alert.
                            if (sendTripAlert)
                            {
                                // Have the HttpClient factory create a new client instance.
                                var httpClient = _httpClientFactory.CreateClient(WellKnown.LOGIC_APP_CLIENT);

                                // Create the payload to send to the Logic App.
                                var payload = new LogicAppAlert
                                {
                                    consignmentId        = trip.consignmentId,
                                    customer             = trip.consignment.customer,
                                    deliveryDueDate      = trip.consignment.deliveryDueDate,
                                    hasHighValuePackages = trip.packages.Any(p => p.highValue),
                                    id = trip.id,
                                    lastRefrigerationUnitTemperatureReading = averageRefrigerationUnitTemp,
                                    location = trip.location,
                                    lowestPackageStorageTemperature = trip.packages.Min(p => p.storageTemperature),
                                    odometerBegin       = trip.odometerBegin,
                                    odometerEnd         = trip.odometerEnd,
                                    plannedTripDistance = trip.plannedTripDistance,
                                    tripStarted         = trip.tripStarted,
                                    tripEnded           = trip.tripEnded,
                                    status             = trip.status,
                                    vin                = trip.vin,
                                    temperatureSetting = trip.temperatureSetting,
                                    recipientEmail     = Environment.GetEnvironmentVariable(WellKnown.RECIPIENT_EMAIL_NAME)
                                };

                                var postBody = JsonConvert.SerializeObject(payload);

                                // TODO 5: Add code to post the LogicAppAlert payload to the Logic App in JSON format.
                                // Complete: await httpClient.PostAsync( ... ));
                            }
                        }
                    }
                }
            }
        }
Ejemplo n.º 6
0
        public async Task SendAlertSummary()
        {
            var compareDate = DateTime.UtcNow;
            var settings    = await GetSettingsFromDatabase();

            // If the user has indicated that they want alerts and they have specified a recipient email address, and
            // they have specified a summary alert interval (sendAlertInterval is not empty), then retrieve the
            // AlertSummaryHistory document from the database to determine if it is time to send a new alert summary.
            if (settings != null && !string.IsNullOrWhiteSpace(settings.recipientEmailAddress) && settings.SendAlerts &&
                !string.IsNullOrWhiteSpace(settings.sendAlertInterval))
            {
                var alertSummaryHistory = new AlertSummaryHistory();
                // Retrieve the alert summary history document from the alerts container.
                var response = await _alertsContainer.ReadItemAsync <AlertSummaryHistory>(WellKnown.EntityTypes.AlertSummaryHistory,
                                                                                          new PartitionKey(WellKnown.EntityTypes.AlertSummaryHistory));

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    alertSummaryHistory = response.Resource;
                }

                // Retrieve the alert summary schedule from the defined cron notation.
                var schedule = CrontabSchedule.Parse(settings.sendAlertInterval);
                // Find the next schedule send date.
                var nextOccurrence =
                    schedule.GetNextOccurrence(alertSummaryHistory.summaryAlertLastSent ?? compareDate.AddDays(-1));

                if (nextOccurrence <= compareDate)
                {
                    // It is time to send a new alert summary.
                    _log.LogInformation($"Sending an alert summary based on next scheduled send date of {nextOccurrence}");

                    // Use the QueueResolver to retrieve a reference to the Azure Storage queue.
                    var alertsQueue = _queueResolver.GetQueue(WellKnown.StorageQueues.AlertQueueName);

                    // Fetch the queue attributes.
                    alertsQueue.FetchAttributes();

                    // Retrieve the cached approximate message count.
                    var cachedMessageCount = alertsQueue.ApproximateMessageCount;

                    _log.LogInformation($"Number of alert messages in the queue: {cachedMessageCount}");

                    if (cachedMessageCount.HasValue && cachedMessageCount.Value > 0)
                    {
                        // Set the batch size for number of messages to retrieve from the queue (max: 32).
                        var batchSize = 32;
                        // Determine how many loops we need to retrieve the messages, based on the batch size and number of messages.
                        var loops  = (int)Math.Ceiling((double)cachedMessageCount.Value / batchSize);
                        var alerts = new List <LogicAppAlert>();

                        // Loop through the batch size of messages until they are all retrieved.
                        for (var loop = 0; loop < loops; loop++)
                        {
                            foreach (var message in await alertsQueue.GetMessagesAsync(batchSize))
                            {
                                var alert = JsonConvert.DeserializeObject <LogicAppAlert>(message.AsString);
                                alerts.Add(alert);
                                // Delete the message from the queue.
                                await alertsQueue.DeleteMessageAsync(message);
                            }
                        }

                        var delayed = alerts.Where(a => a.status == WellKnown.Status.Delayed).ToList();

                        var payload = new LogicAppAlert
                        {
                            tripsStarted   = alerts.Count(a => a.status == WellKnown.Status.Active),
                            tripsCompleted = alerts.Count(a => a.status == WellKnown.Status.Completed),
                            tripsDelayed   = delayed.Count,
                            delayedVINs    = delayed.Count > 0 ? string.Join(", ", delayed.Select(d => d.vin)) : "None",
                            recipientEmail = settings.recipientEmailAddress,
                            isSummary      = true
                        };

                        // Send the summarized alert to the Logic App via its HTTP trigger.
                        await SendAlertToLogicApp(payload);

                        // Upsert (insert or update) the alert summary history to keep track of when we sent the alert.
                        alertSummaryHistory.summaryAlertLastSent = compareDate;
                        await _alertsContainer.UpsertItemAsync(alertSummaryHistory, new PartitionKey(alertSummaryHistory.id));
                    }
                }
            }
        }
Ejemplo n.º 7
0
        public async Task TripProcessor([CosmosDBTrigger(
                                             databaseName: "ContosoAuto",
                                             collectionName: "telemetry",
                                             ConnectionStringSetting = "CosmosDBConnection",
                                             LeaseCollectionName = "leases",
                                             LeaseCollectionPrefix = "trips",
                                             CreateLeaseCollectionIfNotExists = true,
                                             StartFromBeginning = true)] IReadOnlyList <Document> vehicleEvents,
                                        ILogger log)
        {
            log.LogInformation($"Evaluating {vehicleEvents.Count} events from Cosmos DB to optionally update Trip and Consignment metadata.");

            // Retrieve the Trip records by VIN, compare the odometer reading to the starting odometer reading to calculate miles driven,
            // and update the Trip and Consignment status and send an alert if needed once completed.
            var sendTripAlert     = false;
            var database          = "ContosoAuto";
            var metadataContainer = "metadata";

            if (vehicleEvents.Count > 0)
            {
                foreach (var group in vehicleEvents.GroupBy(singleEvent => singleEvent.GetPropertyValue <string>("vin")))
                {
                    var vin          = group.Key;
                    var odometerHigh = group.Max(item => item.GetPropertyValue <double>("odometer"));
                    var averageRefrigerationUnitTemp =
                        group.Average(item => item.GetPropertyValue <double>("refrigerationUnitTemp"));

                    // First, retrieve the metadata Cosmos DB container reference:
                    var container = _cosmosClient.GetContainer(database, metadataContainer);

                    // Create a query, defining the partition key so we don't execute a fan-out query (saving RUs), where the entity type is a Trip and the status is not Completed, Canceled, or Inactive.
                    var query = container.GetItemLinqQueryable <Trip>(requestOptions: new QueryRequestOptions {
                        PartitionKey = new Microsoft.Azure.Cosmos.PartitionKey(vin)
                    })
                                .Where(p => p.status != WellKnown.Status.Completed &&
                                       p.status != WellKnown.Status.Canceled &&
                                       p.status != WellKnown.Status.Inactive &&
                                       p.entityType == WellKnown.EntityTypes.Trip)
                                .ToFeedIterator();

                    if (query.HasMoreResults)
                    {
                        // Only retrieve the first result.
                        var trip = (await query.ReadNextAsync()).FirstOrDefault();

                        if (trip != null)
                        {
                            // Retrieve the Consignment record.
                            var document = await container.ReadItemAsync <Consignment>(trip.consignmentId,
                                                                                       new Microsoft.Azure.Cosmos.PartitionKey(trip.consignmentId));

                            var consignment       = document.Resource;
                            var updateTrip        = false;
                            var updateConsignment = false;

                            // Calculate how far along the vehicle is for this trip.
                            var milesDriven = odometerHigh - trip.odometerBegin;
                            if (milesDriven >= trip.plannedTripDistance)
                            {
                                // The trip is completed!
                                trip.status        = WellKnown.Status.Completed;
                                trip.odometerEnd   = odometerHigh;
                                trip.tripEnded     = DateTime.UtcNow;
                                consignment.status = WellKnown.Status.Completed;

                                // Update the trip and consignment records.
                                updateTrip        = true;
                                updateConsignment = true;

                                sendTripAlert = true;
                            }
                            else
                            {
                                if (DateTime.UtcNow >= consignment.deliveryDueDate && trip.status != WellKnown.Status.Delayed)
                                {
                                    // The trip is delayed!
                                    trip.status        = WellKnown.Status.Delayed;
                                    consignment.status = WellKnown.Status.Delayed;

                                    // Update the trip and consignment records.
                                    updateTrip        = true;
                                    updateConsignment = true;

                                    sendTripAlert = true;
                                }
                            }

                            if (trip.tripStarted == null)
                            {
                                // Set the trip start date.
                                trip.tripStarted = DateTime.UtcNow;
                                // Set the trip and consignment status to Active.
                                trip.status        = WellKnown.Status.Active;
                                consignment.status = WellKnown.Status.Active;

                                updateTrip        = true;
                                updateConsignment = true;

                                sendTripAlert = true;
                            }

                            // Update the trip and consignment records.
                            if (updateTrip)
                            {
                                await container.ReplaceItemAsync(trip, trip.id, new Microsoft.Azure.Cosmos.PartitionKey(trip.partitionKey));
                            }

                            if (updateConsignment)
                            {
                                await container.ReplaceItemAsync(consignment, consignment.id, new Microsoft.Azure.Cosmos.PartitionKey(consignment.partitionKey));
                            }

                            // Send a trip alert.
                            if (sendTripAlert)
                            {
                                // Have the HttpClient factory create a new client instance.
                                var httpClient = _httpClientFactory.CreateClient(NamedHttpClients.LogicAppClient);

                                // Create the payload to send to the Logic App.
                                var payload = new LogicAppAlert
                                {
                                    consignmentId        = trip.consignmentId,
                                    customer             = trip.consignment.customer,
                                    deliveryDueDate      = trip.consignment.deliveryDueDate,
                                    hasHighValuePackages = trip.packages.Any(p => p.highValue),
                                    id = trip.id,
                                    lastRefrigerationUnitTemperatureReading = averageRefrigerationUnitTemp,
                                    location = trip.location,
                                    lowestPackageStorageTemperature = trip.packages.Min(p => p.storageTemperature),
                                    odometerBegin       = trip.odometerBegin,
                                    odometerEnd         = trip.odometerEnd,
                                    plannedTripDistance = trip.plannedTripDistance,
                                    tripStarted         = trip.tripStarted,
                                    tripEnded           = trip.tripEnded,
                                    status             = trip.status,
                                    vin                = trip.vin,
                                    temperatureSetting = trip.temperatureSetting,
                                    recipientEmail     = Environment.GetEnvironmentVariable("RecipientEmail")
                                };

                                var postBody = JsonConvert.SerializeObject(payload);

                                await httpClient.PostAsync(Environment.GetEnvironmentVariable("LogicAppUrl"), new StringContent(postBody, Encoding.UTF8, "application/json"));
                            }
                        }
                    }
                }
            }
        }