From 9958b0876bfcac48ce9e6a1ad6fbb3a66779ffb1 Mon Sep 17 00:00:00 2001 From: Markus Meyer Date: Tue, 18 Feb 2020 13:41:12 +0100 Subject: [PATCH] #59 Logging; #47 Settings --- PlanB.Butler.Services/Constants.cs | 27 +++ .../ConvertByteArrayToString.cs | 30 --- .../Extensions/LoggerExtension.cs | 113 +++++++++++ .../Extensions/NewtonsoftExtension.cs | 87 +++++++++ PlanB.Butler.Services/Finance.cs | 36 ---- PlanB.Butler.Services/FinanceService.cs | 171 +++++++++++++++++ .../GetDailyOrderOverview.cs | 60 ------ .../GetDailyOrderOverviewForUser.cs | 62 ------ PlanB.Butler.Services/GetSalaryDeduction.cs | 61 ------ .../GetServiceInfo/GetServiceInfo.cs | 36 ++++ PlanB.Butler.Services/OrderService.cs | 178 ++++++++++++++++++ .../PlanB.Butler.Services.csproj | 4 + PlanB.Butler.Services/PostData.cs | 6 - PlanB.Butler.Services/PostDocument.cs | 95 ++-------- PlanB.Butler.Services/Readme.md | 5 + PlanB.Butler.Services/Util.cs | 36 ++++ PlanB.Butler.Services/secret.settings.json | 10 + 17 files changed, 679 insertions(+), 338 deletions(-) create mode 100644 PlanB.Butler.Services/Constants.cs delete mode 100644 PlanB.Butler.Services/ConvertByteArrayToString.cs create mode 100644 PlanB.Butler.Services/Extensions/LoggerExtension.cs create mode 100644 PlanB.Butler.Services/Extensions/NewtonsoftExtension.cs delete mode 100644 PlanB.Butler.Services/Finance.cs create mode 100644 PlanB.Butler.Services/FinanceService.cs delete mode 100644 PlanB.Butler.Services/GetDailyOrderOverview.cs delete mode 100644 PlanB.Butler.Services/GetDailyOrderOverviewForUser.cs delete mode 100644 PlanB.Butler.Services/GetSalaryDeduction.cs create mode 100644 PlanB.Butler.Services/GetServiceInfo/GetServiceInfo.cs create mode 100644 PlanB.Butler.Services/OrderService.cs delete mode 100644 PlanB.Butler.Services/PostData.cs create mode 100644 PlanB.Butler.Services/Readme.md create mode 100644 PlanB.Butler.Services/Util.cs create mode 100644 PlanB.Butler.Services/secret.settings.json diff --git a/PlanB.Butler.Services/Constants.cs b/PlanB.Butler.Services/Constants.cs new file mode 100644 index 0000000..62fab81 --- /dev/null +++ b/PlanB.Butler.Services/Constants.cs @@ -0,0 +1,27 @@ +// Copyright (c) PlanB. GmbH. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +namespace PlanB.Butler.Services +{ + /// + /// Constants. + /// + internal static class Constants + { + /// + /// Gets or sets the name of the butler correlation trace. + /// + /// + /// The name of the butler correlation trace. + /// + internal static string ButlerCorrelationTraceName { get; set; } + + /// + /// Gets or sets the butler correlation trace header. + /// + /// + /// The butler correlation trace header. + /// + internal static string ButlerCorrelationTraceHeader { get; set; } + } +} diff --git a/PlanB.Butler.Services/ConvertByteArrayToString.cs b/PlanB.Butler.Services/ConvertByteArrayToString.cs deleted file mode 100644 index 5c8bf39..0000000 --- a/PlanB.Butler.Services/ConvertByteArrayToString.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using System.Runtime.Serialization.Formatters.Binary; - -namespace Post_Document -{ - public static class ConvertByteArrayToString - { - [FunctionName("ConvertByteArrayToString")] - public static async Task Run( - [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] Byte[] req, - ILogger log) - { - byte[] tmp = req; - MemoryStream memStream = new MemoryStream(); - BinaryFormatter binForm = new BinaryFormatter(); - memStream.Write(tmp, 0, tmp.Length); - memStream.Seek(0, SeekOrigin.Begin); - Object obj = (Object)binForm.Deserialize(memStream); - return obj; - } - } -} diff --git a/PlanB.Butler.Services/Extensions/LoggerExtension.cs b/PlanB.Butler.Services/Extensions/LoggerExtension.cs new file mode 100644 index 0000000..9bf3ff0 --- /dev/null +++ b/PlanB.Butler.Services/Extensions/LoggerExtension.cs @@ -0,0 +1,113 @@ +// Copyright (c) PlanB. GmbH. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +using System; +using System.Collections.Generic; + +using Microsoft.Extensions.Logging; + +namespace PlanB.Butler.Services.Extensions +{ + /// + /// The ILogger Extensions. + /// + public static class LoggerExtension + { + /// + /// Informations the specified event identifier. + /// + /// The log. + /// The Butler correlation identifier. + /// The message. + /// The trace. + public static void LogInformation(this ILogger log, Guid butlerCorrelationId, string message, IDictionary trace) + { + if (log == null) + { + return; + } + + EventId eventId = new EventId(butlerCorrelationId.GetHashCode(), Constants.ButlerCorrelationTraceName); + + var state = new Dictionary + { + { Constants.ButlerCorrelationTraceName, butlerCorrelationId }, + { "Message", message }, + }; + + var rator = trace.GetEnumerator(); + while (rator.MoveNext()) + { + if (!state.ContainsKey(rator.Current.Key)) + { + state.Add(rator.Current.Key, rator.Current.Value); + } + } + + log.Log(LogLevel.Information, eventId, state, null, Formatter); + } + + /// + /// Logs the error. + /// + /// The log. + /// The correlation identifier. + /// The message. + /// The trace. + /// The exeception. + public static void LogError(this ILogger log, Guid correlationId, string message, IDictionary trace, Exception ex = null) + { + if (log == null) + { + return; + } + + EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName); + + var state = new Dictionary + { + { Constants.ButlerCorrelationTraceName, correlationId }, + { "Message", message }, + }; + + var rator = trace.GetEnumerator(); + while (rator.MoveNext()) + { + if (!state.ContainsKey(rator.Current.Key)) + { + state.Add(rator.Current.Key, rator.Current.Value); + } + } + + if (ex == null) + { + ex = new Exception(message); + } + + log.Log(LogLevel.Error, eventId, state, ex, Formatter); + } + + /// + /// Style of logging. + /// + /// State. + /// State Param. + /// Exception. + /// Message. + internal static string Formatter(T state, Exception ex) + { + if (ex != null) + { + return ex.ToString(); + } + + Dictionary stateDictionary = state as Dictionary; + if (stateDictionary != null && stateDictionary.TryGetValue("Message", out var message)) + { + return message?.ToString() ?? string.Empty; + } + + return string.Empty; + } + } +} diff --git a/PlanB.Butler.Services/Extensions/NewtonsoftExtension.cs b/PlanB.Butler.Services/Extensions/NewtonsoftExtension.cs new file mode 100644 index 0000000..9f94856 --- /dev/null +++ b/PlanB.Butler.Services/Extensions/NewtonsoftExtension.cs @@ -0,0 +1,87 @@ +// Copyright (c) PlanB. GmbH. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +using System.Collections.Generic; + +using Newtonsoft.Json.Linq; + +namespace PlanB.Butler.Services.Extensions +{ + /// + /// NewtonsoftExtension. + /// + public static class NewtonsoftExtension + { + /// + /// Sanitizes a JObject. + /// + /// JObject. + /// Indicates whether 'id' and 'lastUpdate' fields should be removed. + public static void Sanitize(this JObject obj, bool removeMetaTags = false) + { + List metaData = new List(); + foreach (JToken token in obj.Descendants()) + { + try + { + JProperty propertyToken = (JProperty)token; + if (propertyToken.Name.StartsWith("_")) + { + metaData.Add(token); + } + + if (removeMetaTags) + { + if (propertyToken.Name.Equals("id") || propertyToken.Name.Equals("lastUpdate")) + { + metaData.Add(token); + } + } + } + + // Some Properties may not get Serialized, just skip them + catch + { + } + } + + metaData.ForEach(n => n.Remove()); + } + + /// + /// Sanitizes a JArray. + /// + /// JArray. + /// Indicates whether 'id' and 'lastUpdate' should be removed. + public static void Sanitize(this JArray obj, bool removeDocumentMetaData = false) + { + List metaData = new List(); + foreach (JToken token in obj.Descendants()) + { + try + { + JProperty propertyToken = (JProperty)token; + if (propertyToken.Name.StartsWith("_")) + { + metaData.Add(token); + } + + if (removeDocumentMetaData) + { + if (propertyToken.Name.Equals("id") || propertyToken.Name.Equals("lastUpdate")) + { + metaData.Add(token); + } + } + } + + // Some Properties may not get Serialized, just skip them + catch + { + } + } + + metaData.ForEach(n => n.Remove()); + } + } +} diff --git a/PlanB.Butler.Services/Finance.cs b/PlanB.Butler.Services/Finance.cs deleted file mode 100644 index 99de5e2..0000000 --- a/PlanB.Butler.Services/Finance.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; - -namespace Post_Document -{ - /// - /// Finance. - /// - public static class Finance - { - [FunctionName("Finance")] - public static async Task Run( - [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, - ILogger log) - { - log.LogInformation("C# HTTP trigger function processed a request."); - - string name = req.Query["name"]; - - string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); - dynamic data = JsonConvert.DeserializeObject(requestBody); - name = name ?? data?.name; - - return name != null - ? (ActionResult)new OkObjectResult($"Hello, {name}") - : new BadRequestObjectResult("Please pass a name on the query string or in the request body"); - } - } -} diff --git a/PlanB.Butler.Services/FinanceService.cs b/PlanB.Butler.Services/FinanceService.cs new file mode 100644 index 0000000..e4063aa --- /dev/null +++ b/PlanB.Butler.Services/FinanceService.cs @@ -0,0 +1,171 @@ +// Copyright (c) PlanB. GmbH. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +using BotLibraryV2; +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.WebJobs; +using Microsoft.Azure.WebJobs.Extensions.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.WindowsAzure.Storage; +using Microsoft.WindowsAzure.Storage.Blob; +using Newtonsoft.Json; +using PlanB.Butler.Services.Extensions; + +namespace PlanB.Butler.Services +{ + /// + /// Finance. + /// + public static class FinanceService + { + /// + /// Gets the salary deduction. + /// + /// The req. + /// The log. + /// The context. + /// + /// SalaryDeduction. + /// + [FunctionName(nameof(GetSalaryDeduction))] + public static async Task GetSalaryDeduction( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req, + ILogger log, + ExecutionContext context) + { + log.LogInformation("C# HTTP trigger function processed a request."); + var config = new ConfigurationBuilder() + .SetBasePath(context.FunctionAppDirectory) + .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) + .AddJsonFile("secret.settings.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables() + .Build(); + var connectionString = config["StorageSend"]; + + CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString); + CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); + CloudBlobContainer container = blobClient.GetContainerReference("salarydeduction"); + BlobContinuationToken token = new BlobContinuationToken(); + var operationContext = new OperationContext(); + var options = new BlobRequestOptions(); + var cloudBlobClient = storageAccount.CreateCloudBlobClient(); + var cloudBlobContainer = cloudBlobClient.GetContainerReference("salarydeduction"); + BlobContinuationToken blobContinuationToken = null; + List orderBlob = new List(); + var blobs = await container.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, null, blobContinuationToken, options, operationContext).ConfigureAwait(false); + Microsoft.Extensions.Primitives.StringValues month; + req.Headers.TryGetValue("user", out month); + string stringMonth = Convert.ToString(month); + foreach (var item in blobs.Results) + { + CloudBlockBlob blob = (CloudBlockBlob)item; + await blob.FetchAttributesAsync(); + DateTime date = DateTime.Now; + + if (blob.Metadata.Contains(new KeyValuePair("month", stringMonth))) + { + Order order = new Order(); + await blob.FetchAttributesAsync(); + var blobDownload = blob.DownloadTextAsync(); + var blobData = blobDownload.Result; + orderBlob.Add(JsonConvert.DeserializeObject(blobData)); + } + } + + return JsonConvert.SerializeObject(orderBlob); + } + + /// + /// Posts the document money. + /// + /// The message header. + /// The BLOB. + /// The log. + /// The context. + [Singleton] + [FunctionName(nameof(PostDocumentMoney))] + public static void PostDocumentMoney( + [ServiceBusTrigger("q.planbutlerupdatemoney", Connection = "butlerSend")]Microsoft.Azure.ServiceBus.Message messageHeader, + [Blob("{Label}", FileAccess.ReadWrite, Connection = "StorageSend")]string blob, + ILogger log, + ExecutionContext context) + { + Guid correlationId = Guid.Parse(messageHeader.CorrelationId); + var methodName = MethodBase.GetCurrentMethod().Name; + var trace = new Dictionary(); + EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName); + + try + { + blob = Encoding.Default.GetString(messageHeader.Body); + log.LogInformation(correlationId, $"'{methodName}' - success", trace); + } + catch (Exception e) + { + trace.Add(string.Format("{0} - {1}", MethodBase.GetCurrentMethod().Name, "rejected"), e.Message); + trace.Add(string.Format("{0} - {1} - StackTrace", MethodBase.GetCurrentMethod().Name, "rejected"), e.StackTrace); + trace.Add("MessageId", messageHeader.MessageId); + trace.Add("DeliveryCount", messageHeader.SystemProperties.DeliveryCount.ToString()); + if (messageHeader.SystemProperties.DeliveryCount == 1) + { + log.LogError(correlationId, $"'{methodName}' - rejected", trace, e); + } + + log.LogInformation(correlationId, $"'{methodName}' - {messageHeader.SystemProperties.DeliveryCount} - rejected", trace); + + throw; + } + finally + { + log.LogTrace(eventId, $"'{methodName}' - busobjkey finished"); + log.LogInformation(correlationId, $"'{methodName}' - {messageHeader.SystemProperties.DeliveryCount} - finished", trace); + } + } + + [Singleton] + [FunctionName(nameof(PostDocumentSalary))] + public static async void PostDocumentSalary( + [ServiceBusTrigger("q.planbutlerupdatesalary", Connection = "butlerSend")]Microsoft.Azure.ServiceBus.Message messageHeader, + [Blob("{Label}", FileAccess.ReadWrite, Connection = "StorageSend")]CloudBlockBlob blob, + ILogger log) + { + string payload = Encoding.Default.GetString(messageHeader.Body); + SalaryDeduction orderBlob = new SalaryDeduction(); + orderBlob.Order = new List(); + orderBlob = JsonConvert.DeserializeObject(payload); + string name = string.Empty; + DateTime date = DateTime.Now; + foreach (var item in orderBlob.Order) + { + date = item.Date; + break; + } + + var stringday = date.Day.ToString(); + var stringMonth = date.Month.ToString(); + + blob.Metadata.Add("month", stringMonth); + blob.Metadata.Add("day", stringday); + await blob.UploadTextAsync(payload); + await blob.SetMetadataAsync(); + } + + [Singleton] + [FunctionName(nameof(PostDocumentExcel))] + public static void PostDocumentExcel( + [ServiceBusTrigger("q.planbutlerupdateexcel", Connection = "butlerSend")]Microsoft.Azure.ServiceBus.Message messageHeader, + [Blob("{Label}", FileAccess.ReadWrite, Connection = "StorageSend")]out byte[] payload, + ILogger log) + { + payload = messageHeader.Body; + } + } +} diff --git a/PlanB.Butler.Services/GetDailyOrderOverview.cs b/PlanB.Butler.Services/GetDailyOrderOverview.cs deleted file mode 100644 index a48d973..0000000 --- a/PlanB.Butler.Services/GetDailyOrderOverview.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Microsoft.WindowsAzure.Storage; -using Microsoft.WindowsAzure.Storage.Blob; -using System.Collections.Generic; -using BotLibraryV2; - -namespace Post_Document -{ - public static class GetDailyOrderOverview - { - [FunctionName("GetDailyOrderOverview")] - public static async Task Run( - [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, - ILogger log) - { - log.LogInformation("C# HTTP trigger function processed a request."); - - - var connectionString = string.Empty;// ButlerBot.Util.Settings.StorageAccountConnectionString; - CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString); - CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); - CloudBlobContainer container = blobClient.GetContainerReference("orders"); - BlobContinuationToken token = new BlobContinuationToken(); - var context = new OperationContext(); - var options = new BlobRequestOptions(); - var cloudBlobClient = storageAccount.CreateCloudBlobClient(); - var cloudBlobContainer = cloudBlobClient.GetContainerReference("orders"); - BlobContinuationToken blobContinuationToken = null; - List orderBlob = new List(); - List blobitems = new List(); - var blobs = await container.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, null, blobContinuationToken, options, context).ConfigureAwait(false); - - foreach (var item in blobs.Results) - { - CloudBlockBlob blob = (CloudBlockBlob)item; - await blob.FetchAttributesAsync(); - DateTime date = DateTime.Now; - var stringDate = date.ToString("yyyy-MM-dd"); - if (blob.Metadata.Contains(new KeyValuePair("date", stringDate))) - { - Order order = new Order(); - await blob.FetchAttributesAsync(); - var blobDownload = blob.DownloadTextAsync(); - var blobData = blobDownload.Result; - orderBlob.Add(JsonConvert.DeserializeObject(blobData)); - } - } - - return JsonConvert.SerializeObject(orderBlob); - } - } -} diff --git a/PlanB.Butler.Services/GetDailyOrderOverviewForUser.cs b/PlanB.Butler.Services/GetDailyOrderOverviewForUser.cs deleted file mode 100644 index 49bcfc1..0000000 --- a/PlanB.Butler.Services/GetDailyOrderOverviewForUser.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Microsoft.WindowsAzure.Storage; -using Microsoft.WindowsAzure.Storage.Blob; -using System.Collections.Generic; -using BotLibraryV2; - -namespace Post_Document -{ - public static class GetDailyOrderOverviewForUser - { - [FunctionName("GetDailyOrderOverviewForUser")] - public static async Task Run( - [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, - ILogger log) - { - log.LogInformation("C# HTTP trigger function processed a request."); - Microsoft.Extensions.Primitives.StringValues user; - req.Headers.TryGetValue("user",out user); - var connectionString = string.Empty;// TODO: ButlerBot.Util.Settings.StorageAccountConnectionString; - CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString); - CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); - CloudBlobContainer container = blobClient.GetContainerReference("orders"); - BlobContinuationToken token = new BlobContinuationToken(); - var context = new OperationContext(); - var options = new BlobRequestOptions(); - var cloudBlobClient = storageAccount.CreateCloudBlobClient(); - var cloudBlobContainer = cloudBlobClient.GetContainerReference("orders"); - BlobContinuationToken blobContinuationToken = null; - List orderBlob = new List(); - List blobitems = new List(); - var blobs = await container.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, null, blobContinuationToken, options, context).ConfigureAwait(false); - - foreach (var item in blobs.Results) - { - CloudBlockBlob blob = (CloudBlockBlob)item; - - await blob.FetchAttributesAsync(); - DateTime date = DateTime.Now; - var stringDate = date.ToString("yyyy-MM-dd"); - string username = user.ToString(); - if (blob.Metadata.Contains(new KeyValuePair("date", stringDate)) && blob.Metadata.Contains(new KeyValuePair("user", username))) - { - Order order = new Order(); - await blob.FetchAttributesAsync(); - var blobDownload = blob.DownloadTextAsync(); - var blobData = blobDownload.Result; - orderBlob.Add(JsonConvert.DeserializeObject(blobData)); - } - } - - return JsonConvert.SerializeObject(orderBlob); - } - } -} diff --git a/PlanB.Butler.Services/GetSalaryDeduction.cs b/PlanB.Butler.Services/GetSalaryDeduction.cs deleted file mode 100644 index 9980d9c..0000000 --- a/PlanB.Butler.Services/GetSalaryDeduction.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Microsoft.WindowsAzure.Storage; -using Microsoft.WindowsAzure.Storage.Blob; -using System.Collections.Generic; -using BotLibraryV2; - - -namespace Post_Document -{ - public static class GetSalaryDeduction - { - [FunctionName("GetSalaryDeduction")] - public static async Task Run( - [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, - ILogger log) - { - log.LogInformation("C# HTTP trigger function processed a request."); - - var connectionString = string.Empty;// ButlerBot.Util.Settings.StorageAccountConnectionString; - CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString); - CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); - CloudBlobContainer container = blobClient.GetContainerReference("salarydeduction"); - BlobContinuationToken token = new BlobContinuationToken(); - var context = new OperationContext(); - var options = new BlobRequestOptions(); - var cloudBlobClient = storageAccount.CreateCloudBlobClient(); - var cloudBlobContainer = cloudBlobClient.GetContainerReference("salarydeduction"); - BlobContinuationToken blobContinuationToken = null; - List orderBlob = new List(); - var blobs = await container.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, null, blobContinuationToken, options, context).ConfigureAwait(false); - Microsoft.Extensions.Primitives.StringValues month ; - req.Headers.TryGetValue("user", out month); - string stringMonth = Convert.ToString(month); - foreach (var item in blobs.Results) - { - CloudBlockBlob blob = (CloudBlockBlob)item; - await blob.FetchAttributesAsync(); - DateTime date = DateTime.Now; - - if (blob.Metadata.Contains(new KeyValuePair("month", stringMonth))) - { - Order order = new Order(); - await blob.FetchAttributesAsync(); - var blobDownload = blob.DownloadTextAsync(); - var blobData = blobDownload.Result; - orderBlob.Add(JsonConvert.DeserializeObject(blobData)); - } - } - - return JsonConvert.SerializeObject(orderBlob); - } - } -} diff --git a/PlanB.Butler.Services/GetServiceInfo/GetServiceInfo.cs b/PlanB.Butler.Services/GetServiceInfo/GetServiceInfo.cs new file mode 100644 index 0000000..f6aa70f --- /dev/null +++ b/PlanB.Butler.Services/GetServiceInfo/GetServiceInfo.cs @@ -0,0 +1,36 @@ +// Copyright (c) PlanB. GmbH. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Azure.WebJobs; +using Microsoft.Azure.WebJobs.Extensions.Http; +using Microsoft.Extensions.Logging; + +namespace PlanB.Butler.Services.GetServiceInfo +{ + /// + /// GetServiceInfo. + /// + public static class GetServiceInfo + { + /// + /// Runs the specified req. + /// + /// The req. + /// The log. + /// ServiceInfo. + [FunctionName(nameof(GetServiceInfo))] + public static IActionResult Run( + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req, + ILogger log) + { + var serviceInfo = new + { + status = "OK", + }; + + return (ActionResult)new OkObjectResult(serviceInfo); + } + } +} diff --git a/PlanB.Butler.Services/OrderService.cs b/PlanB.Butler.Services/OrderService.cs new file mode 100644 index 0000000..4e35a0f --- /dev/null +++ b/PlanB.Butler.Services/OrderService.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +using BotLibraryV2; +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.WebJobs; +using Microsoft.Azure.WebJobs.Extensions.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.WindowsAzure.Storage; +using Microsoft.WindowsAzure.Storage.Blob; +using Newtonsoft.Json; + +namespace PlanB.Butler.Services +{ + /// + /// OrderService. + /// + public static class OrderService + { + /// + /// Gets the daily order overview. + /// + /// The req. + /// The log. + /// The context. + /// + /// Daily Overview. + /// + [FunctionName(nameof(GetDailyOrderOverview))] + public static async Task GetDailyOrderOverview( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] + HttpRequest req, + ILogger log, + ExecutionContext context) + { + log.LogInformation("C# HTTP trigger function processed a request."); + var config = new ConfigurationBuilder() + .SetBasePath(context.FunctionAppDirectory) + .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) + .AddJsonFile("secret.settings.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables() + .Build(); + var connectionString = config["StorageSend"]; + + CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString); + CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); + CloudBlobContainer container = blobClient.GetContainerReference("orders"); + BlobContinuationToken token = new BlobContinuationToken(); + var operationContext = new OperationContext(); + var options = new BlobRequestOptions(); + var cloudBlobClient = storageAccount.CreateCloudBlobClient(); + var cloudBlobContainer = cloudBlobClient.GetContainerReference("orders"); + BlobContinuationToken blobContinuationToken = null; + List orderBlob = new List(); + List blobitems = new List(); + var blobs = await container.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, null, blobContinuationToken, options, operationContext).ConfigureAwait(false); + + foreach (var item in blobs.Results) + { + CloudBlockBlob blob = (CloudBlockBlob)item; + await blob.FetchAttributesAsync(); + DateTime date = DateTime.Now; + var stringDate = date.ToString("yyyy-MM-dd"); + if (blob.Metadata.Contains(new KeyValuePair("date", stringDate))) + { + Order order = new Order(); + await blob.FetchAttributesAsync(); + var blobDownload = blob.DownloadTextAsync(); + var blobData = blobDownload.Result; + orderBlob.Add(JsonConvert.DeserializeObject(blobData)); + } + } + + return JsonConvert.SerializeObject(orderBlob); + } + + /// + /// Gets the daily order overview for user. + /// + /// The req. + /// The log. + /// The context. + /// Daily Overview. + [FunctionName(nameof(GetDailyOrderOverviewForUser))] + public static async Task GetDailyOrderOverviewForUser( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req, + ILogger log, + ExecutionContext context) + { + var config = new ConfigurationBuilder() + .SetBasePath(context.FunctionAppDirectory) + .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) + .AddJsonFile("secret.settings.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables() + .Build(); + + log.LogInformation("C# HTTP trigger function processed a request."); + req.Headers.TryGetValue("user", out Microsoft.Extensions.Primitives.StringValues user); + + var connectionString = config["StorageSend"]; + + CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString); + CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); + CloudBlobContainer container = blobClient.GetContainerReference("orders"); + BlobContinuationToken token = new BlobContinuationToken(); + var operationContext = new OperationContext(); + var options = new BlobRequestOptions(); + var cloudBlobClient = storageAccount.CreateCloudBlobClient(); + var cloudBlobContainer = cloudBlobClient.GetContainerReference("orders"); + + BlobContinuationToken blobContinuationToken = null; + List orderBlob = new List(); + List blobitems = new List(); + var blobs = await container.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, null, blobContinuationToken, options, operationContext).ConfigureAwait(false); + + foreach (var item in blobs.Results) + { + CloudBlockBlob blob = (CloudBlockBlob)item; + + await blob.FetchAttributesAsync(); + DateTime date = DateTime.Now; + var stringDate = date.ToString("yyyy-MM-dd"); + string username = user.ToString(); + if (blob.Metadata.Contains(new KeyValuePair("date", stringDate)) && blob.Metadata.Contains(new KeyValuePair("user", username))) + { + await blob.FetchAttributesAsync(); + var blobDownload = blob.DownloadTextAsync(); + var blobData = blobDownload.Result; + orderBlob.Add(JsonConvert.DeserializeObject(blobData)); + } + } + + return JsonConvert.SerializeObject(orderBlob); + } + + /// + /// Posts the document order. + /// + /// The message header. + /// The BLOB. + /// The log. + /// The context. + [Singleton] + [FunctionName(nameof(PostDocumentOrder))] + public static async void PostDocumentOrder( + [ServiceBusTrigger("q.planbutlerupdateorder", Connection = "butlerSend")]Microsoft.Azure.ServiceBus.Message messageHeader, + [Blob("{Label}", FileAccess.ReadWrite, Connection = "StorageSend")]CloudBlockBlob blob, + ILogger log, + ExecutionContext context) + { + string payload = Encoding.Default.GetString(messageHeader.Body); + OrderBlob orderBlob = new OrderBlob(); + orderBlob.OrderList = new List(); + orderBlob = JsonConvert.DeserializeObject(payload); + string name = string.Empty; + DateTime date = DateTime.Now; + foreach (var item in orderBlob.OrderList) + { + name = item.Name; + date = item.Date; + break; + } + + var stringDate = date.ToString("yyyy-MM-dd"); + + blob.Metadata.Add("user", name); + blob.Metadata.Add("date", stringDate); + await blob.UploadTextAsync(payload); + await blob.SetMetadataAsync(); + } + + + } +} diff --git a/PlanB.Butler.Services/PlanB.Butler.Services.csproj b/PlanB.Butler.Services/PlanB.Butler.Services.csproj index 0cc40eb..afea73d 100644 --- a/PlanB.Butler.Services/PlanB.Butler.Services.csproj +++ b/PlanB.Butler.Services/PlanB.Butler.Services.csproj @@ -39,6 +39,10 @@ PreserveNewest + + PreserveNewest + Never + PreserveNewest Never diff --git a/PlanB.Butler.Services/PostData.cs b/PlanB.Butler.Services/PostData.cs deleted file mode 100644 index bd6fc42..0000000 --- a/PlanB.Butler.Services/PostData.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Post_Document -{ - internal class PostData - { - } -} \ No newline at end of file diff --git a/PlanB.Butler.Services/PostDocument.cs b/PlanB.Butler.Services/PostDocument.cs index ec94049..4bb5e5c 100644 --- a/PlanB.Butler.Services/PostDocument.cs +++ b/PlanB.Butler.Services/PostDocument.cs @@ -1,94 +1,23 @@ +// Copyright (c) PlanB. GmbH. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; + using BotLibraryV2; using Microsoft.Azure.WebJobs; using Microsoft.Extensions.Logging; using Microsoft.WindowsAzure.Storage.Blob; using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; +using PlanB.Butler.Services.Extensions; -namespace Post_Document +namespace PlanB.Butler.Services { public static class PostDocument { - [Singleton] - [FunctionName(nameof(PostDocumentOrder))] - public static async void PostDocumentOrder([ServiceBusTrigger("q.planbutlerupdateorder", Connection = "butlerSend")]Microsoft.Azure.ServiceBus.Message messageHeader, - [Blob("{Label}", FileAccess.ReadWrite, Connection = "StorageSend")]CloudBlockBlob blob, - ILogger log) - { - - // Implement Logging after MVP. - string payload = Encoding.Default.GetString(messageHeader.Body); - OrderBlob orderBlob = new OrderBlob(); - orderBlob.OrderList = new List(); - orderBlob = JsonConvert.DeserializeObject(payload); - string name = string.Empty; - DateTime date = DateTime.Now; - foreach (var item in orderBlob.OrderList) - { - name = item.Name; - date = item.Date; - break; - } - - var stringDate = date.ToString("yyyy-MM-dd"); - blob.Metadata.Add("user", name); - blob.Metadata.Add("date", stringDate); - await blob.UploadTextAsync(payload); - await blob.SetMetadataAsync(); - - } - [Singleton] - [FunctionName(nameof(PostDocumentMoney))] - public static async void PostDocumentMoney([ServiceBusTrigger("q.planbutlerupdatemoney", Connection = "butlerSend")]Microsoft.Azure.ServiceBus.Message messageHeader, - [Blob("{Label}", FileAccess.ReadWrite, Connection = "StorageSend")]CloudBlockBlob blob, - ILogger log) - { - // Implement Logging after MVP. - - string payload = Encoding.Default.GetString(messageHeader.Body); - await blob.UploadTextAsync(payload); - } - [Singleton] - [FunctionName(nameof(PostDocumentSalary))] - public static async void PostDocumentSalary([ServiceBusTrigger("q.planbutlerupdatesalary", Connection = "butlerSend")]Microsoft.Azure.ServiceBus.Message messageHeader, - [Blob("{Label}", FileAccess.ReadWrite, Connection = "StorageSend")]CloudBlockBlob blob, - ILogger log) - { - // Implement Logging after MVP. - string payload = Encoding.Default.GetString(messageHeader.Body); - SalaryDeduction orderBlob = new SalaryDeduction(); - orderBlob.Order = new List(); - orderBlob = JsonConvert.DeserializeObject(payload); - string name = string.Empty; - DateTime date = DateTime.Now; - foreach (var item in orderBlob.Order) - { - date = item.Date; - break; - } - var stringday = date.Day.ToString(); - var stringMonth = date.Month.ToString(); - - blob.Metadata.Add("month", stringMonth); - blob.Metadata.Add("day", stringday); - await blob.UploadTextAsync(payload); - await blob.SetMetadataAsync(); - } - [Singleton] - [FunctionName(nameof(PostDocumentExcel))] - public static void PostDocumentExcel([ServiceBusTrigger("q.planbutlerupdateexcel", Connection = "butlerSend")]Microsoft.Azure.ServiceBus.Message messageHeader, - [Blob("{Label}", FileAccess.ReadWrite, Connection = "StorageSend")]out byte[] payload, - ILogger log) - { - // Implement Logging after MVP. - payload = messageHeader.Body; - } } -} - +} \ No newline at end of file diff --git a/PlanB.Butler.Services/Readme.md b/PlanB.Butler.Services/Readme.md new file mode 100644 index 0000000..4ca9554 --- /dev/null +++ b/PlanB.Butler.Services/Readme.md @@ -0,0 +1,5 @@ +# Confirguration + +Configuration is based on this solution: + +[Azure Functions local.settings.json Secrets and Source Control](https://www.tomfaltesek.com/azure-functions-local-settings-json-and-source-control/ "Azure Functions local.settings.json Secrets and Source Control") diff --git a/PlanB.Butler.Services/Util.cs b/PlanB.Butler.Services/Util.cs new file mode 100644 index 0000000..f85f191 --- /dev/null +++ b/PlanB.Butler.Services/Util.cs @@ -0,0 +1,36 @@ +// Copyright (c) PlanB. GmbH. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +using System; +using System.Linq; + +using Microsoft.AspNetCore.Http; + +namespace PlanB.Butler.Services +{ + /// + /// Util. + /// + internal static class Util + { + /// + /// Reads the correlation identifier. + /// + /// The headers. + /// Correlation Id. + internal static Guid ReadCorrelationId(IHeaderDictionary headers) + { + Guid correlationId = Guid.NewGuid(); + + if (headers != null && headers.TryGetValue(Constants.ButlerCorrelationTraceName, out var headerValues)) + { + if (Guid.TryParse(headerValues.FirstOrDefault(), out correlationId)) + { + correlationId = new Guid(headerValues.FirstOrDefault()); + } + } + + return correlationId; + } + } +} diff --git a/PlanB.Butler.Services/secret.settings.json b/PlanB.Butler.Services/secret.settings.json new file mode 100644 index 0000000..f803ae7 --- /dev/null +++ b/PlanB.Butler.Services/secret.settings.json @@ -0,0 +1,10 @@ +{ + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "FUNCTIONS_WORKER_RUNTIME": "dotnet", + "butlerSend": "-secret-", + "StorageSend": "-secret-", + "APPINSIGHTS_INSTRUMENTATIONKEY": "-secret-" + } +} \ No newline at end of file