diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml
index 84885c6..4341aa1 100644
--- a/.github/workflows/dotnetcore.yml
+++ b/.github/workflows/dotnetcore.yml
@@ -10,7 +10,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
- dotnet-version: 3.0.100
+ dotnet-version: 3.1.101
- name: Build with dotnet
run: dotnet build PlanB.Butler.sln --configuration Release
@@ -23,4 +23,15 @@ jobs:
with:
dotnet-version: 2.2.108
- name: Test with dotnet
- run: dotnet test PlanB.Butler.Library/PlanB.Butler.Library.Test/PlanB.Butler.Library.Test.csproj --configuration Release
+ run: dotnet test PlanB.Butler.Library/PlanB.Butler.Library.Test/PlanB.Butler.Library.Test.csproj --configuration Release --collect:"Code Coverage"
+
+ test-service-library:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: 3.1.101
+ - name: Test with dotnet
+ run: dotnet test PlanB.Butler.Services/PlanB.Butler.Services.Test/PlanB.Butler.Services.Test.csproj --configuration Release --collect:"Code Coverage"
diff --git a/PlanB.Butler.Admin/PlanB.Butler.Admin/Contracts/IMealService.cs b/PlanB.Butler.Admin/PlanB.Butler.Admin/Contracts/IMealService.cs
index eb05bf5..bf6306f 100644
--- a/PlanB.Butler.Admin/PlanB.Butler.Admin/Contracts/IMealService.cs
+++ b/PlanB.Butler.Admin/PlanB.Butler.Admin/Contracts/IMealService.cs
@@ -23,8 +23,8 @@ public interface IMealService
/// Creates the meal.
///
/// The meal.
- /// True or false.
- Task CreateMeal(MealViewModel meal);
+ /// Meal.
+ Task CreateMeal(MealViewModel meal);
///
/// Updates the meal.
@@ -40,5 +40,11 @@ public interface IMealService
/// Meal by Id.
Task GetMeal(string id);
+ ///
+ /// Deletes the meal.
+ ///
+ /// The identifier.
+ /// Succes or failure.
+ Task DeleteMeal(string id);
}
}
diff --git a/PlanB.Butler.Admin/PlanB.Butler.Admin/Controllers/MealController.cs b/PlanB.Butler.Admin/PlanB.Butler.Admin/Controllers/MealController.cs
index 48ff4f7..06f0ef6 100644
--- a/PlanB.Butler.Admin/PlanB.Butler.Admin/Controllers/MealController.cs
+++ b/PlanB.Butler.Admin/PlanB.Butler.Admin/Controllers/MealController.cs
@@ -81,7 +81,6 @@ public async Task Edit(string id, [Bind("Id,CorrelationId,Date,Pr
if (this.ModelState.IsValid)
{
-
var result = await this.mealService.UpdateMeal(meal);
return this.RedirectToAction(nameof(this.Index));
}
@@ -94,7 +93,7 @@ public async Task Edit(string id, [Bind("Id,CorrelationId,Date,Pr
///
/// The identifier.
/// Meal.
- public async Task Edit(string? id)
+ public async Task Edit(string id)
{
if (string.IsNullOrEmpty(id))
{
@@ -110,5 +109,42 @@ public async Task Edit(string? id)
return this.View(meal);
}
+
+ ///
+ /// Deletes the specified identifier.
+ ///
+ /// The identifier.
+ /// IActionResult.
+ public async Task Delete(string id)
+ {
+ if (string.IsNullOrEmpty(id))
+ {
+ return this.NotFound();
+ }
+
+ var meal = await this.mealService.GetMeal(id);
+
+ if (meal == null)
+ {
+ return this.NotFound();
+ }
+
+ return this.View(meal);
+ }
+
+ ///
+ /// Deletes the confirmed.
+ ///
+ /// The identifier.
+ /// IActionResult.
+ [HttpPost]
+ [ActionName("Delete")]
+ [ValidateAntiForgeryToken]
+ public async Task DeleteConfirmed(string id)
+ {
+ await this.mealService.DeleteMeal(id);
+
+ return this.RedirectToAction(nameof(this.Index));
+ }
}
}
diff --git a/PlanB.Butler.Admin/PlanB.Butler.Admin/Services/MealService.cs b/PlanB.Butler.Admin/PlanB.Butler.Admin/Services/MealService.cs
index 715a2cf..5d0e706 100644
--- a/PlanB.Butler.Admin/PlanB.Butler.Admin/Services/MealService.cs
+++ b/PlanB.Butler.Admin/PlanB.Butler.Admin/Services/MealService.cs
@@ -47,7 +47,7 @@ public MealService(HttpClient httpClient, IConfiguration configuration)
///
/// True or false.
///
- public async Task CreateMeal(MealViewModel meal)
+ public async Task CreateMeal(MealViewModel meal)
{
Guid correlationId = Guid.NewGuid();
meal.CorrelationId = correlationId;
@@ -63,8 +63,26 @@ public async Task CreateMeal(MealViewModel meal)
Util.AddDefaultEsbHeaders(httpRequestMessage, correlationId, this.config["FunctionsKey"]);
var result = await this.httpClient.SendAsync(httpRequestMessage);
result.EnsureSuccessStatusCode();
- var success = result.IsSuccessStatusCode;
- return success;
+
+ MealViewModel responseModel = JsonConvert.DeserializeObject(result.Content.ReadAsStringAsync().Result);
+ return responseModel;
+ }
+
+ ///
+ /// Deletes the meal.
+ ///
+ /// The identifier.
+ ///
+ /// Succes or failure.
+ ///
+ public async Task DeleteMeal(string id)
+ {
+ var uri = this.config["MealsUri"].TrimEnd('/') + "/" + id;
+
+ this.httpClient.DefaultRequestHeaders.Add(Constants.FunctionsKeyHeader, this.config["FunctionsKey"]);
+ var response = await this.httpClient.DeleteAsync(uri);
+
+ return response.IsSuccessStatusCode;
}
///
diff --git a/PlanB.Butler.Admin/PlanB.Butler.Admin/Views/Meal/Delete.cshtml b/PlanB.Butler.Admin/PlanB.Butler.Admin/Views/Meal/Delete.cshtml
new file mode 100644
index 0000000..64973a3
--- /dev/null
+++ b/PlanB.Butler.Admin/PlanB.Butler.Admin/Views/Meal/Delete.cshtml
@@ -0,0 +1,50 @@
+@model PlanB.Butler.Admin.Models.MealViewModel
+
+@{
+ ViewData["Title"] = "Delete";
+}
+
+Delete
+
+Are you sure you want to delete this?
+
+
MealViewModel
+
+
+ -
+ @Html.DisplayNameFor(model => model.Id)
+
+ -
+ @Html.DisplayFor(model => model.Id)
+
+ -
+ @Html.DisplayNameFor(model => model.Date)
+
+ -
+ @Html.DisplayFor(model => model.Date)
+
+ -
+ @Html.DisplayNameFor(model => model.Price)
+
+ -
+ @Html.DisplayFor(model => model.Price)
+
+ -
+ @Html.DisplayNameFor(model => model.Name)
+
+ -
+ @Html.DisplayFor(model => model.Name)
+
+ -
+ @Html.DisplayNameFor(model => model.Restaurant)
+
+ -
+ @Html.DisplayFor(model => model.Restaurant)
+
+
+
+
+
diff --git a/PlanB.Butler.Bot/Bots/TeamsBot.cs b/PlanB.Butler.Bot/Bots/TeamsBot.cs
index 0792c6b..65751c8 100644
--- a/PlanB.Butler.Bot/Bots/TeamsBot.cs
+++ b/PlanB.Butler.Bot/Bots/TeamsBot.cs
@@ -1,18 +1,24 @@
-namespace PlanB.Butler.Bot
-{
- using System.Collections.Generic;
- using System.Threading;
- using System.Threading.Tasks;
- using Microsoft.Bot.Builder;
- using Microsoft.Bot.Builder.Dialogs;
- using Microsoft.Bot.Schema;
- using Microsoft.Extensions.Logging;
- using System.Resources;
- using System.Reflection;
+// Copyright (c) PlanB. GmbH. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
- // This bot is derived (view DialogBot) from the TeamsACtivityHandler class currently included as part of this sample.
+using System.Collections.Generic;
+using System.Reflection;
+using System.Resources;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Bot.Builder;
+using Microsoft.Bot.Builder.Dialogs;
+using Microsoft.Bot.Schema;
+using Microsoft.Extensions.Logging;
+namespace PlanB.Butler.Bot
+{
+ ///
+ /// This bot is derived (view DialogBot) from the TeamsACtivityHandler class currently included as part of this sample.
+ ///
+ ///
+ ///
public class TeamsBot : DialogBot where T : Dialog
{
///
diff --git a/PlanB.Butler.Bot/Dialogs/NextOrder.cs b/PlanB.Butler.Bot/Dialogs/NextOrder.cs
index fb7b599..2bb11ad 100644
--- a/PlanB.Butler.Bot/Dialogs/NextOrder.cs
+++ b/PlanB.Butler.Bot/Dialogs/NextOrder.cs
@@ -121,14 +121,14 @@ public NextOrder(IOptions config, IBotTelemetryClient telemetryClient
// This array defines how the Waterfall will execute.
var waterfallSteps = new WaterfallStep[]
{
- CompanyStepAsync,
+ this.CompanyStepAsync,
NameStepAsync,
RestaurantStepAsync,
- QuantatyStepAsync,
+ QuantityStepAsync,
FoodStepAsync,
- MealQuantatyStepAsync,
+ MealQuantityStepAsync,
PriceStepAsync,
- SummaryStepAsync,
+ this.SummaryStepAsync,
};
// Add named dialogs to the DialogSet. These names are saved in the dialog state.
@@ -280,7 +280,13 @@ private static async Task RestaurantStepAsync(WaterfallStepCon
}
}
- private static async Task QuantatyStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
+ ///
+ /// Quantities the step asynchronous.
+ ///
+ /// The step context.
+ /// The cancellation token.
+ /// DialogTurnResult.
+ private static async Task QuantityStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
try
{
@@ -364,8 +370,13 @@ private static async Task FoodStepAsync(WaterfallStepContext s
}
}
-
- private static async Task MealQuantatyStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
+ ///
+ /// Meals the quantity step asynchronous.
+ ///
+ /// The step context.
+ /// The cancellation token.
+ /// DialogTurnResult.
+ private static async Task MealQuantityStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var obj = ((FoundChoice)stepContext.Result).Value;
if (stepContext.Values["rest1"].ToString() == "yes")
diff --git a/PlanB.Butler.Bot/GlobalSuppressions.cs b/PlanB.Butler.Bot/GlobalSuppressions.cs
new file mode 100644
index 0000000..8054593
--- /dev/null
+++ b/PlanB.Butler.Bot/GlobalSuppressions.cs
@@ -0,0 +1,8 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "Makes no sense due to Generic", Scope = "type", Target = "~T:PlanB.Butler.Bot.TeamsBot`1")]
diff --git a/PlanB.Butler.Bot/PlanB.Butler.Bot.csproj b/PlanB.Butler.Bot/PlanB.Butler.Bot.csproj
index e6a1291..f997d18 100644
--- a/PlanB.Butler.Bot/PlanB.Butler.Bot.csproj
+++ b/PlanB.Butler.Bot/PlanB.Butler.Bot.csproj
@@ -3,14 +3,21 @@
netcoreapp2.2
latest
+ PlanB. GmbH
+ PlanB Butler Bot
+ PlanB. GmbH
+ bin\Release\netcoreapp2.2\
+ bin\Release\netcoreapp2.2\PlanB.Butler.Bot.xml
+ bin\Debug\netcoreapp2.2\
+ bin\Debug\netcoreapp2.2\PlanB.Butler.Bot.xml
@@ -28,7 +35,6 @@
-
diff --git a/PlanB.Butler.Bot/Program.cs b/PlanB.Butler.Bot/Program.cs
index 7125613..a119161 100644
--- a/PlanB.Butler.Bot/Program.cs
+++ b/PlanB.Butler.Bot/Program.cs
@@ -3,18 +3,30 @@
//
// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.5.0
+using Microsoft.AspNetCore;
+using Microsoft.AspNetCore.Hosting;
+
namespace PlanB.Butler.Bot
{
- using Microsoft.AspNetCore;
- using Microsoft.AspNetCore.Hosting;
-
+ ///
+ /// Program.
+ ///
public class Program
{
+ ///
+ /// Defines the entry point of the application.
+ ///
+ /// The arguments.
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
+ ///
+ /// Creates the web host builder.
+ ///
+ /// The arguments.
+ /// IWebHostBuilder.
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup();
diff --git a/PlanB.Butler.Bot/Startup.cs b/PlanB.Butler.Bot/Startup.cs
index 06a263b..2c9c8fa 100644
--- a/PlanB.Butler.Bot/Startup.cs
+++ b/PlanB.Butler.Bot/Startup.cs
@@ -5,6 +5,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
+
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
diff --git a/PlanB.Butler.Library/PlanB.Butler.Library/BackendCommunication.cs b/PlanB.Butler.Library/PlanB.Butler.Library/BackendCommunication.cs
index 4a58b85..58f77f8 100644
--- a/PlanB.Butler.Library/PlanB.Butler.Library/BackendCommunication.cs
+++ b/PlanB.Butler.Library/PlanB.Butler.Library/BackendCommunication.cs
@@ -27,6 +27,7 @@ public class BackendCommunication
/// The storage account URL.
/// The storage account key.
///
+ [Obsolete("Call function instead")]
public string GetDocument(string container, string resourceName, string storageAccountUrl, string storageAccountKey)
{
using (HttpClient httpClient = new HttpClient())
@@ -92,6 +93,7 @@ public string GenerateStorageSasTokenWrite(string resourceName, string storageAc
return sasToken;
}
+ [Obsolete("Call function instead")]
public HttpStatusCode PutDocument(string container, string resourceName, string body, string queueName, string serviceBusConnectionString)
{
string label = $"{container}/{resourceName}";
@@ -121,6 +123,7 @@ public HttpStatusCode PutDocument(string container, string resourceName, string
/// Name of the queue.
/// The service bus connection string.
///
+ [Obsolete("Call function instead")]
public HttpStatusCode PutDocumentByteArray(string container, string resourceName, byte[] body, string queueName, string serviceBusConnectionString)
{
string label = $"{container}/{resourceName}";
@@ -139,6 +142,7 @@ public HttpStatusCode PutDocumentByteArray(string container, string resourceName
}
}
+ [Obsolete("Call function instead")]
public string GenerateServiceBusSasToken(string serviceBusConnectionString, string que)
{
var connectionString = serviceBusConnectionString;
diff --git a/PlanB.Butler.Library/PlanB.Butler.Library/BotModels/Order.cs b/PlanB.Butler.Library/PlanB.Butler.Library/BotModels/Order.cs
index e8bbcf5..82a6bcb 100644
--- a/PlanB.Butler.Library/PlanB.Butler.Library/BotModels/Order.cs
+++ b/PlanB.Butler.Library/PlanB.Butler.Library/BotModels/Order.cs
@@ -21,8 +21,17 @@ public class Order
public double Price { get; set; }
+ [Obsolete("Please use 'Quantity'")]
public int Quantaty { get; set; }
+ ///
+ /// Gets or sets the quantity.
+ ///
+ ///
+ /// The quantity.
+ ///
+ public int Quantity { get; set; }
+
public double Grand { get; set; }
}
}
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services.Test/MealServiceMockTest.cs b/PlanB.Butler.Services/PlanB.Butler.Services.Test/MealServiceMockTest.cs
index e8fdd11..d5aca4d 100644
--- a/PlanB.Butler.Services/PlanB.Butler.Services.Test/MealServiceMockTest.cs
+++ b/PlanB.Butler.Services/PlanB.Butler.Services.Test/MealServiceMockTest.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -13,6 +14,7 @@
using Microsoft.WindowsAzure.Storage.Blob;
using Moq;
using Newtonsoft.Json;
+using PlanB.Butler.Services.Controllers;
using PlanB.Butler.Services.Models;
namespace PlanB.Butler.Services.Test
@@ -38,6 +40,11 @@ public class MealServiceMockTest
///
private Mock mockBlobContainer;
+ ///
+ /// The mock BLOB.
+ ///
+ private Mock mockBlob;
+
///
/// The correlation identifier.
///
@@ -59,8 +66,11 @@ public void Init()
this.context = new Microsoft.Azure.WebJobs.ExecutionContext() { FunctionName = nameof(MealService) };
this.log = new FunctionTestLogger();
- var mockBlobUri = new Uri("http://bogus/myaccount/blob");
+ var mockBlobUri = new Uri("http://localhost/container");
this.mockBlobContainer = new Mock(MockBehavior.Loose, mockBlobUri);
+ this.mockBlob = new Mock(new Uri("http://localhost/blob"));
+ this.mockBlob.Setup(n => n.UploadTextAsync(It.IsAny())).Returns(Task.FromResult(true));
+ this.mockBlobContainer.Setup(n => n.GetBlockBlobReference(It.IsAny())).Returns(this.mockBlob.Object);
}
///
@@ -75,14 +85,14 @@ public void CreateMealTest()
Date = DateTime.Now,
Name = "Kässpätzle",
Price = 2.3,
- Restaurant = "Gasthof Adler",
+ Restaurant = "Gasthof Adler " + DateTime.Now.Ticks,
};
// Setup Mock
var httpRequest = CreateMockRequest(mealModel);
var result = MealService.CreateMeal(httpRequest.Object, this.mockBlobContainer.Object, this.log, this.context).Result;
Assert.IsNotNull(result);
- Assert.AreEqual(typeof(OkResult), result.GetType());
+ Assert.AreEqual(typeof(OkObjectResult), result.GetType());
}
///
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services.Test/MealServiceTest.cs b/PlanB.Butler.Services/PlanB.Butler.Services.Test/MealServiceTest.cs
index 0d44564..37f3a31 100644
--- a/PlanB.Butler.Services/PlanB.Butler.Services.Test/MealServiceTest.cs
+++ b/PlanB.Butler.Services/PlanB.Butler.Services.Test/MealServiceTest.cs
@@ -2,7 +2,10 @@
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using System;
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
+using PlanB.Butler.Services.Controllers;
+using PlanB.Butler.Services.Models;
namespace PlanB.Butler.Services.Test
{
@@ -189,5 +192,82 @@ public void IsDateInRangeCheckEndEqualCheck()
var result = MealService.IsDateInRange(startDate, endDate, toCheckDate);
Assert.AreEqual(true, result);
}
+
+ ///
+ /// Validates the meal test ok.
+ ///
+ [TestMethod]
+ public void ValidateMealTestOk()
+ {
+ Guid correlationId = Guid.NewGuid();
+
+ MealModel mealModel = new MealModel()
+ {
+ Name = "Test",
+ Restaurant = "TestRestaurant",
+ Date = DateTime.Now,
+ };
+
+ var result = MealService.Validate(mealModel, correlationId, out ErrorModel errorModel);
+ Assert.AreEqual(true, result);
+ Assert.IsNull(errorModel);
+ }
+
+ ///
+ /// Validates the meal test missing meal.
+ ///
+ [TestMethod]
+ public void ValidateMealTestMissingMeal()
+ {
+ Guid correlationId = Guid.NewGuid();
+
+ MealModel mealModel = new MealModel()
+ {
+ Restaurant = "TestRestaurant",
+ Date = DateTime.Now,
+ };
+
+ var result = MealService.Validate(mealModel, correlationId, out ErrorModel errorModel);
+ Assert.AreEqual(false, result);
+ Assert.IsNotNull(errorModel);
+ }
+
+ ///
+ /// Validates the meal test missing restaurant.
+ ///
+ [TestMethod]
+ public void ValidateMealTestMissingRestaurant()
+ {
+ Guid correlationId = Guid.NewGuid();
+
+ MealModel mealModel = new MealModel()
+ {
+ Name = "Test",
+ Date = DateTime.Now,
+ };
+
+ var result = MealService.Validate(mealModel, correlationId, out ErrorModel errorModel);
+ Assert.AreEqual(false, result);
+ Assert.IsNotNull(errorModel);
+ }
+
+ ///
+ /// Validates the meal test missing date.
+ ///
+ [TestMethod]
+ public void ValidateMealTestMissingDate()
+ {
+ Guid correlationId = Guid.NewGuid();
+
+ MealModel mealModel = new MealModel()
+ {
+ Name = "Test",
+ Restaurant = "TestRestaurant",
+ };
+
+ var result = MealService.Validate(mealModel, correlationId, out ErrorModel errorModel);
+ Assert.AreEqual(false, result);
+ Assert.IsNotNull(errorModel);
+ }
}
}
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services.Test/PlanB.Butler.Services.Test.csproj b/PlanB.Butler.Services/PlanB.Butler.Services.Test/PlanB.Butler.Services.Test.csproj
index f5caa69..25d22f6 100644
--- a/PlanB.Butler.Services/PlanB.Butler.Services.Test/PlanB.Butler.Services.Test.csproj
+++ b/PlanB.Butler.Services/PlanB.Butler.Services.Test/PlanB.Butler.Services.Test.csproj
@@ -1,13 +1,13 @@
- netcoreapp3.0
+ netcoreapp3.1
false
- bin\Debug\netcoreapp3.0\
+ bin\Debug\netcoreapp3.1\
bin\Debug\netcoreapp3.1\PlanB.Butler.Services.Test.xml
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services.Test/RestaurantServiceMockTest.cs b/PlanB.Butler.Services/PlanB.Butler.Services.Test/RestaurantServiceMockTest.cs
new file mode 100644
index 0000000..efe9928
--- /dev/null
+++ b/PlanB.Butler.Services/PlanB.Butler.Services.Test/RestaurantServiceMockTest.cs
@@ -0,0 +1,151 @@
+// 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.Threading.Tasks;
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Azure.ServiceBus;
+using Microsoft.Extensions.Primitives;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Microsoft.WindowsAzure.Storage.Blob;
+using Moq;
+using Newtonsoft.Json;
+using PlanB.Butler.Services.Controllers;
+using PlanB.Butler.Services.Models;
+
+namespace PlanB.Butler.Services.Test
+{
+ ///
+ /// RestaurantServiceMockTest.
+ ///
+ [TestClass]
+ public class RestaurantServiceMockTest
+ {
+ ///
+ /// The context.
+ ///
+ private Microsoft.Azure.WebJobs.ExecutionContext context;
+
+ ///
+ /// The log.
+ ///
+ private FunctionTestLogger log;
+
+ ///
+ /// The mock BLOB container.
+ ///
+ private Mock mockBlobContainer;
+
+ ///
+ /// The mock BLOB.
+ ///
+ private Mock mockBlob;
+
+ ///
+ /// The correlation identifier.
+ ///
+ private Guid correlationId;
+
+ ///
+ /// The message header.
+ ///
+ private Message messageHeader;
+
+ ///
+ /// Initializes this instance.
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ this.correlationId = Guid.NewGuid();
+ this.messageHeader = new Message() { CorrelationId = this.correlationId.ToString() };
+ this.context = new Microsoft.Azure.WebJobs.ExecutionContext() { FunctionName = nameof(MealService) };
+ this.log = new FunctionTestLogger();
+
+ var mockBlobUri = new Uri("http://localhost/container");
+ this.mockBlobContainer = new Mock(MockBehavior.Loose, mockBlobUri);
+ this.mockBlob = new Mock(new Uri("http://localhost/blob"));
+ this.mockBlob.Setup(n => n.UploadTextAsync(It.IsAny())).Returns(Task.FromResult(true));
+ this.mockBlobContainer.Setup(n => n.GetBlockBlobReference(It.IsAny())).Returns(this.mockBlob.Object);
+ }
+
+ ///
+ /// Creates the restaurant test.
+ ///
+ [TestMethod]
+ public void CreateRestaurantOkTest()
+ {
+ RestaurantModel restaurantModel = new RestaurantModel()
+ {
+ City = "Main City",
+ EmailAddress = "restaurant@domain.com",
+ Name = "The Restaurant",
+ PhoneNumber = "32168",
+ };
+
+ // Setup Mock
+ var httpRequest = CreateMockRequest(restaurantModel);
+ var result = RestaurantService.CreateRestaurant(httpRequest.Object, this.mockBlobContainer.Object, this.log, this.context).Result;
+ Assert.IsNotNull(result);
+ Assert.AreEqual(typeof(OkObjectResult), result.GetType());
+ }
+
+ ///
+ /// Creates the restaurant fail name test.
+ ///
+ [TestMethod]
+ public void CreateRestaurantFailNameTest()
+ {
+ RestaurantModel restaurantModel = new RestaurantModel()
+ {
+ City = "Main City",
+ EmailAddress = "restaurant@domain.com",
+ PhoneNumber = "32168",
+ };
+
+ // Setup Mock
+ var httpRequest = CreateMockRequest(restaurantModel);
+ var result = RestaurantService.CreateRestaurant(httpRequest.Object, this.mockBlobContainer.Object, this.log, this.context).Result;
+ Assert.IsNotNull(result);
+ Assert.AreEqual(typeof(BadRequestObjectResult), result.GetType());
+ }
+
+ ///
+ /// Creates the mock request.
+ ///
+ /// The body.
+ /// HttpRequest.
+ private static Mock CreateMockRequest(object body)
+ {
+ var ms = new MemoryStream();
+ var sw = new StreamWriter(ms);
+
+ var json = JsonConvert.SerializeObject(body);
+
+ sw.Write(json);
+ sw.Flush();
+
+ ms.Position = 0;
+ var mockContext = new Mock();
+ var mockResponse = new Mock();
+ var mockHeaderDictionary = new Mock();
+
+ mockContext.Setup(c => c.Response).Returns(mockResponse.Object);
+ mockResponse.Setup(c => c.Headers).Returns(mockHeaderDictionary.Object);
+
+ var mockRequest = new Mock();
+
+ // mockRequest.Setup(req => req.Query).Returns(new QueryCollection(query));
+ Dictionary header = new Dictionary();
+ mockRequest.Setup(req => req.Headers).Returns(new HeaderDictionary(header));
+ mockRequest.SetupGet(req => req.HttpContext).Returns(mockContext.Object);
+ mockRequest.Setup(x => x.Body).Returns(ms);
+
+ return mockRequest;
+ }
+ }
+}
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services/FinanceService.cs b/PlanB.Butler.Services/PlanB.Butler.Services/Controllers/FinanceService.cs
similarity index 99%
rename from PlanB.Butler.Services/PlanB.Butler.Services/FinanceService.cs
rename to PlanB.Butler.Services/PlanB.Butler.Services/Controllers/FinanceService.cs
index 1f10a80..875c35f 100644
--- a/PlanB.Butler.Services/PlanB.Butler.Services/FinanceService.cs
+++ b/PlanB.Butler.Services/PlanB.Butler.Services/Controllers/FinanceService.cs
@@ -19,7 +19,7 @@
using Newtonsoft.Json;
using PlanB.Butler.Services.Extensions;
-namespace PlanB.Butler.Services
+namespace PlanB.Butler.Services.Controllers
{
///
/// Finance.
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services/Controllers/MealService.cs b/PlanB.Butler.Services/PlanB.Butler.Services/Controllers/MealService.cs
new file mode 100644
index 0000000..01bdb4e
--- /dev/null
+++ b/PlanB.Butler.Services/PlanB.Butler.Services/Controllers/MealService.cs
@@ -0,0 +1,631 @@
+// 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.Globalization;
+using System.IO;
+using System.Net.Mime;
+using System.Reflection;
+using System.Threading.Tasks;
+using System.Web;
+
+using AzureFunctions.Extensions.Swashbuckle.Attribute;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Azure.WebJobs;
+using Microsoft.Azure.WebJobs.Extensions.Http;
+using Microsoft.Extensions.Logging;
+using Microsoft.WindowsAzure.Storage;
+using Microsoft.WindowsAzure.Storage.Blob;
+using Newtonsoft.Json;
+using PlanB.Butler.Services.Extensions;
+using PlanB.Butler.Services.Models;
+
+namespace PlanB.Butler.Services.Controllers
+{
+ ///
+ /// MealService.
+ ///
+ public static class MealService
+ {
+ ///
+ /// The meta date.
+ ///
+ private const string MetaDate = "date";
+
+ ///
+ /// The meta restaurant.
+ ///
+ private const string MetaRestaurant = "restaurant";
+
+ ///
+ /// Create meal.
+ ///
+ /// The req.
+ /// The cloud BLOB container.
+ /// The log.
+ /// The context.
+ ///
+ /// IActionResult.
+ ///
+ [FunctionName("CreateMeal")]
+ [Consumes(MediaTypeNames.Application.Json)]
+ [ProducesResponseType(typeof(MealModel), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
+ public static async Task CreateMeal(
+ [HttpTrigger(AuthorizationLevel.Function, "post", Route = "meals")]
+ [RequestBodyType(typeof(MealModel), "Meal request")]HttpRequest req,
+ [Blob("meals", FileAccess.ReadWrite, Connection = "StorageSend")] CloudBlobContainer cloudBlobContainer,
+ ILogger log,
+ ExecutionContext context)
+ {
+ Guid correlationId = Util.ReadCorrelationId(req.Headers);
+ var methodName = MethodBase.GetCurrentMethod().Name;
+ var trace = new Dictionary();
+ EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
+
+ IActionResult actionResult = null;
+ using (log.BeginScope("Method:{methodName} CorrelationId:{CorrelationId} Label:{Label}", methodName, correlationId.ToString(), context.InvocationId.ToString()))
+ {
+ try
+ {
+ string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
+ trace.Add("requestBody", requestBody);
+
+ MealModel mealModel = JsonConvert.DeserializeObject(requestBody);
+ if (mealModel.CorrelationId == null || mealModel.CorrelationId.Equals(Guid.Empty))
+ {
+ mealModel.CorrelationId = correlationId;
+ }
+
+ bool isValid = Validate(mealModel, correlationId, out ErrorModel errorModel);
+
+ if (isValid)
+ {
+ var fileName = CreateFileName(mealModel);
+ trace.Add($"fileName", fileName);
+ mealModel.Id = fileName;
+
+ var fullFileName = $"{fileName}.json";
+ trace.Add($"fullFileName", fullFileName);
+
+ req.HttpContext.Response.Headers.Add(Constants.ButlerCorrelationTraceHeader, correlationId.ToString());
+
+ CloudBlockBlob blob = cloudBlobContainer.GetBlockBlobReference($"{fullFileName}");
+ if (blob != null)
+ {
+ blob.Properties.ContentType = "application/json";
+ var metaDate = mealModel.Date.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
+ blob.Metadata.Add(MetaDate, metaDate);
+ blob.Metadata.Add(MetaRestaurant, mealModel.Restaurant);
+ blob.Metadata.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString().Replace("-", string.Empty));
+ var meal = JsonConvert.SerializeObject(mealModel);
+ trace.Add("meal", meal);
+
+ Task task = blob.UploadTextAsync(requestBody);
+ task.Wait();
+ actionResult = new OkObjectResult(mealModel);
+ log.LogInformation(correlationId, $"'{methodName}' - success", trace);
+ }
+ }
+ else
+ {
+ actionResult = new BadRequestObjectResult(errorModel);
+ log.LogInformation(correlationId, $"'{methodName}' - is not valid", trace);
+ }
+ }
+ catch (Exception e)
+ {
+ trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
+ trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
+ log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
+ log.LogError(correlationId, $"'{methodName}' - rejected", trace);
+ ErrorModel errorModel = new ErrorModel()
+ {
+ CorrelationId = correlationId,
+ Details = e.StackTrace,
+ Message = e.Message,
+ };
+
+ actionResult = new BadRequestObjectResult(errorModel);
+ }
+ finally
+ {
+ log.LogTrace(eventId, $"'{methodName}' - finished");
+ log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
+ }
+ }
+
+ return actionResult;
+ }
+
+ ///
+ /// Deletes the meal by identifier.
+ ///
+ /// The req.
+ /// The identifier.
+ /// The BLOB.
+ /// The log.
+ /// The context.
+ /// IActionResult.
+ [ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [FunctionName("DeleteMealById")]
+ public static IActionResult DeleteMealById(
+ [HttpTrigger(AuthorizationLevel.Function, "delete", Route = "meals/{id}")] HttpRequest req,
+ string id,
+ [Blob("meals/{id}.json", FileAccess.ReadWrite, Connection = "StorageSend")] CloudBlockBlob blob,
+ ILogger log,
+ ExecutionContext context)
+ {
+ Guid correlationId = Util.ReadCorrelationId(req.Headers);
+ var methodName = MethodBase.GetCurrentMethod().Name;
+ var trace = new Dictionary();
+ EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
+ IActionResult actionResult = null;
+
+ using (log.BeginScope("Method:{methodName} CorrelationId:{CorrelationId} Label:{Label}", methodName, correlationId.ToString(), context.InvocationId.ToString()))
+ {
+ try
+ {
+ trace.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString());
+ trace.Add("id", id);
+
+ if (blob != null)
+ {
+ Task task = blob.DeleteIfExistsAsync();
+ task.Wait();
+
+ actionResult = new OkResult();
+ log.LogInformation(correlationId, $"'{methodName}' - success", trace);
+ }
+ }
+ catch (Exception e)
+ {
+ trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
+ trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
+ log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
+ log.LogError(correlationId, $"'{methodName}' - rejected", trace);
+
+ ErrorModel errorModel = new ErrorModel()
+ {
+ CorrelationId = correlationId,
+ Details = e.StackTrace,
+ Message = e.Message,
+ };
+ actionResult = new BadRequestObjectResult(errorModel);
+ }
+ finally
+ {
+ log.LogTrace(eventId, $"'{methodName}' - finished");
+ log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
+ }
+ }
+
+ return actionResult;
+ }
+
+ ///
+ /// Updates the meal by identifier.
+ ///
+ /// The req.
+ /// The identifier.
+ /// The BLOB.
+ /// The cloud BLOB container.
+ /// The log.
+ /// The context.
+ /// IActionResult.
+ [FunctionName("UpdateMealById")]
+ [Consumes(MediaTypeNames.Application.Json)]
+ [ProducesResponseType(typeof(MealModel), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
+ public static async Task UpdateMealById(
+ [HttpTrigger(AuthorizationLevel.Function, "put", Route = "meals/{id}")] HttpRequest req,
+ string id,
+ [Blob("meals/{id}.json", FileAccess.ReadWrite, Connection = "StorageSend")] string existingContent,
+ [Blob("meals", FileAccess.ReadWrite, Connection = "StorageSend")] CloudBlobContainer cloudBlobContainer,
+ ILogger log,
+ ExecutionContext context)
+ {
+ Guid correlationId = Util.ReadCorrelationId(req.Headers);
+ var methodName = MethodBase.GetCurrentMethod().Name;
+ var trace = new Dictionary();
+ EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
+ IActionResult actionResult = null;
+
+ MealModel mealModel = null;
+ using (log.BeginScope("Method:{methodName} CorrelationId:{CorrelationId} Label:{Label}", methodName, correlationId.ToString(), context.InvocationId.ToString()))
+ {
+ try
+ {
+ trace.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString());
+ trace.Add("id", id);
+ mealModel = JsonConvert.DeserializeObject(existingContent);
+
+ var date = mealModel.Date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
+
+ var filename = $"{date}-{mealModel.Restaurant}.json";
+ trace.Add($"filename", filename);
+ string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
+ trace.Add("requestBody", requestBody);
+ mealModel = JsonConvert.DeserializeObject(requestBody);
+
+ req.HttpContext.Response.Headers.Add(Constants.ButlerCorrelationTraceHeader, correlationId.ToString());
+
+ bool isValid = Validate(mealModel, correlationId, out ErrorModel errorModel);
+ if (isValid)
+ {
+ CloudBlockBlob blob = cloudBlobContainer.GetBlockBlobReference($"{filename}");
+ if (blob != null)
+ {
+ blob.Properties.ContentType = "application/json";
+ var metaDate = mealModel.Date.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
+ blob.Metadata.Add(MetaDate, metaDate);
+ blob.Metadata.Add(MetaRestaurant, mealModel.Restaurant);
+ blob.Metadata.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString().Replace("-", string.Empty));
+ var meal = JsonConvert.SerializeObject(mealModel);
+ trace.Add("meal", meal);
+
+ Task task = blob.UploadTextAsync(meal);
+ task.Wait();
+ actionResult = new OkObjectResult(mealModel);
+ log.LogInformation(correlationId, $"'{methodName}' - success", trace);
+ }
+ }
+ else
+ {
+ actionResult = new BadRequestObjectResult(errorModel);
+ log.LogInformation(correlationId, $"'{methodName}' - is not valid", trace);
+ }
+ }
+ catch (Exception e)
+ {
+ trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
+ trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
+ log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
+ log.LogError(correlationId, $"'{methodName}' - rejected", trace);
+
+ ErrorModel errorModel = new ErrorModel()
+ {
+ CorrelationId = correlationId,
+ Details = e.StackTrace,
+ Message = e.Message,
+ };
+ actionResult = new BadRequestObjectResult(mealModel);
+ }
+ finally
+ {
+ log.LogTrace(eventId, $"'{methodName}' - finished");
+ log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
+ }
+ }
+
+ return actionResult;
+ }
+
+ ///
+ /// Reads the meals.
+ ///
+ /// The req.
+ /// The cloud BLOB container.
+ /// The log.
+ /// The context.
+ ///
+ /// All meals.
+ ///
+ [ProducesResponseType(typeof(List), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
+ [FunctionName("GetMeals")]
+ public static async Task GetMeals(
+ [HttpTrigger(AuthorizationLevel.Function, "get", Route = "meals")] HttpRequest req,
+ [Blob("meals", FileAccess.ReadWrite, Connection = "StorageSend")] CloudBlobContainer cloudBlobContainer,
+ ILogger log,
+ ExecutionContext context)
+ {
+ Guid correlationId = Util.ReadCorrelationId(req.Headers);
+ var methodName = MethodBase.GetCurrentMethod().Name;
+ var trace = new Dictionary();
+ EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
+ IActionResult actionResult = null;
+
+ List meals = new List();
+ using (log.BeginScope("Method:{methodName} CorrelationId:{CorrelationId} Label:{Label}", methodName, correlationId.ToString(), context.InvocationId.ToString()))
+ {
+ try
+ {
+ trace.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString());
+ string startDateQuery = req.Query["startDate"];
+ string endDateQuery = req.Query["endDate"];
+ string restaurantQuery = req.Query["restaurant"];
+ string prefix = string.Empty;
+
+ bool checkForDate = false;
+ DateTime start = DateTime.MinValue;
+ DateTime end = DateTime.MinValue;
+
+ if (!(string.IsNullOrEmpty(startDateQuery) && string.IsNullOrEmpty(endDateQuery)))
+ {
+ checkForDate = true;
+ DateTime.TryParse(startDateQuery, out start);
+ DateTime.TryParse(endDateQuery, out end);
+ }
+
+ if (checkForDate)
+ {
+ prefix = CreateBlobPrefix(startDateQuery, endDateQuery);
+ }
+
+ BlobContinuationToken blobContinuationToken = null;
+ var options = new BlobRequestOptions();
+ var operationContext = new OperationContext();
+
+ List cloudBlockBlobs = new List();
+ do
+ {
+ var blobs = await cloudBlobContainer.ListBlobsSegmentedAsync(prefix, true, BlobListingDetails.All, null, blobContinuationToken, options, operationContext).ConfigureAwait(false);
+ blobContinuationToken = blobs.ContinuationToken;
+ cloudBlockBlobs.AddRange(blobs.Results);
+ }
+ while (blobContinuationToken != null);
+
+ foreach (var item in cloudBlockBlobs)
+ {
+ CloudBlockBlob blob = (CloudBlockBlob)item;
+ if (checkForDate)
+ {
+ await blob.FetchAttributesAsync();
+ if (blob.Metadata.ContainsKey(MetaDate))
+ {
+ var mealMetaDate = blob.Metadata[MetaDate];
+ DateTime mealDate = DateTime.MinValue;
+ if (DateTime.TryParse(mealMetaDate, out mealDate))
+ {
+ var isDateInRange = IsDateInRange(start, end, mealDate);
+ if (isDateInRange)
+ {
+ var blobContent = blob.DownloadTextAsync();
+ var blobMeal = JsonConvert.DeserializeObject(await blobContent);
+ meals.Add(blobMeal);
+ }
+ }
+ }
+ }
+ else
+ {
+ var content = blob.DownloadTextAsync();
+ var meal = JsonConvert.DeserializeObject(await content);
+ meals.Add(meal);
+ }
+ }
+
+ log.LogInformation(correlationId, $"'{methodName}' - success", trace);
+ actionResult = new OkObjectResult(meals);
+ }
+ catch (Exception e)
+ {
+ trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
+ trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
+ log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
+ log.LogError(correlationId, $"'{methodName}' - rejected", trace);
+
+ ErrorModel errorModel = new ErrorModel()
+ {
+ CorrelationId = correlationId,
+ Details = e.StackTrace,
+ Message = e.Message,
+ };
+ actionResult = new BadRequestObjectResult(errorModel);
+ }
+ finally
+ {
+ log.LogTrace(eventId, $"'{methodName}' - finished");
+ log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
+ }
+ }
+
+ return actionResult;
+ }
+
+ ///
+ /// Gets the meal by id.
+ ///
+ /// The req.
+ /// The identifier.
+ /// The BLOB.
+ /// The log.
+ /// The context.
+ ///
+ /// Meal by id.
+ ///
+ [ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(typeof(MealModel), StatusCodes.Status200OK)]
+ [FunctionName("GetMealById")]
+ public static IActionResult GetMealById(
+ [HttpTrigger(AuthorizationLevel.Function, "get", Route = "meals/{id}")] HttpRequest req,
+ string id,
+ [Blob("meals/{id}.json", FileAccess.ReadWrite, Connection = "StorageSend")] string blob,
+ ILogger log,
+ ExecutionContext context)
+ {
+ Guid correlationId = Util.ReadCorrelationId(req.Headers);
+ var methodName = MethodBase.GetCurrentMethod().Name;
+ var trace = new Dictionary();
+ EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
+ IActionResult actionResult = null;
+
+ MealModel mealModel = null;
+ using (log.BeginScope("Method:{methodName} CorrelationId:{CorrelationId} Label:{Label}", methodName, correlationId.ToString(), context.InvocationId.ToString()))
+ {
+ try
+ {
+ trace.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString());
+ trace.Add("id", id);
+ mealModel = JsonConvert.DeserializeObject(blob);
+
+ log.LogInformation(correlationId, $"'{methodName}' - success", trace);
+ actionResult = new OkObjectResult(mealModel);
+ }
+ catch (Exception e)
+ {
+ trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
+ trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
+ log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
+ log.LogError(correlationId, $"'{methodName}' - rejected", trace);
+
+ ErrorModel errorModel = new ErrorModel()
+ {
+ CorrelationId = correlationId,
+ Details = e.StackTrace,
+ Message = e.Message,
+ };
+ actionResult = new BadRequestObjectResult(mealModel);
+ }
+ finally
+ {
+ log.LogTrace(eventId, $"'{methodName}' - finished");
+ log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
+ }
+ }
+
+ return actionResult;
+ }
+
+ ///
+ /// Determines whether the date in range compared to start and end.
+ ///
+ /// The start.
+ /// The end.
+ /// To check.
+ ///
+ /// true if date is in range; otherwise, false.
+ ///
+ internal static bool IsDateInRange(DateTime start, DateTime end, DateTime toCheck)
+ {
+ if (toCheck < start)
+ {
+ return false;
+ }
+
+ if (end < toCheck)
+ {
+ return false;
+ }
+
+ if (start.Equals(toCheck))
+ {
+ return true;
+ }
+
+ if (end.Equals(toCheck))
+ {
+ return true;
+ }
+
+ if (start.Equals(end) && start.Equals(toCheck))
+ {
+ return true;
+ }
+
+ long difference = toCheck.Ticks - start.Ticks;
+ long sum = start.Ticks + difference;
+
+ if (sum < end.Ticks)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Creates the BLOB prefix.
+ ///
+ /// The start date.
+ /// The end date.
+ /// Prefix.
+ internal static string CreateBlobPrefix(string startDate, string endDate)
+ {
+ string prefix = string.Empty;
+ if (string.IsNullOrEmpty(startDate) || string.IsNullOrEmpty(endDate))
+ {
+ return prefix;
+ }
+
+ if (startDate.Length == endDate.Length)
+ {
+ for (int i = 0; i < startDate.Length; i++)
+ {
+ if (startDate[i] == endDate[i])
+ {
+ prefix += startDate[i];
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return prefix;
+ }
+
+ ///
+ /// Validates the meal.
+ ///
+ /// The meal model.
+ /// The correlation identifier.
+ /// The error model.
+ /// True if data is valid; otherwise False.
+ internal static bool Validate(MealModel mealModel, Guid correlationId, out ErrorModel errorModel)
+ {
+ bool isValid = true;
+ errorModel = null;
+ if (string.IsNullOrEmpty(mealModel.Name))
+ {
+ errorModel = new ErrorModel()
+ {
+ CorrelationId = correlationId,
+ Message = "No meal name!",
+ };
+ isValid = false;
+ }
+
+ if (string.IsNullOrEmpty(mealModel.Restaurant))
+ {
+ errorModel = new ErrorModel()
+ {
+ CorrelationId = correlationId,
+ Message = "No meal restaurant!",
+ };
+ isValid = false;
+ }
+
+ if (mealModel.Date == null || mealModel.Date == DateTime.MinValue)
+ {
+ errorModel = new ErrorModel()
+ {
+ CorrelationId = correlationId,
+ Message = "No meal date!",
+ };
+ isValid = false;
+ }
+
+ return isValid;
+ }
+
+ ///
+ /// Creates the name of the file.
+ ///
+ /// The model.
+ /// FileName without extension.
+ internal static string CreateFileName(MealModel model)
+ {
+ var date = model.Date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
+ string fileName = $"{date}-{model.Restaurant}";
+ fileName = HttpUtility.UrlEncode(fileName);
+ return fileName;
+ }
+ }
+}
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services/OrderService.cs b/PlanB.Butler.Services/PlanB.Butler.Services/Controllers/OrderService.cs
similarity index 99%
rename from PlanB.Butler.Services/PlanB.Butler.Services/OrderService.cs
rename to PlanB.Butler.Services/PlanB.Butler.Services/Controllers/OrderService.cs
index 428c313..8dda202 100644
--- a/PlanB.Butler.Services/PlanB.Butler.Services/OrderService.cs
+++ b/PlanB.Butler.Services/PlanB.Butler.Services/Controllers/OrderService.cs
@@ -22,7 +22,7 @@
using PlanB.Butler.Services.Extensions;
using PlanB.Butler.Services.Models;
-namespace PlanB.Butler.Services
+namespace PlanB.Butler.Services.Controllers
{
///
/// OrderService.
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services/Controllers/RestaurantService.cs b/PlanB.Butler.Services/PlanB.Butler.Services/Controllers/RestaurantService.cs
new file mode 100644
index 0000000..8670425
--- /dev/null
+++ b/PlanB.Butler.Services/PlanB.Butler.Services/Controllers/RestaurantService.cs
@@ -0,0 +1,402 @@
+// 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.Globalization;
+using System.IO;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+
+using AzureFunctions.Extensions.Swashbuckle.Attribute;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Azure.WebJobs;
+using Microsoft.Azure.WebJobs.Extensions.Http;
+using Microsoft.Extensions.Logging;
+using Microsoft.WindowsAzure.Storage;
+using Microsoft.WindowsAzure.Storage.Blob;
+using Newtonsoft.Json;
+using PlanB.Butler.Services.Extensions;
+using PlanB.Butler.Services.Models;
+
+namespace PlanB.Butler.Services.Controllers
+{
+ ///
+ /// RestaurantService.
+ ///
+ public static class RestaurantService
+ {
+ ///
+ /// The meta restaurant.
+ ///
+ private const string MetaRestaurant = "restaurant";
+
+ ///
+ /// The meta city.
+ ///
+ private const string MetaCity = "city";
+
+ ///
+ /// Gets the restaurants.
+ ///
+ /// The req.
+ /// The cloud BLOB container.
+ /// The log.
+ /// The context.
+ /// All Restaurants.
+ [ProducesResponseType(typeof(List), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
+ [FunctionName("GetRestaurants")]
+ public static async Task GetRestaurants(
+ [HttpTrigger(AuthorizationLevel.Function, "get", Route = "restaurants")] HttpRequest req,
+ [Blob("restaurants", FileAccess.ReadWrite, Connection = "StorageSend")] CloudBlobContainer cloudBlobContainer,
+ ILogger log,
+ ExecutionContext context)
+ {
+ Guid correlationId = Util.ReadCorrelationId(req.Headers);
+ var methodName = MethodBase.GetCurrentMethod().Name;
+ var trace = new Dictionary();
+ EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
+ IActionResult actionResult = null;
+
+ List restaurant = new List();
+ using (log.BeginScope("Method:{methodName} CorrelationId:{CorrelationId} Label:{Label}", methodName, correlationId.ToString(), context.InvocationId.ToString()))
+ {
+ try
+ {
+ BlobContinuationToken blobContinuationToken = null;
+ var options = new BlobRequestOptions();
+ var operationContext = new OperationContext();
+
+ List cloudBlockBlobs = new List();
+ do
+ {
+ var blobs = await cloudBlobContainer.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, null, blobContinuationToken, options, operationContext).ConfigureAwait(false);
+ blobContinuationToken = blobs.ContinuationToken;
+ cloudBlockBlobs.AddRange(blobs.Results);
+ }
+ while (blobContinuationToken != null);
+
+ log.LogInformation(correlationId, $"'{methodName}' - success", trace);
+ actionResult = new OkObjectResult(restaurant);
+ }
+ catch (Exception e)
+ {
+ trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
+ trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
+ log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
+ log.LogError(correlationId, $"'{methodName}' - rejected", trace);
+
+ ErrorModel errorModel = new ErrorModel()
+ {
+ CorrelationId = correlationId,
+ Details = e.StackTrace,
+ Message = e.Message,
+ };
+ actionResult = new BadRequestObjectResult(errorModel);
+ }
+ finally
+ {
+ log.LogTrace(eventId, $"'{methodName}' - finished");
+ log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
+ }
+ }
+
+ return actionResult;
+ }
+
+ ///
+ /// Gets the restaurant by identifier.
+ ///
+ /// The req.
+ /// The identifier.
+ /// The BLOB.
+ /// The log.
+ /// The context.
+ /// Restaurant.
+ [ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ [ProducesResponseType(typeof(RestaurantModel), StatusCodes.Status200OK)]
+ [FunctionName("GetRestaurantById")]
+ public static IActionResult GetRestaurantById(
+ [HttpTrigger(AuthorizationLevel.Function, "get", Route = "restaurants/{id}")] HttpRequest req,
+ string id,
+ [Blob("restaurants/{id}.json", FileAccess.ReadWrite, Connection = "StorageSend")] string blob,
+ ILogger log,
+ ExecutionContext context)
+ {
+ Guid correlationId = Util.ReadCorrelationId(req.Headers);
+ var methodName = MethodBase.GetCurrentMethod().Name;
+ var trace = new Dictionary();
+ EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
+ IActionResult actionResult = null;
+
+ using (log.BeginScope("Method:{methodName} CorrelationId:{CorrelationId} Label:{Label}", methodName, correlationId.ToString(), context.InvocationId.ToString()))
+ {
+ try
+ {
+ trace.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString());
+ trace.Add("id", id);
+ RestaurantModel model = JsonConvert.DeserializeObject(blob);
+
+ log.LogInformation(correlationId, $"'{methodName}' - success", trace);
+ actionResult = new OkObjectResult(model);
+ }
+ catch (Exception e)
+ {
+ trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
+ trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
+ log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
+ log.LogError(correlationId, $"'{methodName}' - rejected", trace);
+
+ ErrorModel errorModel = new ErrorModel()
+ {
+ CorrelationId = correlationId,
+ Details = e.StackTrace,
+ Message = e.Message,
+ };
+ actionResult = new BadRequestObjectResult(errorModel);
+ }
+ finally
+ {
+ log.LogTrace(eventId, $"'{methodName}' - finished");
+ log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
+ }
+ }
+
+ return actionResult;
+ }
+
+ ///
+ /// Deletes the restaurant by identifier.
+ ///
+ /// The req.
+ /// The identifier.
+ /// The BLOB.
+ /// The log.
+ /// The context.
+ /// IActionResult.
+ [ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [FunctionName("DeleteRestaurantById")]
+ public static IActionResult DeleteRestaurantById(
+ [HttpTrigger(AuthorizationLevel.Function, "delete", Route = "restaurants/{id}")] HttpRequest req,
+ string id,
+ [Blob("restaurants/{id}.json", FileAccess.ReadWrite, Connection = "StorageSend")] CloudBlockBlob blob,
+ ILogger log,
+ ExecutionContext context)
+ {
+ Guid correlationId = Util.ReadCorrelationId(req.Headers);
+ var methodName = MethodBase.GetCurrentMethod().Name;
+ var trace = new Dictionary();
+ EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
+ IActionResult actionResult = null;
+
+ using (log.BeginScope("Method:{methodName} CorrelationId:{CorrelationId} Label:{Label}", methodName, correlationId.ToString(), context.InvocationId.ToString()))
+ {
+ try
+ {
+ trace.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString());
+ trace.Add("id", id);
+
+ if (blob != null)
+ {
+ Task task = blob.DeleteIfExistsAsync();
+ task.Wait();
+
+ actionResult = new OkResult();
+ log.LogInformation(correlationId, $"'{methodName}' - success", trace);
+ }
+ }
+ catch (Exception e)
+ {
+ trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
+ trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
+ log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
+ log.LogError(correlationId, $"'{methodName}' - rejected", trace);
+
+ ErrorModel errorModel = new ErrorModel()
+ {
+ CorrelationId = correlationId,
+ Details = e.StackTrace,
+ Message = e.Message,
+ };
+ actionResult = new BadRequestObjectResult(errorModel);
+ }
+ finally
+ {
+ log.LogTrace(eventId, $"'{methodName}' - finished");
+ log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
+ }
+ }
+
+ return actionResult;
+ }
+
+ ///
+ /// Creates the restaurant.
+ ///
+ /// The req.
+ /// The cloud BLOB container.
+ /// The log.
+ /// The context.
+ /// IActionResult.
+ [FunctionName("CreateRestaurant")]
+ [ProducesResponseType(typeof(RestaurantModel), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
+ public static async Task CreateRestaurant(
+ [HttpTrigger(AuthorizationLevel.Function, "post", Route = "restaurants")]
+ [RequestBodyType(typeof(RestaurantModel), "Restaurant request")]HttpRequest req,
+ [Blob("restaurants", FileAccess.ReadWrite, Connection = "StorageSend")] CloudBlobContainer cloudBlobContainer,
+ ILogger log,
+ ExecutionContext context)
+ {
+ Guid correlationId = Util.ReadCorrelationId(req.Headers);
+ var methodName = MethodBase.GetCurrentMethod().Name;
+ var trace = new Dictionary();
+ EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
+
+ IActionResult actionResult = null;
+ using (log.BeginScope("Method:{methodName} CorrelationId:{CorrelationId} Label:{Label}", methodName, correlationId.ToString(), context.InvocationId.ToString()))
+ {
+ try
+ {
+ trace.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString());
+ string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
+ trace.Add("requestBody", requestBody);
+
+ RestaurantModel restaurantModel = JsonConvert.DeserializeObject(requestBody);
+
+ bool isValid = Validate(restaurantModel, correlationId, log, out ErrorModel errorModel);
+
+ if (isValid)
+ {
+ var fileName = CreateFileName(restaurantModel);
+ trace.Add($"fileName", fileName);
+ restaurantModel.Id = fileName;
+
+ var fullFileName = $"{fileName}.json";
+ trace.Add($"fullFileName", fullFileName);
+
+ req.HttpContext.Response.Headers.Add(Constants.ButlerCorrelationTraceHeader, correlationId.ToString());
+
+ CloudBlockBlob blob = cloudBlobContainer.GetBlockBlobReference($"{fullFileName}");
+ if (blob != null)
+ {
+ blob.Properties.ContentType = "application/json";
+ blob.Metadata.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString().Replace("-", string.Empty));
+ blob.Metadata.Add(MetaRestaurant, System.Web.HttpUtility.HtmlEncode(restaurantModel.Name));
+ blob.Metadata.Add(MetaCity, System.Web.HttpUtility.HtmlEncode(restaurantModel.City));
+ var restaurant = JsonConvert.SerializeObject(restaurantModel);
+ trace.Add("restaurant", restaurant);
+
+ Task task = blob.UploadTextAsync(requestBody);
+ task.Wait();
+
+ actionResult = new OkObjectResult(restaurantModel);
+ log.LogInformation(correlationId, $"'{methodName}' - success", trace);
+ }
+ }
+ else
+ {
+ actionResult = new BadRequestObjectResult(errorModel);
+ log.LogInformation(correlationId, $"'{methodName}' - is not valid", trace);
+ }
+ }
+ catch (Exception e)
+ {
+ trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
+ trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
+ log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
+ log.LogError(correlationId, $"'{methodName}' - rejected", trace);
+ ErrorModel errorModel = new ErrorModel()
+ {
+ CorrelationId = correlationId,
+ Details = e.StackTrace,
+ Message = e.Message,
+ };
+
+ actionResult = new BadRequestObjectResult(errorModel);
+ }
+ finally
+ {
+ log.LogTrace(eventId, $"'{methodName}' - finished");
+ log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
+ }
+ }
+
+ return actionResult;
+ }
+
+ ///
+ /// Validates the specified model.
+ ///
+ /// The model.
+ /// The correlation identifier.
+ /// The log.
+ /// The error model.
+ /// True if data is valid; otherwise False.
+ internal static bool Validate(RestaurantModel model, Guid correlationId, ILogger log, out ErrorModel errorModel)
+ {
+ bool isValid = true;
+ errorModel = null;
+ var trace = new Dictionary();
+ var methodName = MethodBase.GetCurrentMethod().Name;
+ trace.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString());
+
+ StringBuilder message = new StringBuilder();
+ if (string.IsNullOrEmpty(model.Name))
+ {
+ message.Append("No restaurant name!");
+ isValid = false;
+ }
+
+ if (string.IsNullOrEmpty(model.City))
+ {
+ message.Append("No restaurant city!");
+ isValid = false;
+ }
+
+ if (string.IsNullOrEmpty(model.PhoneNumber))
+ {
+ message.Append("No restaurant phone!");
+ isValid = false;
+ }
+
+ if (!isValid)
+ {
+ errorModel = new ErrorModel()
+ {
+ CorrelationId = correlationId,
+ Message = message.ToString(),
+ };
+ trace.Add("Message", errorModel.Message);
+ log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
+ }
+ else
+ {
+ log.LogInformation(correlationId, $"'{methodName}' - success", trace);
+ }
+
+ log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
+
+ return isValid;
+ }
+
+ ///
+ /// Creates the name of the file.
+ ///
+ /// The model.
+ /// FileName without extension.
+ internal static string CreateFileName(RestaurantModel model)
+ {
+ string fileName = $"{model.Name}-{model.City}";
+ fileName = HttpUtility.UrlEncode(fileName);
+ return fileName;
+ }
+ }
+}
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services/MealService.cs b/PlanB.Butler.Services/PlanB.Butler.Services/MealService.cs
deleted file mode 100644
index e5bd164..0000000
--- a/PlanB.Butler.Services/PlanB.Butler.Services/MealService.cs
+++ /dev/null
@@ -1,503 +0,0 @@
-// 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.Globalization;
-using System.IO;
-using System.Net.Mime;
-using System.Reflection;
-using System.Threading.Tasks;
-
-using AzureFunctions.Extensions.Swashbuckle.Attribute;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Azure.WebJobs;
-using Microsoft.Azure.WebJobs.Extensions.Http;
-using Microsoft.Extensions.Logging;
-using Microsoft.WindowsAzure.Storage;
-using Microsoft.WindowsAzure.Storage.Blob;
-using Newtonsoft.Json;
-using PlanB.Butler.Services.Extensions;
-using PlanB.Butler.Services.Models;
-
-namespace PlanB.Butler.Services
-{
- ///
- /// MealService.
- ///
- public static class MealService
- {
- ///
- /// The meta date.
- ///
- private const string MetaDate = "date";
-
- ///
- /// The meta restaurant.
- ///
- private const string MetaRestaurant = "restaurant";
-
- ///
- /// Create meal.
- ///
- /// The req.
- /// The cloud BLOB container.
- /// The log.
- /// The context.
- ///
- /// IActionResult.
- ///
- [FunctionName("CreateMeal")]
- [Consumes(MediaTypeNames.Application.Json)]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
- public static async Task CreateMeal(
- [HttpTrigger(AuthorizationLevel.Function, "post", Route = "meals")]
- [RequestBodyType(typeof(MealModel), "Meal request")]HttpRequest req,
- [Blob("meals", FileAccess.ReadWrite, Connection = "StorageSend")] CloudBlobContainer cloudBlobContainer,
- ILogger log,
- ExecutionContext context)
- {
- Guid correlationId = Util.ReadCorrelationId(req.Headers);
- var methodName = MethodBase.GetCurrentMethod().Name;
- var trace = new Dictionary();
- EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
-
- IActionResult actionResult = null;
- try
- {
- string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
- trace.Add("requestBody", requestBody);
-
- MealModel mealModel = JsonConvert.DeserializeObject(requestBody);
- if (mealModel.CorrelationId == null || mealModel.CorrelationId.Equals(Guid.Empty))
- {
- mealModel.CorrelationId = correlationId;
- }
-
- bool isValid = true;
- if (string.IsNullOrEmpty(mealModel.Name))
- {
- ErrorModel errorModel = new ErrorModel()
- {
- CorrelationId = correlationId,
- Message = "No meal name!",
- };
- isValid = false;
- actionResult = new BadRequestObjectResult(errorModel);
- }
-
- if (string.IsNullOrEmpty(mealModel.Restaurant))
- {
- ErrorModel errorModel = new ErrorModel()
- {
- CorrelationId = correlationId,
- Message = "No meal restaurant!",
- };
- isValid = false;
- actionResult = new BadRequestObjectResult(errorModel);
- }
-
- if (isValid)
- {
- var date = mealModel.Date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
-
- var filename = $"{date}-{mealModel.Restaurant}.json";
- trace.Add($"filename", filename);
-
- req.HttpContext.Response.Headers.Add(Constants.ButlerCorrelationTraceHeader, correlationId.ToString());
-
- CloudBlockBlob blob = cloudBlobContainer.GetBlockBlobReference($"{filename}");
- if (blob != null)
- {
- blob.Properties.ContentType = "application/json";
- var metaDate = mealModel.Date.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
- blob.Metadata.Add(MetaDate, metaDate);
- blob.Metadata.Add(MetaRestaurant, mealModel.Restaurant);
- blob.Metadata.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString().Replace("-", string.Empty));
- var meal = JsonConvert.SerializeObject(mealModel);
- trace.Add("meal", meal);
-
- Task task = blob.UploadTextAsync(requestBody);
- task.Wait();
- }
-
- actionResult = new OkResult();
- log.LogInformation(correlationId, $"'{methodName}' - success", trace);
- }
-
- log.LogInformation(correlationId, $"'{methodName}' - is not valid", trace);
- }
- catch (Exception e)
- {
- trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
- trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
- log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
- log.LogError(correlationId, $"'{methodName}' - rejected", trace);
- ErrorModel errorModel = new ErrorModel()
- {
- CorrelationId = correlationId,
- Details = e.StackTrace,
- Message = e.Message,
- };
-
- actionResult = new BadRequestObjectResult(errorModel);
- }
- finally
- {
- log.LogTrace(eventId, $"'{methodName}' - finished");
- log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
- }
-
- return actionResult;
- }
-
- ///
- /// Updates the meal by identifier.
- ///
- /// The req.
- /// The identifier.
- /// The BLOB.
- /// The cloud BLOB container.
- /// The log.
- /// The context.
- /// IActionResult.
- [FunctionName("UpdateMealById")]
- public static async Task UpdateMealById(
- [HttpTrigger(AuthorizationLevel.Function, "put", Route = "meals/{id}")] HttpRequest req,
- string id,
- [Blob("meals/{id}.json", FileAccess.ReadWrite, Connection = "StorageSend")] string existingContent,
- [Blob("meals", FileAccess.ReadWrite, Connection = "StorageSend")] CloudBlobContainer cloudBlobContainer,
- ILogger log,
- ExecutionContext context)
- {
- Guid correlationId = Util.ReadCorrelationId(req.Headers);
- var methodName = MethodBase.GetCurrentMethod().Name;
- var trace = new Dictionary();
- EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
- IActionResult actionResult = null;
-
- MealModel mealModel = null;
-
- try
- {
- trace.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString());
- trace.Add("id", id);
- mealModel = JsonConvert.DeserializeObject(existingContent);
-
- var date = mealModel.Date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
-
- var filename = $"{date}-{mealModel.Restaurant}.json";
- trace.Add($"filename", filename);
- string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
- trace.Add("requestBody", requestBody);
- mealModel = JsonConvert.DeserializeObject(requestBody);
-
- req.HttpContext.Response.Headers.Add(Constants.ButlerCorrelationTraceHeader, correlationId.ToString());
-
- CloudBlockBlob blob = cloudBlobContainer.GetBlockBlobReference($"{filename}");
- if (blob != null)
- {
- blob.Properties.ContentType = "application/json";
- var metaDate = mealModel.Date.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
- blob.Metadata.Add(MetaDate, metaDate);
- blob.Metadata.Add(MetaRestaurant, mealModel.Restaurant);
- blob.Metadata.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString().Replace("-", string.Empty));
- var meal = JsonConvert.SerializeObject(mealModel);
- trace.Add("meal", meal);
-
- Task task = blob.UploadTextAsync(meal);
- task.Wait();
- }
-
- log.LogInformation(correlationId, $"'{methodName}' - success", trace);
- actionResult = new OkObjectResult(mealModel);
- }
- catch (Exception e)
- {
- trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
- trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
- log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
- log.LogError(correlationId, $"'{methodName}' - rejected", trace);
-
- ErrorModel errorModel = new ErrorModel()
- {
- CorrelationId = correlationId,
- Details = e.StackTrace,
- Message = e.Message,
- };
- actionResult = new BadRequestObjectResult(mealModel);
- }
- finally
- {
- log.LogTrace(eventId, $"'{methodName}' - finished");
- log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
- }
-
- return actionResult;
- }
-
- ///
- /// Reads the meals.
- ///
- /// The req.
- /// The cloud BLOB container.
- /// The log.
- /// The context.
- ///
- /// All meals.
- ///
- [ProducesResponseType(typeof(List), StatusCodes.Status200OK)]
- [ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
- [FunctionName("GetMeals")]
- public static async Task GetMeals(
- [HttpTrigger(AuthorizationLevel.Function, "get", Route = "meals")] HttpRequest req,
- [Blob("meals", FileAccess.ReadWrite, Connection = "StorageSend")] CloudBlobContainer cloudBlobContainer,
- ILogger log,
- ExecutionContext context)
- {
- Guid correlationId = Util.ReadCorrelationId(req.Headers);
- var methodName = MethodBase.GetCurrentMethod().Name;
- var trace = new Dictionary();
- EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
- IActionResult actionResult = null;
-
- List meals = new List();
-
- try
- {
- trace.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString());
- string startDateQuery = req.Query["startDate"];
- string endDateQuery = req.Query["endDate"];
- string restaurantQuery = req.Query["restaurant"];
- string prefix = string.Empty;
-
- bool checkForDate = false;
- DateTime start = DateTime.MinValue;
- DateTime end = DateTime.MinValue;
-
- if (!(string.IsNullOrEmpty(startDateQuery) && string.IsNullOrEmpty(endDateQuery)))
- {
- checkForDate = true;
- DateTime.TryParse(startDateQuery, out start);
- DateTime.TryParse(endDateQuery, out end);
- }
-
- if (checkForDate)
- {
- prefix = CreateBlobPrefix(startDateQuery, endDateQuery);
- }
-
- BlobContinuationToken blobContinuationToken = null;
- var options = new BlobRequestOptions();
- var operationContext = new OperationContext();
-
- List cloudBlockBlobs = new List();
- do
- {
- var blobs = await cloudBlobContainer.ListBlobsSegmentedAsync(prefix, true, BlobListingDetails.All, null, blobContinuationToken, options, operationContext).ConfigureAwait(false);
- blobContinuationToken = blobs.ContinuationToken;
- cloudBlockBlobs.AddRange(blobs.Results);
- }
- while (blobContinuationToken != null);
-
- foreach (var item in cloudBlockBlobs)
- {
- CloudBlockBlob blob = (CloudBlockBlob)item;
- if (checkForDate)
- {
- await blob.FetchAttributesAsync();
- if (blob.Metadata.ContainsKey(MetaDate))
- {
- var mealMetaDate = blob.Metadata[MetaDate];
- DateTime mealDate = DateTime.MinValue;
- if (DateTime.TryParse(mealMetaDate, out mealDate))
- {
- var isDateInRange = IsDateInRange(start, end, mealDate);
- if (isDateInRange)
- {
- var blobContent = blob.DownloadTextAsync();
- var blobMeal = JsonConvert.DeserializeObject(await blobContent);
- meals.Add(blobMeal);
- }
- }
- }
- }
- else
- {
- var content = blob.DownloadTextAsync();
- var meal = JsonConvert.DeserializeObject(await content);
- meals.Add(meal);
- }
- }
-
- log.LogInformation(correlationId, $"'{methodName}' - success", trace);
- actionResult = new OkObjectResult(meals);
- }
- catch (Exception e)
- {
- trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
- trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
- log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
- log.LogError(correlationId, $"'{methodName}' - rejected", trace);
-
- ErrorModel errorModel = new ErrorModel()
- {
- CorrelationId = correlationId,
- Details = e.StackTrace,
- Message = e.Message,
- };
- actionResult = new BadRequestObjectResult(errorModel);
- }
- finally
- {
- log.LogTrace(eventId, $"'{methodName}' - finished");
- log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
- }
-
- return actionResult;
- }
-
- ///
- /// Gets the meal by id.
- ///
- /// The req.
- /// The identifier.
- /// The BLOB.
- /// The log.
- /// The context.
- ///
- /// Meal by id.
- ///
- [ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
- [ProducesResponseType(typeof(MealModel), StatusCodes.Status200OK)]
- [FunctionName("GetMealById")]
- public static IActionResult GetMealById(
- [HttpTrigger(AuthorizationLevel.Function, "get", Route = "meals/{id}")] HttpRequest req,
- string id,
- [Blob("meals/{id}.json", FileAccess.ReadWrite, Connection = "StorageSend")] string blob,
- ILogger log,
- ExecutionContext context)
- {
- Guid correlationId = Util.ReadCorrelationId(req.Headers);
- var methodName = MethodBase.GetCurrentMethod().Name;
- var trace = new Dictionary();
- EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
- IActionResult actionResult = null;
-
- MealModel mealModel = null;
-
- try
- {
- trace.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString());
- trace.Add("id", id);
- mealModel = JsonConvert.DeserializeObject(blob);
-
- log.LogInformation(correlationId, $"'{methodName}' - success", trace);
- actionResult = new OkObjectResult(mealModel);
- }
- catch (Exception e)
- {
- trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
- trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
- log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
- log.LogError(correlationId, $"'{methodName}' - rejected", trace);
-
- ErrorModel errorModel = new ErrorModel()
- {
- CorrelationId = correlationId,
- Details = e.StackTrace,
- Message = e.Message,
- };
- actionResult = new BadRequestObjectResult(mealModel);
- }
- finally
- {
- log.LogTrace(eventId, $"'{methodName}' - finished");
- log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
- }
-
- return actionResult;
- }
-
- ///
- /// Determines whether the date in range compared to start and end.
- ///
- /// The start.
- /// The end.
- /// To check.
- ///
- /// true if date is in range; otherwise, false.
- ///
- internal static bool IsDateInRange(DateTime start, DateTime end, DateTime toCheck)
- {
- if (toCheck < start)
- {
- return false;
- }
-
- if (end < toCheck)
- {
- return false;
- }
-
- if (start.Equals(toCheck))
- {
- return true;
- }
-
- if (end.Equals(toCheck))
- {
- return true;
- }
-
- if (start.Equals(end) && start.Equals(toCheck))
- {
- return true;
- }
-
- long difference = toCheck.Ticks - start.Ticks;
- long sum = start.Ticks + difference;
-
- if (sum < end.Ticks)
- {
- return true;
- }
-
- return false;
- }
-
- ///
- /// Creates the BLOB prefix.
- ///
- /// The start date.
- /// The end date.
- /// Prefix.
- internal static string CreateBlobPrefix(string startDate, string endDate)
- {
- string prefix = string.Empty;
- if (string.IsNullOrEmpty(startDate) || string.IsNullOrEmpty(endDate))
- {
- return prefix;
- }
-
- if (startDate.Length == endDate.Length)
- {
- for (int i = 0; i < startDate.Length; i++)
- {
- if (startDate[i] == endDate[i])
- {
- prefix += startDate[i];
- }
- else
- {
- break;
- }
- }
- }
-
- return prefix;
- }
- }
-}
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services/Models/MealModel.cs b/PlanB.Butler.Services/PlanB.Butler.Services/Models/MealModel.cs
index 176ad51..bf4118a 100644
--- a/PlanB.Butler.Services/PlanB.Butler.Services/Models/MealModel.cs
+++ b/PlanB.Butler.Services/PlanB.Butler.Services/Models/MealModel.cs
@@ -14,21 +14,13 @@ namespace PlanB.Butler.Services.Models
public class MealModel
{
///
- /// Gets the identifier.
+ /// Gets or sets the identifier.
///
///
/// The identifier.
///
[JsonProperty("id")]
- public string Id
- {
- get
- {
- var date = this.Date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
- var id = $"{date}-{this.Restaurant}";
- return id;
- }
- }
+ public string Id { get; set; }
///
/// Gets or sets the correlation identifier.
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services/Models/OrderModel.cs b/PlanB.Butler.Services/PlanB.Butler.Services/Models/OrderModel.cs
index 0a6081d..ead99e6 100644
--- a/PlanB.Butler.Services/PlanB.Butler.Services/Models/OrderModel.cs
+++ b/PlanB.Butler.Services/PlanB.Butler.Services/Models/OrderModel.cs
@@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using System;
-using System.Collections.Generic;
namespace PlanB.Butler.Services.Models
{
@@ -12,33 +11,7 @@ namespace PlanB.Butler.Services.Models
public class OrderModel
{
///
- /// Gets or sets the company status Enum.
- ///
- public enum OrderRelationship
- {
- ///
- /// External.
- ///
- External = 1,
-
- ///
- /// Internal.
- ///
- Internal = 2,
-
- ///
- /// Client.
- ///
- Client = 3,
-
- ///
- /// Intership.
- ///
- Intership = 4,
- }
-
- ///
- /// Gets or sets the name.
+ /// Gets or sets the Relationship.
///
///
/// The name.
@@ -108,25 +81,5 @@ public enum OrderRelationship
/// The benefit.
///
public double Benefit { get; set; }
-
- //public OrderModel(string companyStatus, DateTime date, string name, string companyName, string restaurant, string meal, double price, int quantity, double benefit)
- //{
- // Dictionary lookUpCompanyStatus = new Dictionary();
- // lookUpCompanyStatus.Add("Extern", OrderRelationship.External);
- // lookUpCompanyStatus.Add("Intern", OrderRelationship.Internal);
- // lookUpCompanyStatus.Add("Kunde", OrderRelationship.Client);
- // lookUpCompanyStatus.Add("Praktikant", OrderRelationship.Intership);
-
- // OrderRelationship selected = lookUpCompanyStatus[companyStatus];
- // this.Relationship = selected;
- // this.Date = date;
- // this.Name = name;
- // this.CompanyName = companyName;
- // this.Restaurant = restaurant;
- // this.Meal = meal;
- // this.Price = price;
- // this.Quantity = quantity;
- // this.Benefit = benefit;
- //}
}
}
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services/Models/OrderRelationship.cs b/PlanB.Butler.Services/PlanB.Butler.Services/Models/OrderRelationship.cs
new file mode 100644
index 0000000..b7c267d
--- /dev/null
+++ b/PlanB.Butler.Services/PlanB.Butler.Services/Models/OrderRelationship.cs
@@ -0,0 +1,31 @@
+// 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.Models
+{
+ ///
+ /// OrderRelationship.
+ ///
+ public enum OrderRelationship
+ {
+ ///
+ /// External.
+ ///
+ External = 1,
+
+ ///
+ /// Internal.
+ ///
+ Internal = 2,
+
+ ///
+ /// Client.
+ ///
+ Client = 3,
+
+ ///
+ /// Intership.
+ ///
+ Intership = 4,
+ }
+}
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services/Models/RestaurantModel.cs b/PlanB.Butler.Services/PlanB.Butler.Services/Models/RestaurantModel.cs
index b2aa4cd..39acb14 100644
--- a/PlanB.Butler.Services/PlanB.Butler.Services/Models/RestaurantModel.cs
+++ b/PlanB.Butler.Services/PlanB.Butler.Services/Models/RestaurantModel.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Text;
using Newtonsoft.Json;
@@ -23,8 +24,7 @@ public class RestaurantModel
[JsonProperty("id")]
public string Id
{
- get;
- set;
+ get; set;
}
///
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services/PlanB.Butler.Services.csproj b/PlanB.Butler.Services/PlanB.Butler.Services/PlanB.Butler.Services.csproj
index c2f0a3c..270390b 100644
--- a/PlanB.Butler.Services/PlanB.Butler.Services/PlanB.Butler.Services.csproj
+++ b/PlanB.Butler.Services/PlanB.Butler.Services/PlanB.Butler.Services.csproj
@@ -1,16 +1,16 @@

- netcoreapp3.0
+ netcoreapp3.1
v2
PlanB.Butler.Services
- bin\Debug\netcoreapp3.0\
- bin\Debug\netcoreapp3.0\PlanB.Butler.Services.xml
+
+ bin\Debug\netcoreapp3.1\PlanB.Butler.Services.xml
- bin\Release\netcoreapp3.0\PlanB.Butler.Services.xml
- bin\Release\netcoreapp3.0\
+ bin\Release\netcoreapp3.1\PlanB.Butler.Services.xml
+ bin\Release\netcoreapp3.1\
@@ -24,9 +24,9 @@
-
+
-
+
all
diff --git a/PlanB.Butler.Services/PlanB.Butler.Services/RestaurantService.cs b/PlanB.Butler.Services/PlanB.Butler.Services/RestaurantService.cs
deleted file mode 100644
index bc5a923..0000000
--- a/PlanB.Butler.Services/PlanB.Butler.Services/RestaurantService.cs
+++ /dev/null
@@ -1,178 +0,0 @@
-// 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.Globalization;
-using System.IO;
-using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
-
-using AzureFunctions.Extensions.Swashbuckle.Attribute;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Azure.WebJobs;
-using Microsoft.Azure.WebJobs.Extensions.Http;
-using Microsoft.Extensions.Logging;
-using Microsoft.WindowsAzure.Storage;
-using Microsoft.WindowsAzure.Storage.Blob;
-using Newtonsoft.Json;
-using PlanB.Butler.Services.Extensions;
-using PlanB.Butler.Services.Models;
-
-namespace PlanB.Butler.Services
-{
- ///
- /// RestaurantService.
- ///
- public static class RestaurantService
- {
- private const string MetaRestaurant = "restaurant";
- private const string MetaCity = "city";
-
- ///
- /// Gets the restaurants.
- ///
- /// The req.
- /// The cloud BLOB container.
- /// The log.
- /// The context.
- /// All Restaurants.
- [ProducesResponseType(typeof(List), StatusCodes.Status200OK)]
- [ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
- [FunctionName("GetRestaurants")]
- public static async Task GetRestaurants(
- [HttpTrigger(AuthorizationLevel.Function, "get", Route = "restaurants")] HttpRequest req,
- [Blob("restaurants", FileAccess.ReadWrite, Connection = "StorageSend")] CloudBlobContainer cloudBlobContainer,
- ILogger log,
- ExecutionContext context)
- {
- Guid correlationId = Util.ReadCorrelationId(req.Headers);
- var methodName = MethodBase.GetCurrentMethod().Name;
- var trace = new Dictionary();
- EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
- IActionResult actionResult = null;
-
- List restaurant = new List();
-
- try
- {
- BlobContinuationToken blobContinuationToken = null;
- var options = new BlobRequestOptions();
- var operationContext = new OperationContext();
-
- List cloudBlockBlobs = new List();
- do
- {
- var blobs = await cloudBlobContainer.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, null, blobContinuationToken, options, operationContext).ConfigureAwait(false);
- blobContinuationToken = blobs.ContinuationToken;
- cloudBlockBlobs.AddRange(blobs.Results);
- }
- while (blobContinuationToken != null);
-
- log.LogInformation(correlationId, $"'{methodName}' - success", trace);
- actionResult = new OkObjectResult(restaurant);
- }
- catch (Exception e)
- {
- trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
- trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
- log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
- log.LogError(correlationId, $"'{methodName}' - rejected", trace);
-
- ErrorModel errorModel = new ErrorModel()
- {
- CorrelationId = correlationId,
- Details = e.StackTrace,
- Message = e.Message,
- };
- actionResult = new BadRequestObjectResult(errorModel);
- }
- finally
- {
- log.LogTrace(eventId, $"'{methodName}' - finished");
- log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
- }
-
- return actionResult;
- }
-
- ///
- /// Creates the restaurant.
- ///
- /// The req.
- /// The cloud BLOB container.
- /// The log.
- /// The context.
- /// IActionResult.
- [FunctionName("CreateRestaurant")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
- public static async Task CreateRestaurant(
- [HttpTrigger(AuthorizationLevel.Function, "post", Route = "restaurants")]
- [RequestBodyType(typeof(RestaurantModel), "Restaurant request")]HttpRequest req,
- [Blob("restaurants", FileAccess.ReadWrite, Connection = "StorageSend")] CloudBlobContainer cloudBlobContainer,
- ILogger log,
- ExecutionContext context)
- {
- Guid correlationId = Util.ReadCorrelationId(req.Headers);
- var methodName = MethodBase.GetCurrentMethod().Name;
- var trace = new Dictionary();
- EventId eventId = new EventId(correlationId.GetHashCode(), Constants.ButlerCorrelationTraceName);
-
- IActionResult actionResult = null;
- try
- {
- string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
- trace.Add("requestBody", requestBody);
-
- RestaurantModel restaurantModel = JsonConvert.DeserializeObject(requestBody);
-
- var filename = $"{restaurantModel.Name}-{restaurantModel.City}.json";
- trace.Add($"filename", filename);
-
- req.HttpContext.Response.Headers.Add(Constants.ButlerCorrelationTraceHeader, correlationId.ToString());
-
- CloudBlockBlob blob = cloudBlobContainer.GetBlockBlobReference($"{filename}");
- if (blob != null)
- {
- blob.Properties.ContentType = "application/json";
- blob.Metadata.Add(Constants.ButlerCorrelationTraceName, correlationId.ToString().Replace("-", string.Empty));
- blob.Metadata.Add(MetaRestaurant, System.Web.HttpUtility.HtmlEncode(restaurantModel.Name));
- blob.Metadata.Add(MetaCity, System.Web.HttpUtility.HtmlEncode(restaurantModel.City));
- var restaurant = JsonConvert.SerializeObject(restaurantModel);
- trace.Add("restaurant", restaurant);
-
- Task task = blob.UploadTextAsync(requestBody);
- task.Wait();
- }
-
- log.LogInformation(correlationId, $"'{methodName}' - success", trace);
- actionResult = new OkResult();
- }
- catch (Exception e)
- {
- trace.Add(string.Format("{0} - {1}", methodName, "rejected"), e.Message);
- trace.Add(string.Format("{0} - {1} - StackTrace", methodName, "rejected"), e.StackTrace);
- log.LogInformation(correlationId, $"'{methodName}' - rejected", trace);
- log.LogError(correlationId, $"'{methodName}' - rejected", trace);
- ErrorModel errorModel = new ErrorModel()
- {
- CorrelationId = correlationId,
- Details = e.StackTrace,
- Message = e.Message,
- };
-
- actionResult = new BadRequestObjectResult(errorModel);
- }
- finally
- {
- log.LogTrace(eventId, $"'{methodName}' - finished");
- log.LogInformation(correlationId, $"'{methodName}' - finished", trace);
- }
-
- return actionResult;
- }
- }
-}