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 +{ + /// <summary> + /// Constants. + /// </summary> + internal static class Constants + { + /// <summary> + /// Gets or sets the name of the butler correlation trace. + /// </summary> + /// <value> + /// The name of the butler correlation trace. + /// </value> + internal static string ButlerCorrelationTraceName { get; set; } + + /// <summary> + /// Gets or sets the butler correlation trace header. + /// </summary> + /// <value> + /// The butler correlation trace header. + /// </value> + 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<Object> 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 +{ + /// <summary> + /// The ILogger Extensions. + /// </summary> + public static class LoggerExtension + { + /// <summary> + /// Informations the specified event identifier. + /// </summary> + /// <param name="log">The log.</param> + /// <param name="butlerCorrelationId">The Butler correlation identifier.</param> + /// <param name="message">The message.</param> + /// <param name="trace">The trace.</param> + public static void LogInformation(this ILogger log, Guid butlerCorrelationId, string message, IDictionary<string, string> trace) + { + if (log == null) + { + return; + } + + EventId eventId = new EventId(butlerCorrelationId.GetHashCode(), Constants.ButlerCorrelationTraceName); + + var state = new Dictionary<string, object> + { + { 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); + } + + /// <summary> + /// Logs the error. + /// </summary> + /// <param name="log">The log.</param> + /// <param name="correlationId">The correlation identifier.</param> + /// <param name="message">The message.</param> + /// <param name="trace">The trace.</param> + /// <param name="ex">The exeception.</param> + public static void LogError(this ILogger log, Guid correlationId, string message, IDictionary<string, string> trace, Exception ex = null) + { + if (log == null) + { + return; + } + + EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName); + + var state = new Dictionary<string, object> + { + { 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); + } + + /// <summary> + /// Style of logging. + /// </summary> + /// <typeparam name="T">State.</typeparam> + /// <param name="state">State Param.</param> + /// <param name="ex">Exception.</param> + /// <returns>Message.</returns> + internal static string Formatter<T>(T state, Exception ex) + { + if (ex != null) + { + return ex.ToString(); + } + + Dictionary<string, object> stateDictionary = state as Dictionary<string, object>; + 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 +{ + /// <summary> + /// NewtonsoftExtension. + /// </summary> + public static class NewtonsoftExtension + { + /// <summary> + /// Sanitizes a JObject. + /// </summary> + /// <param name="obj">JObject.</param> + /// <param name="removeMetaTags">Indicates whether 'id' and 'lastUpdate' fields should be removed.</param> + public static void Sanitize(this JObject obj, bool removeMetaTags = false) + { + List<JToken> metaData = new List<JToken>(); + 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()); + } + + /// <summary> + /// Sanitizes a JArray. + /// </summary> + /// <param name="obj">JArray.</param> + /// <param name="removeDocumentMetaData">Indicates whether 'id' and 'lastUpdate' should be removed.</param> + public static void Sanitize(this JArray obj, bool removeDocumentMetaData = false) + { + List<JToken> metaData = new List<JToken>(); + 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 -{ - /// <summary> - /// Finance. - /// </summary> - public static class Finance - { - [FunctionName("Finance")] - public static async Task<IActionResult> 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 +{ + /// <summary> + /// Finance. + /// </summary> + public static class FinanceService + { + /// <summary> + /// Gets the salary deduction. + /// </summary> + /// <param name="req">The req.</param> + /// <param name="log">The log.</param> + /// <param name="context">The context.</param> + /// <returns> + /// SalaryDeduction. + /// </returns> + [FunctionName(nameof(GetSalaryDeduction))] + public static async Task<string> 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<SalaryDeduction> orderBlob = new List<SalaryDeduction>(); + 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<string, string>("month", stringMonth))) + { + Order order = new Order(); + await blob.FetchAttributesAsync(); + var blobDownload = blob.DownloadTextAsync(); + var blobData = blobDownload.Result; + orderBlob.Add(JsonConvert.DeserializeObject<SalaryDeduction>(blobData)); + } + } + + return JsonConvert.SerializeObject(orderBlob); + } + + /// <summary> + /// Posts the document money. + /// </summary> + /// <param name="messageHeader">The message header.</param> + /// <param name="blob">The BLOB.</param> + /// <param name="log">The log.</param> + /// <param name="context">The context.</param> + [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<string, string>(); + 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<Order>(); + orderBlob = JsonConvert.DeserializeObject<SalaryDeduction>(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<string> 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> orderBlob = new List<OrderBlob>(); - List<string> blobitems = new List<string>(); - 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<string, string>("date", stringDate))) - { - Order order = new Order(); - await blob.FetchAttributesAsync(); - var blobDownload = blob.DownloadTextAsync(); - var blobData = blobDownload.Result; - orderBlob.Add(JsonConvert.DeserializeObject<OrderBlob>(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<string> 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> orderBlob = new List<OrderBlob>(); - List<string> blobitems = new List<string>(); - 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<string, string>("date", stringDate)) && blob.Metadata.Contains(new KeyValuePair<string, string>("user", username))) - { - Order order = new Order(); - await blob.FetchAttributesAsync(); - var blobDownload = blob.DownloadTextAsync(); - var blobData = blobDownload.Result; - orderBlob.Add(JsonConvert.DeserializeObject<OrderBlob>(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<string> 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<SalaryDeduction> orderBlob = new List<SalaryDeduction>(); - 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<string, string>("month", stringMonth))) - { - Order order = new Order(); - await blob.FetchAttributesAsync(); - var blobDownload = blob.DownloadTextAsync(); - var blobData = blobDownload.Result; - orderBlob.Add(JsonConvert.DeserializeObject<SalaryDeduction>(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 +{ + /// <summary> + /// GetServiceInfo. + /// </summary> + public static class GetServiceInfo + { + /// <summary> + /// Runs the specified req. + /// </summary> + /// <param name="req">The req.</param> + /// <param name="log">The log.</param> + /// <returns>ServiceInfo.</returns> + [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 +{ + /// <summary> + /// OrderService. + /// </summary> + public static class OrderService + { + /// <summary> + /// Gets the daily order overview. + /// </summary> + /// <param name="req">The req.</param> + /// <param name="log">The log.</param> + /// <param name="context">The context.</param> + /// <returns> + /// Daily Overview. + /// </returns> + [FunctionName(nameof(GetDailyOrderOverview))] + public static async Task<string> 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> orderBlob = new List<OrderBlob>(); + List<string> blobitems = new List<string>(); + 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<string, string>("date", stringDate))) + { + Order order = new Order(); + await blob.FetchAttributesAsync(); + var blobDownload = blob.DownloadTextAsync(); + var blobData = blobDownload.Result; + orderBlob.Add(JsonConvert.DeserializeObject<OrderBlob>(blobData)); + } + } + + return JsonConvert.SerializeObject(orderBlob); + } + + /// <summary> + /// Gets the daily order overview for user. + /// </summary> + /// <param name="req">The req.</param> + /// <param name="log">The log.</param> + /// <param name="context">The context.</param> + /// <returns>Daily Overview.</returns> + [FunctionName(nameof(GetDailyOrderOverviewForUser))] + public static async Task<string> 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> orderBlob = new List<OrderBlob>(); + List<string> blobitems = new List<string>(); + 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<string, string>("date", stringDate)) && blob.Metadata.Contains(new KeyValuePair<string, string>("user", username))) + { + await blob.FetchAttributesAsync(); + var blobDownload = blob.DownloadTextAsync(); + var blobData = blobDownload.Result; + orderBlob.Add(JsonConvert.DeserializeObject<OrderBlob>(blobData)); + } + } + + return JsonConvert.SerializeObject(orderBlob); + } + + /// <summary> + /// Posts the document order. + /// </summary> + /// <param name="messageHeader">The message header.</param> + /// <param name="blob">The BLOB.</param> + /// <param name="log">The log.</param> + /// <param name="context">The context.</param> + [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<Order>(); + orderBlob = JsonConvert.DeserializeObject<OrderBlob>(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 @@ <None Update="host.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> + <None Update="secret.settings.json"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + <CopyToPublishDirectory>Never</CopyToPublishDirectory> + </None> <None Update="local.settings.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToPublishDirectory>Never</CopyToPublishDirectory> 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<Order>(); - orderBlob = JsonConvert.DeserializeObject<OrderBlob>(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<Order>(); - orderBlob = JsonConvert.DeserializeObject<SalaryDeduction>(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 +{ + /// <summary> + /// Util. + /// </summary> + internal static class Util + { + /// <summary> + /// Reads the correlation identifier. + /// </summary> + /// <param name="headers">The headers.</param> + /// <returns>Correlation Id.</returns> + 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